woonizzooni

대한민국 행정동 경계 좌표 추출 #3 - java > GeoJSON 본문

Programming/Java

대한민국 행정동 경계 좌표 추출 #3 - java > GeoJSON

woonizzooni 2019. 9. 6. 21:51

 

이전 게시물 참고.

  1. QGiS이용

  2. python이용 

 

 

이번에는 java코드로 shapefile에서 GeoJson형식으로 좌표 추출 시도.

 

 

o 빌드 & 실행 환경 구성

geotools를 이용할 건데, 우선 아래 내용 참고해서 실행 환경 구성.

https://docs.geotools.org/latest/userguide/tutorial/quickstart/

 

현재 편의상 eclipse, maven을 활용해본다. 아래는 내 pom.xml설정 상태.

  <dependencies>
    ...
    <dependency>
      <groupId>org.geotools</groupId>
      <artifactId>gt-shapefile</artifactId>
      <version>${geotools.version}</version>
    </dependency>
    <dependency>
      <groupId>org.geotools</groupId>
      <artifactId>gt-swing</artifactId>
      <version>${geotools.version}</version>
    </dependency>
    <dependency>
      <groupId>org.geotools</groupId>
      <artifactId>gt-opengis</artifactId>
      <version>${geotools.version}</version>
    </dependency>
    <dependency>
      <groupId>org.geotools</groupId>
      <artifactId>gt-geojson</artifactId>
      <version>${geotools.version}</version>
    </dependency>
  </dependencies>
  
  <repositories>
    <repository>
      <id>maven2-repository.dev.java.net</id>
      <name>Java.net repository</name>
      <url>http://download.java.net/maven/2</url>
    </repository>
    <repository>
      <id>osgeo</id>
      <name>Open Source Geospatial Foundation Repository</name>
      <url>http://download.osgeo.org/webdav/geotools/</url>
    </repository>
    <repository>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
      <id>boundless</id>
      <name>Boundless Maven Repository</name>
      <url>http://repo.boundlessgeo.com/main</url>
    </repository>
  </repositories>

 

o 코드 작성

- geotools 중 'GeoJSON Plugin' 사용. 다만 이 플러그인이 'Unsupported'로 분류되어 있음을 기억해두고..

   private static String filename = "E:\\Z_SOP_BND_ADM_DONG_PG\\Z_SOP_BND_ADM_DONG_PG.shp";
   public static void main(String[] args) {
        try {
            File shpfile = new File(filename);
            
            // DataStore & FeatureSource 접근 방법 1
            /*
            ShapefileDataStore dataStore = new ShapefileDataStore(shpFile.toURI().toURL());
            dataStore.setCharset(Charset.forName("euc-kr"));
            
            SimpleFeatureSource featureSource = dataStore.getFeatureSource();
            */
            
            // DataStore & DataSource 접근 방법 2
            Map<String, String> params = new HashMap<String, String>();
            params.put("url", shpfile.toURI().toString());
            params.put("charset", "euc-kr");
            
            DataStore dataStore = DataStoreFinder.getDataStore(params);

            String[] typeNames = dataStore.getTypeNames();
            SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeNames[0]);
            
            // featureCollection 접근 & geojson 인코딩
            SimpleFeatureCollection featureCollection = featureSource.getFeatures();

            FeatureJSON featureJson = new FeatureJSON();
            StringWriter writer = new StringWriter();
            featureJson.writeFeatureCollection(featureCollection, writer);

            // json 출력
            //String geoJson = writer.toString();
            //System.out.println(geoJson);
            String geoJsonFile = "E:\\Z_SOP_BND_ADM_DONG_PG\\Z_SOP_BND_ADM_DONG_PG.geojson";
            FileWriter fw = new FileWriter(geoJsonFile);
            fw.write(writer.toString());
            fw.flush();
            fw.close();
        } catch (Throwable e) {
            
        }
    }

 

 - 정보 가공이 필요할 경우 대략 아래 클래스를 참고해서 수정해보면 되겠다.

   아래는 geometry와 feature properties의 파라미터 추가 & 위치 변경 예. 

 

