2011년 7월 29일 금요일

[FOSS4G]GeoServer + KML + Google Earth

GeoServerWMS 서비스를 확장한 KML 서비스가 가능합니다.

▣ KML/KMZ로 저장하기
GeoServer의 Layer Preview에서 아래와 같이 KML(Compressed(KMZ), Plain(KML)) 포맷을 선택하면 로컬 파일로 저장이 가능합니다.


▣ 구글 어스에 네트워크 링크로 레이어 추가하기
GeoServer의 Layer Preview에서 구글 어스에 추가할 레이어를 확인 후 아래와 같이 KML 링크에 오른쪽 마우스를 클릭 후 링크 주소를 복사합니다.
네트워크 링크로 레이어를 추가하면 GeoServer에서 스타일링을 관리할 수 있으나, Refresh 후 약간의 깜빡임이 생기네요.

일반적으로 URL 체계는 다음과 같습니다.

http://GEOSERVER_URL/워크스페이스명/wms/kml?layers=레이어명
또는
http://GEOSERVER_URL/wms/kml?layers=레이어명

ex)http://localhost:8090/geoserver/wms/kml?layers=cite:KR_AP_SID
구글 어스를 실행 후 추가(A) -> 네트워크 링크 메뉴를 실행합니다.
아래와 같이 복사한 KML 서비스 링크를 복사하고 이름을 정의합니다.
확인 버튼을 누르면 GeoServer에서 정의한 심볼과 같이 레이어가 추가됩니다.


▣ Google Earth에서 확인


▣ ArcGIS Layer To KML
ArcGIS 에서도 다음과 같이 레이어를 KML로 내보내기 할 수 있으며, 설정한 심볼 및 라벨 정보가 그대로 구글 어스에서 보여지게 됩니다.
* 팁 : Label/Display Field 설정 확인


2011년 7월 28일 목요일

[GeoTools]Graduated Color Renderer 생성

이번 시간에는 GeoTools 및  GeoTools의 Extension 중 하나인 Brewer를 이용하여 Graduated Color Renderer를 생성하는 샘플 코드를 작성해 보도록 하겠습니다.

▣ 학습 내용
 - Shapefile DataStore  및 Shapefile 불러오기
 - Function 및  ColorBrewer  사용하기
 - Style 생성 및 SLD XML로 내보내기
 - MapContext  사용하여 지도 보기

▣ Reference
GeoTools Brewer Extension
 - http://docs.geotools.org/latest/userguide/extension/brewer/index.html#

Colorbrewer: Color Advice for Maps : http://colorbrewer2.org/
 - Colorbrewer에서 사용되는 Palette Name들입니다.
 - Diverging: PuOr, BrBG, PRGn, PiYG, RdBu, RdGy, RdYlBu, Spectral, RdYlGn
 - Qualitative: Set1, Pastel1, Set2, Pastel2, Dark2, Set3, Paired, Accents,
 - Sequential: YlGn, YlGnBu, GnBu, BuGn, PuBuGn, PuBu, BuPu, RdPu, PuRd, OrRd, YlOrRd, YlOrBr, Purples, Blues, Greens, Oranges, Reds, Grays,

◎ Ranged ClassificationFunction
 - GeoTools에 지원하는 ClassificationFunction은 다음과 같습니다.
 - JenksNaturalBreaksFunction(Jenks), EqualIntervalFunction(EqualInterval), StandardDeviationFunction(StandardDeviation), QuantileFunction(Quantile )

◎ ArcObjects
 - ArcObjects에서는 IClassifyGEN과  IClassBreaksRenderer를 이용하여 주제도를 작성할 수 있습니다.
IClassBreaksRendererIClassifyGEN

▣ Sample Dataset
 - [Shapefile] Sample Datasets for Spatial Statistics Analysis

▣ Preview
◎ Map
◎ SLD to XML
 - StyledLayerDescriptor

▣ Sample Code
import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.transform.TransformerException;

