지오서비스웹(GEOSERVICE-WEB)으로 만든 통계 지도

지오서비스웹으로 만들 수 있는 통계지도입니다. 입력 데이터는 서울시 행정구역도와 인구 데이터입니다.

먼저 색상 단계 구분도입니다. 서울시 행정구역도에 포함되는 포인트 인구 데이터를 집계해서 얻은 결과입니다.

다음은 핵사곤 영역을 이용한 색상 단계 구분도인데요. 핵사곤 영역은 서울시 행정구역도를 이용해 지오서비스웹의 핵사곤 생성 기능을 이용해 만들었습니다.

다음은 밀도도입니다.

지오서비스웹은 웹에서 누구나 사용할 수 있는 서비스이며 자세한 내용은 아래의 URL을 참조하시기 바랍니다.

지오서비스웹(GEOSERVICE-WEB)

SQL로 문자열 일부만 업데이트하기

42423421라는 문자열에서 42는 2개 존재하는데, 이 중 앞부분에 해당하는 42만을 51로 변경하고자 합니다. 즉 42423421를 51423511이 아닌 51423421로 말입니다. 먼저 SELECT 문으로 제대로된 변환이 이루어지는 확인해 보면 ..

SELECT cd, regexp_replace(cd, '^42', '51', 'g') FROM emd WHERE cd LIKE '42%'

실제 업데이트 하는 SQL은 다음과 같습니다.

UPDATE emd SET cd = regexp_replace(cd, '^42', '51', 'g') WHERE cd LIKE '42%'

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를 위한 지오메트리 객체를 생성, 중첩 여부 확인 코드입니다. 참고로 위의 코드는 다음과 같은 결과를 만들어 냅니다.

pyQGIS를 이용한 벡터 데이터 처리 10 : 버퍼(Buffer) 연산

지오메트리에 대한 공간 연산을 공간 분석을 위해 활용할 수 있는데 그 연산 중 버퍼 연산에 대한 코드를 설명합니다. 먼저 레이어를 추가하고 RN이라는 필드의 값이 “로”로 끝나는 피쳐를 선택하고 선택된 피쳐의 지오메트리에 대해 버퍼 연산을 수행한 뒤 그 결과를 다른 SHP 파일에 저장하는 코드를 작성해 보겠습니다.

먼저 레이어를 추가하고 RN 필드값이 “로”로 끝나는 피쳐를 선택하는 코드를 다음처럼 작성합니다.

QgsProject.instance().removeAllMapLayers()
layer = QgsVectorLayer("D:/__Data__/세종특별자치시_36000/TL_SPRD_MANAGE.shp", "TL_SPRD_MANAGE")
QgsProject.instance().addMapLayers([layer])
layer.selectByExpression('"RN" like \'%로\'')

버퍼 연산 결과를 저장할 SHP 파일 작성자(Writer)를 생성합니다.

fields = layer.fields()
fileName = "D:/__Data__/buffer.shp"
writer = QgsVectorFileWriter(
    fileName,
    "utf-8", 
    fields,
    QgsWkbTypes.Polygon,
    layer.sourceCrs(),
    "ESRI Shapefile"
)

이제 선택된 피쳐를 하나씩 순회하면서 버퍼 연산을 수행하고 그 결과를 새로운 SHP 파일에 기록합니다.

dist = 100
features = layer.selectedFeatures()
for feat in features:
    geom = feat.geometry()
    buff = geom.buffer(dist, 8)
    feat.setGeometry(buff)
    writer.addFeature(feat)

del(writer) # 새로운 SHP 파일 닫기

새로운 SHP 파일을 레이어로 추가하는 코드는 다음과 같습니다.

layer = QgsVectorLayer(fileName, "새로운 레이어", "ogr")
QgsProject.instance().addMapLayers([layer])

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