FingerEyes-Xr에서 JSTS를 사용한 쓰는 코드 정리

FingerEyes-Xr은 JS 기반의 맵엔진이고 JSTS는 JS 기반의 지오메트리 연산 라이브러리입니다.

웹브라우저에서 공간 데이터에 대한 처리를 수행하는 경우에 JSTS를 사용하면 매우 좋은데요. 오래전부터 1.9버전을 사용하고 있었는데 오늘 보니 JSTS가 2.9.3 버전까지 올라왔군요.

JSTS에 대한 코드만을 뽑아 정리하기가 버겨워…. 대충 언급하여 정리해 봅니다. 아래 코드의 목적은 중첩되는 공간 데이터에 대한 헥사곤 도형을 생성해 주는 것입니다.

#run() {
  const selectLayers = this.#dialog.content.querySelector(".input-layer")
  const radius = parseFloat(this.#dialog.content.querySelector(".radius").value)
  const layerName = selectLayers.value
  const layer = app.map.layers().layer(layerName)
  const mbrLayer = layer.MBR()

  // MBR의 좌표계를 배경지도에 대한 좌표계로 변경함
  const proj = layer.EPSG() === -1 ? null : proj4(layer.proj4Name(), 'EPSG:' + layer.EPSG());
  const [minX, minY] = proj ? proj.forward([mbrLayer.minX, mbrLayer.minY]) : [mbrLayer.minX, mbrLayer.minY];
  let [maxX, maxY] = proj ? proj.forward([mbrLayer.maxX, mbrLayer.maxY]) : [mbrLayer.maxX, mbrLayer.maxY];
  maxX += radius*2
  maxY += radius*2

  const xStep = radius + radius/2
  const yStep = radius * Math.sin(60*(Math.PI/180)) * 2
  const gl = new Xr.layers.GraphicLayer("gl")
  app.map.layers().add(gl)

  const cntRows = layer.totalRowsCount();
  const wktReader = new jsts.io.WKTReader()

  for(let y=minY; y<maxY; y+=yStep) {
    for(let cx=minX; cx<maxX; cx+=xStep) {  
      ...
      const pts = [[ /* Hexagon 도형 구성 좌표 만드는 코드 */ ]]

      const psd = new Xr.data.PolygonShapeData(pts)
      const pgr = new Xr.data.PolygonGraphicRow(count, psd)

      const geomHexa = wktReader.read(psd.toWKT(false))   
      let intersected = false
      for(let i=0; i<cntRows; i++) {
        const shapeRow = layer.getShapeByFID(i)
        const geom = wktReader.read(shapeRow.shapeData().toWKT(true))   
        if(geom.intersects(geomHexa)) {
          intersected = true
          break
        }
      }
  
      if(intersected) gl.rowSet().add(pgr)
    }
  }

  app.map.update()
}

JSTS에 대한 주요 코드는 21번, 31/35번, 36번인데요. 각각 jsts.io.WKTReader 객체 준비, WKT로부터 JSTS를 위한 지오메트리 객체를 생성, 중첩 여부 확인 코드입니다. 참고로 위의 코드는 다음과 같은 결과를 만들어 냅니다.

Blender Modeling Tip : 빠르게 면 채우기

위의 그림을 보면 뚫린 곳이 있는데 먼저 2개의 정점이 선택된 상태입니다. 이 상태에서 F키를 입력하면 면이 채워(Fill)집니다. F키를 연속으로 누르면 자동으로 인접한 면이 채워집니다. 아래는 그 결과입니다.

Blender Modeling Tip : N-Gon 만 선택하기

면을 선택하는 것이므로 편집 모드에서 Face 선택 모드로 하고 [Select]-[Select All By Trait]-[Faces By Sides]를 실행합니다. 4개보다 더 많은 정점으로 구성된 면인 N-Gon을 선택해야 하므로 Number of Vertices를 4로, Type을 Greater Than으로 선택하면 N-Gon만이 선택됩니다.

Blender Modeling Tip : 모델의 형상을 유지하며 가장자리에 Edge 추가하기

Bevel 기능을 사용했는데 변경한 옵션은 Width(원하는 값만큼), Segments(2), Shape(1), Miter Outer(Arc)입니다. Shape를 1로 주어야 원래 모델의 형상이 유지되고 Miter Outer를 Arc로 주어야 N-Gon이 적게 만들어집니다. 여기서 Bevel을 적용하기 위한 Edge를 선택해야 하는데 Select-[Select Sharp Edges]로 선택하면 한번에 간단히 선택할 수 있습니다.

#GWC UI Library : ValueRangesColorMatcher

웹 UI 라이브러리인 GWC에서 제공하는 ValueRangesColorMatcher 컴포넌트에 대한 예제 코드입니다.

먼저 DOM 구성은 다음과 같습니다.

그리고 CSS 구성은 다음과 같구요.

.center {
    display: flex;
    width: 100%;
    height: 100%;
    justify-content: center;
    align-items: center;
    gap: 1em;
}

#matcher {
    width: 30em;
    height: 15em;
}

js 코드는 다음과 같습니다.

window.onload = () => {
    const data = [ 165, 120, 160, 200, 135, 115, 100, 125, 135, 190, 156, 130 ]
    const rangeColors = [
        { ranges: [100, 120], color: '#00ff00'},
        { ranges: [120, 141], color: '#88ff00'},
        { ranges: [141, 160], color: '#ffff00'},
        { ranges: [160, 180], color: '#ff8800'},
        { ranges: [180, 200], color: '#ff0000'}
    ]

    matcher.defineData(data, rangeColors)

    document.querySelector(".btn-get").addEventListener("click", () => {
        alert(JSON.stringify(matcher.valueRangeColors, null, 4))
    })

    GeoServiceWebComponentManager.instance.update();
};

실행 결과는 다음과 같습니다.

소수점을 설정할 수 있으며 속성은 toFixed 입니다.