import org.geotools.brewer.color.BrewerPalette;
import org.geotools.brewer.color.ColorBrewer;
import org.geotools.brewer.color.StyleGenerator;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.function.RangedClassifier;
import org.geotools.map.DefaultMapContext;
import org.geotools.map.MapContext;
import org.geotools.styling.FeatureTypeConstraint;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.SLDTransformer;
import org.geotools.styling.Stroke;
import org.geotools.styling.Style;
import org.geotools.styling.StyleFactory;
import org.geotools.styling.StyledLayerDescriptor;
import org.geotools.styling.UserLayer;
import org.geotools.swing.JMapFrame;
import org.geotools.util.logging.Logging;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.expression.Function;

public class ColorBrewerMap {
    static FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(null);
    static StyleFactory sf = CommonFactoryFinder.getStyleFactory(null);

    static final Logger LOGGER = Logging.getLogger(ColorBrewerMap.class);
    public static void main(String[] args) throws IOException {
        String sourceFolder = "C:/TEMP/spatialstatistics";
        DataStore srcDataStore = getShapefileDataStore(sourceFolder);
        SimpleFeatureSource srcSfs = srcDataStore.getFeatureSource("seoul_hexagon_1000");

        // get featurecollection
        SimpleFeatureCollection srcFc = srcSfs.getFeatures(Filter.INCLUDE);
        String propertyName = "EVE_CNT";
        String functionName = "EqualInterval";
        int numClass = 5;
        String paletteName = "YlGn";

        // prepare classifier
        Function function = ff.function(functionName, ff.property(propertyName), ff.literal(numClass));

        RangedClassifier classifier = (RangedClassifier) function.evaluate(srcFc);

        // prepare ColorBrewer
        ColorBrewer colorBrewer = ColorBrewer.instance();
        BrewerPalette brewerPalette = colorBrewer.getPalette(paletteName);
        Color[] colors = brewerPalette.getColors(classifier.getSize());

        // create style
        SimpleFeatureType featureType = srcFc.getSchema();
        Stroke outline = sf.createStroke(ff.literal(Color.GRAY), ff.literal(1.0f), ff.literal(1.0f));
        FeatureTypeStyle fts = StyleGenerator.createFeatureTypeStyle(classifier,
                ff.property(propertyName), colors, featureType.getTypeName(),
                featureType.getGeometryDescriptor(), StyleGenerator.ELSEMODE_IGNORE,
               1.0, outline);
        Style style = sf.createStyle();
        style.featureTypeStyles().add(fts);

        // export sld as XML
        printStyleToSLD(style);
      
        // show map
        MapContext mapContext = new DefaultMapContext();
        mapContext.setTitle(featureType.getTypeName() + "-" + function.getName());
        mapContext.addLayer(srcFc, style);

        JMapFrame mapFrame = new JMapFrame(mapContext);
        mapFrame.setSize(800, 600);
        mapFrame.enableStatusBar(true);
        mapFrame.enableToolBar(true);
        mapFrame.setVisible(true);
    }
  
    static void printStyleToSLD(Style style) {
        UserLayer layer = sf.createUserLayer();
        layer.setLayerFeatureConstraints(new FeatureTypeConstraint[] { null });
        layer.setName(style.getName());
        layer.addUserStyle(style);

        StyledLayerDescriptor sld = sf.createStyledLayerDescriptor();
        sld.addStyledLayer(layer);

        SLDTransformer styleTransform = new SLDTransformer();
        try {
            styleTransform.setIndentation(4);
            styleTransform.setEncoding(Charset.forName("UTF-8"));
            String xml = styleTransform.transform(sld);

            System.out.println(xml);
        } catch (TransformerException e) {
            LOGGER.log(Level.FINER, e.getMessage(), e);
        }

    }

    static DataStore getShapefileDataStore(String folder) throws IOException {
        Map<String, Serializable> params = new HashMap<String, Serializable>();
        try {
            File file = new File(folder);

            params.put("url", file.toURI().toURL());
            params.put("create spatial index", Boolean.FALSE);
            params.put("charset", "CP949");  // or EUC-KR

            return DataStoreFinder.getDataStore(params);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }

        return null;
    }
}
▣ 심화학습
◎  Normalization Field 사용
만약 ArcGIS에서처럼 Normalization field를 사용하려면 어떻게 해야 할까요?