org.geotools.geojson.feature.FeatureJSON.class

   class FeatureCollectionEncoder

   class FeatureEncoder 

    public void writeFeatureCollection(FeatureCollection features, Object output) throws IOException {
        Map<String, Object> obj = new LinkedHashMap<String, Object>();
        
        // type : FeatureCollection
        obj.put("type", "FeatureCollection");

        // crs : {}
        if (features.getSchema().getGeometryDescriptor() != null) {
            final ReferencedEnvelope bounds = features.getBounds();
            final CoordinateReferenceSystem crs = (bounds != null) ? bounds.getCoordinateReferenceSystem() : null;

            if (!isStandardCRS(crs)) {
                obj.put("crs", createCRS(crs));
            }
        }

        // features : []
        obj.put("features", new FeatureCollectionEncoder(features, gjson));
        GeoJSONUtil.encode(obj, output);
    }
    
    Map<String, Object> createCRS(CoordinateReferenceSystem crs) throws IOException {
        Map<String, Object> obj = new LinkedHashMap<String, Object>();
        obj.put("type", "name");

        Map<String, Object> props = new LinkedHashMap<String, Object>();
        if (crs == null) {
            props.put("name", "EPSG:4326");
        } else {
            try {
                String identifier = CRS.lookupIdentifier(crs, true);
                props.put("name", identifier);
            } catch (FactoryException e) {
                throw (IOException) new IOException("Error looking up crs identifier").initCause(e);
            }
        }
        obj.put("properties", props);
        return obj;
    }
    
    class FeatureEncoder implements JSONAware {
        SimpleFeatureType featureType;
        SimpleFeature feature;
        int featureSeq = 0;

        public FeatureEncoder(SimpleFeature feature) {
            this(feature.getType());
            this.feature = feature;
        }

        public FeatureEncoder(SimpleFeatureType featureType) {
            this.featureType = featureType;
        }
        
        public String toJSONString(SimpleFeature feature) {
            StringBuilder sb = new StringBuilder();
            sb.append("{");

            // type ----------------------------
            entry("type", "Feature", sb);
            sb.append(",");

            // properties ----------------------------
            int gindex =
                    featureType.getGeometryDescriptor() != null
                            ? featureType.indexOf(
                                    featureType.getGeometryDescriptor().getLocalName())
                            : -1;

            string("properties", sb).append(":").append("{");
            string("objectid", sb).append(":").append(Integer.toString(++this.featureSeq)).append(",");
            boolean attributesWritten = false;
            for (int i = 0; i < featureType.getAttributeCount(); i++) {
                AttributeDescriptor ad = featureType.getDescriptor(i);

                // skip the default geometry, it's already encoded
                if (i == gindex) {
                    continue;
                }

                Object value = feature.getAttribute(i);
                if (value == null) {
                    // skip
                    continue;
                }

                attributesWritten = true;

                // handle special types separately, everything else as a string or literal
                if (value instanceof Envelope) {
                    array(ad.getLocalName(), gjson.toString((Envelope) value), sb);
                } else if (value instanceof BoundingBox) {
                    array(ad.getLocalName(), gjson.toString((BoundingBox) value), sb);
                } else if (value instanceof Geometry) {
                    string(ad.getLocalName(), sb)
                            .append(":")
                            .append(gjson.toString((Geometry) value));
                } else {
                    // ad.getLocalName().toLowerCase()
                    //   base_year : 기준년도
                    //   adm_dr_cd : 행정동코드
                    //   adm_dr_nm : 행정동명
                    entry(ad.getLocalName().toLowerCase(), value, sb);
                }
                sb.append(",");
            }

            if (attributesWritten) {
                sb.setLength(sb.length() - 1);
            }
            sb.append("},");
            
            // geometry : 공간정보 (Polygon, MultiPolygon) ----------------------------
            if (feature.getDefaultGeometry() != null) {
                string("geometry", sb)
                        .append(":")
                        .append(gjson.toString((Geometry) feature.getDefaultGeometry()));
            }
            sb.append("}");
            return sb.toString();
        }
        
        public String toJSONString() {
            return toJSONString(feature);
        }
    }

 

org.geotools.geojson.geom.GeometryJSON.class

    .createPoint() : Polygon, MultiPolygon등 geometry 유형별

    static class CoordinateSequenceEncoder.class :  coordinates 인코더

 

org.geotools.geojson.geom.GeometryJSON.class

    .toString(geometry)
    .write()
    .create(geometry)
    .createPolygon(), createMultiPolygon()...
    .toList(GeometryCollectino)

    CoordinateSequenceEncoder.class <-- geometry 인코딩부

 

org.geotools.geojson.GeoJSONUtil.class
    .encode()

 

 

o 결과

 - 음.. 일단 이상한건(?)  사직동이 QGiS툴, java에서는 MultiPolygon으로, python으로는 Polygon으로 인식된다. -_-

{
  "type": "FeatureCollection",
  "crs": {
    "type": "name",
    "properties": {
      "name": "CRS:84"
    }
  },
  "features": [
    {
      "type": "Feature",
      "properties": {
        "objectid": 1,
        "base_year": "2018",
        "adm_dr_cd": "1101053",
        "adm_dr_nm": "사직동"
      },
      "geometry": {
        "type": "MultiPolygon",
        "coordinates": [
          [
            [
              [
                126.9689,
                37.5781
              ],
              [
                126.969,
                37.5779
              ],
              [
                126.9692,
                37.5779
              ],
              [
                126.9692,
                37.578
              ],
              [
                126.9692,
                37.578
              ],
              [
                126.9692,

 

이제 '이 내용' 에 위 geometry만 DB에 넣으면 

시도/시군구/법정동/행정동 기준의 geocoding, reverse geocoding API 구현을 위한

백데이터를 마련했다고 볼 수 있겠다.  아직 안해봤으니 일단 대충만이라도.... 흠.

 

 

 

[참고]

GeoTools

https://geotools.org/

https://github.com/geotools/geotools

https://docs.geotools.org/latest/javadocs/

 

User Guide

https://docs.geotools.org/latest/userguide/

 

Data - DataStore plugins: Shaperfile Plugin > Connection Parameters, Reading DBF
https://docs.geotools.org/stable/userguide/library/data/

 

Main - Contents : FeatureCollection

https://docs.geotools.org/latest/userguide/library/main/

 

Unsupported Module

https://docs.geotools.org/latest/userguide/unsupported/

 

https://stackoverflow.com/questions/2044876/does-anyone-know-of-a-library-in-java-that-can-parse-esri-shapefiles

 

 

Comments