Function function = ff.function(functionName, ff.property(propertyName), ff.literal(numClass));

에서 아래와 같이  Normalization field와 Divide를 함께 사용하면 됩니다.

String normalProeprtyName = "GEOM_AREA";
Divide divide = ff.divide(ff.property(propertyName), ff.property(normalProeprtyName));
Function function = ff.function(functionName, divide, ff.literal(numClass));

◎  StyleGenerator.createFeatureTypeStyle () 을 직접 구현해 보기

2011년 7월 13일 수요일

[GeoServer]X, Y 필드를 포함한 테이블을 레이어로 퍼블리싱 하기

GeoServer에서는 물리적으로 존재하는 레이어나 테이블을 이용하여 Virtual Table을 구성해서 레이어를 퍼블리싱 할 수 있는 확장기능이 있습니다.
자세한 내용은 아래를 참고하세요.

SQL views - http://docs.geoserver.org/latest/en/user/data/sqlview.html

아래 예제는 이를 조금 확장하여 X, Y 필드를 포함한 테이블을 포인트 레이어로 퍼블리싱 하는 방법을 설명합니다.

◎ 가정
 - 다음과 같이 PostgreSQL(+PostGIS)의 테이블 중 lon(경도, x좌표값으로 가정), lat(위도, y좌표값으로 가정) 필드를 포함하는 테이블이 있습니다.
 - 2011년 7월 4일자 기상청 제공 지진발생현황테이블 엑셀파일

○ PostgreSQL 샘플 테이블 스키마
CREATE TABLE public.earthquake
(
  rgn character varying(10),
  t_year integer,
  et date,
  em numeric,
  lon numeric,
  lat numeric,
  loc character varying(200),
  fin_udt character varying(8)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE public.earthquake OWNER TO postgres;

◎ GeoServer에서 퍼블리싱하기
우선 PostGIS DataStore를 추가합니다. GeoServer에서 DataStore를 추가하는 방법은 아래 포스팅을 참고하세요
 - GeoServer 2.0.0 설치 및 활용 가이드

Layers - Add a new resource를 선택 후 다음과 같이 X, Y 필드값이 포함된 테이블을 확인 후 스키마정보를 미리 확인합니다. 대상 테이블은 경위도 좌표값이 포함된 earthquake 테이블입니다.
위 그림에서 earthquake 테이블의 publish 버튼을 누르지 않고 [Configure new SQL view...]라는 링크를 클릭합니다.

다음 그림과 같이 View Name과 SQL Statemeht를 입력합니다. 필드 이름은 테이블 스키마에 맞게 수정하시면 됩니다. 필요하다면 SQL Statement에서 GeoServer 도움말에 제시된 Where절 및 파라미터화된 SQL을 사용할 수 있습니다.

- View Name = earthquake_point
- SQL Statemeht

SELECT ST_SetSRID(ST_Point(lon, lat),4326) as the_geom, rgn, t_year, et, em, loc FROM earthquake



여기에서 ST_Point는 X, Y 값을 이용해서 Point를 만드는 PostGIS 공간함수, ST_SetSRID는 Geometry에 좌표체계를 등록하는 공간함수입니다.


Attributes 탭에서 Guess geometry type and srid 체크박스를 체크한 후 Refresh 버튼을 누르면 위 그림과 같이 속성필드값이 추가됩니다. 여기에서 위 그림처럼 the_geom의 Type을 Point, SRID=4326(원본 테이블의 X, Y값을 WGS84 경위도로 가정)을 입력합니다.

Save 버튼을 누르면 아래와 같이 Edit Layer 화면이 나타납니다. 이후 과정은 일반적인 레이어 추가과정이므로 생략합니다.

이후 아래 그림과 같이 Edit Layer 탭의 [Edit sql view] 기능을 이용해서 언제든지 SQL을 편집할 수 있습니다.
다음은 Layer Preview 기능을 사용하여 등록된 레이어를 미리보기한 이미지입니다.

이상으로 간단하게 X, Y 필드값을 가진 속성테이블을 레이어로 퍼블리싱하는 과정을 살펴보았습니다.

성능이 허락하는 한 이 외에도 DBMS가 가진 Spatial Function을 이용하여 속성테이블을 레이어로 퍼블리싱하는 방법들이 있습니다. X, Y 필드값과 버퍼거리 필드값 등을 이용해서 폴리곤(ST_Buffer 활용 등)으로 퍼블리싱하는 등이 그 예라 할 수 있겠습니다.

2011년 7월 12일 화요일

[WPS] Basic & Advanced Statistics Process

WPS(Web Processing Service)를 활용하는 KOPSS(국토공간계획지원체계) 및 타 프로젝트에서 활용이 가능하도록 구성한 Basic & Advanced Statistics Process들

1. 개요
- GeoTools와 GeoServer에서 운영되도록 구성
- XML은 Custom PPIO를 확장
- GML은 GeoServer에서 지원하는 GML2, GML3를 지원
- FeatureCollection의 Output Format은 GML, Shape-zip, GeoJSON 외 KML을 확장
- 프로세스들은 ArcGIS와 결과값 비교함
 : 공간통계 프로세스들은 ArcGIS - Float연산, WPS - Double연산, 소숫점 6자리까지 오차 없음

2. WPS Process - Basic & Advanced Statistics

3. GeoServer WPS Request Builder에서 실행한 몇가지 샘플
◎ SA_Classify
○ Request

<?xml version="1.0" encoding="UTF-8"?>
<wps:Execute version="1.0.0" service="WPS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.opengis.net/wps/1.0.0" xmlns:wfs="http://www.opengis.net/wfs" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:gml="http://www.opengis.net/gml" xmlns:ogc="http://www.opengis.net/ogc" xmlns:wcs="http://www.opengis.net/wcs/1.1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd">
  <ows:Identifier>kopss:SA_Classify</ows:Identifier>
  <wps:DataInputs>
    <wps:Input>
      <ows:Identifier>inputFeatures</ows:Identifier>
      <wps:Reference mimeType="text/xml; subtype=wfs-collection/1.0" xlink:href="http://geoserver/wfs" method="POST">
        <wps:Body>
          <wfs:GetFeature service="WFS" version="1.0.0" outputFormat="GML2">
            <wfs:Query typeName="cite:local_gi"/>
          </wfs:GetFeature>
        </wps:Body>
      </wps:Reference>
    </wps:Input>
    <wps:Input>
      <ows:Identifier>inputCoverages</ows:Identifier>
      <wps:Reference mimeType="image/tiff" xlink:href="http://geoserver/wcs" method="POST">
        <wps:Body/>
      </wps:Reference>
    </wps:Input>
    <wps:Input>
      <ows:Identifier>fieldName</ows:Identifier>
      <wps:Data>
        <wps:LiteralData>eve_cnt</wps:LiteralData>
      </wps:Data>
    </wps:Input>
    <wps:Input>
      <ows:Identifier>methodName</ows:Identifier>
      <wps:Data>
        <wps:LiteralData>NaturalBreaks</wps:LiteralData>
      </wps:Data>
    </wps:Input>
    <wps:Input>
      <ows:Identifier>numClasses</ows:Identifier>
      <wps:Data>
        <wps:LiteralData>6</wps:LiteralData>
      </wps:Data>
    </wps:Input>
  </wps:DataInputs>
  <wps:ResponseForm>
    <wps:RawDataOutput mimeType="text/xml">
      <ows:Identifier>result</ows:Identifier>
    </wps:RawDataOutput>
  </wps:ResponseForm>
</wps:Execute>


○ Response

◎ SA_StandardDeviationalEllipse
○ Request



<?xml version="1.0" encoding="UTF-8"?>
<wps:Execute version="1.0.0" service="WPS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.opengis.net/wps/1.0.0" xmlns:wfs="http://www.opengis.net/wfs" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:gml="http://www.opengis.net/gml" xmlns:ogc="http://www.opengis.net/ogc" xmlns:wcs="http://www.opengis.net/wcs/1.1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd">
  <ows:Identifier>kopss:SA_StandardDeviationalEllipse</ows:Identifier>
  <wps:DataInputs>
    <wps:Input>
      <ows:Identifier>inputFeatures</ows:Identifier>
      <wps:Reference mimeType="text/xml; subtype=wfs-collection/1.0" xlink:href="http://geoserver/wfs" method="POST">
        <wps:Body>
          <wfs:GetFeature service="WFS" version="1.0.0" outputFormat="GML2">
            <wfs:Query typeName="cite:seoul_bp_apt"/>
          </wfs:GetFeature>
        </wps:Body>
      </wps:Reference>
    </wps:Input>
    <wps:Input>
      <ows:Identifier>ellipseSize</ows:Identifier>
      <wps:Data>
        <wps:LiteralData>1_STANDARD_DEVIATION</wps:LiteralData>
      </wps:Data>
    </wps:Input>
    <wps:Input>
      <ows:Identifier>caseField</ows:Identifier>
      <wps:Data>
        <wps:LiteralData>SGG_NM</wps:LiteralData>
      </wps:Data>
    </wps:Input>
  </wps:DataInputs>
  <wps:ResponseForm>
    <wps:RawDataOutput mimeType="text/xml; subtype=wfs-collection/1.0">
      <ows:Identifier>result</ows:Identifier>
    </wps:RawDataOutput>
  </wps:ResponseForm>
</wps:Execute>


○ Response
 - 결과는 GML을 선택했으며 caseField 를 시군구로 설정함
 - 아래 지도는 GML을 GeoTools를 이용하여 원본 데이터와 함께 지도화

Spatial Data를 지원하는 국산 DBMS GeoTools용 JdbcDataStore 확장

최신 정보는 이 블로그의 다음 포스트([GeoTools Data Store] 국내 공간 DBMS 및 파일 포맷 지원)를 참조하세요.

Spatial Data를 지원하는 국산 DBMS GeoTools용 JdbcDataStore 확장


1. Altibase Spatial(버전 5.5.1)용 GeoTools JdbcDataStore


2. TmaxSoft - Tibero Spatial
◎ GeoTools에서 DataStore 연결
Map<String, Object> params = new HashMap<String, Object>();
params.put(JDBCDataStoreFactory.DBTYPE.key, "tibero");
params.put(JDBCDataStoreFactory.HOST.key, "localhost");
params.put(JDBCDataStoreFactory.DATABASE.key, "tibero");
params.put(JDBCDataStoreFactory.PORT.key, "8629");
params.put(JDBCDataStoreFactory.USER.key, "tibero");
params.put(JDBCDataStoreFactory.PASSWD.key, "manager");

TiberoNGDataStoreFactory factory = new TiberoNGDataStoreFactory();
DataStore dataStore = factory.createDataStore(params);
// or 
DataStore dataStore = DataStoreFinder.getDataStore(params);

3. REALTIMETECH - Kairos Spatial
◎ GeoTools에서 DataStore 연결
Map<String, Object> params = new HashMap<String, Object>();
params.put(JDBCDataStoreFactory.DBTYPE.key, "kairos");
params.put(JDBCDataStoreFactory.HOST.key, "localhost");
params.put(JDBCDataStoreFactory.DATABASE.key, "test");
params.put(JDBCDataStoreFactory.PORT.key, "5000");
params.put(JDBCDataStoreFactory.USER.key, "root");
params.put(JDBCDataStoreFactory.PASSWD.key, "root");

KairosNGDataStoreFactory factory = new KairosNGDataStoreFactory();
DataStore dataStore = factory.createDataStore(params);
// or
DataStore dataStore = DataStoreFinder.getDataStore(params);


4. GeoServer 활용
◎ GeoServer에서의 Vector Data Store 연결모습
◎ Kairos
◎ Tibero