GeoTools를 사용하여지도를 작성하고 이미지에 저장 [닫기]


9

GeoTools로지도를 만들고 이미지 (예 : JPEG)로 저장하고 싶습니다. 내 요구 사항은 간단합니다.

  1. 정치 경계와 계수 선이라는 2 개의 레이어로 세계지도를 만듭니다. 레이어는 다른 소스와 다른 투영에서 나온 것입니다.
  2. 다른 투영으로 맵을 출력합니다 (예 : "EPSG : 5070", "EPSG : 4326", "EPSG : 54012", "EPSG : 54009"등).
  3. 출력을 다른 AOI에 클립하십시오 (예 : -124.79 ~ -66.9 lon, 24.4 ~ 49.4 lat).

API를 통해 프로그래밍 방식으로 수행하고 싶습니다. 지금까지 나는 제한된 성공을 거두었 다. 이 접근법을 사용하여 다양한 투영법으로 맵을 작성하고 출력하는 법을 배웠습니다.

//Step 1: Create map
MapContent map = new MapContent();
map.setTitle("World");

//Step 2: Set projection
CoordinateReferenceSystem crs = CRS.decode("EPSG:5070"); //Conic projection over US
MapViewport vp = map.getViewport();
vp.setCoordinateReferenceSystem(crs);

//Step 3: Add layers to map
CoordinateReferenceSystem mapCRS = map.getCoordinateReferenceSystem();
map.addLayer(reproject(getPoliticalBoundaries(), mapCRS));
map.addLayer(reproject(getGraticules(), mapCRS));

//Step 4: Save image
saveImage(map, "/temp/graticules.jpg", 800);

저장 방법은 GeoTools 웹 사이트 에서 직접 제공됩니다 .

public void saveImage(final MapContent map, final String file, final int imageWidth) {

    GTRenderer renderer = new StreamingRenderer();
    renderer.setMapContent(map);

    Rectangle imageBounds = null;
    ReferencedEnvelope mapBounds = null;
    try {
        mapBounds = map.getMaxBounds();
        double heightToWidth = mapBounds.getSpan(1) / mapBounds.getSpan(0);
        imageBounds = new Rectangle(
                0, 0, imageWidth, (int) Math.round(imageWidth * heightToWidth));

    } catch (Exception e) {
        // failed to access map layers
        throw new RuntimeException(e);
    }

    BufferedImage image = new BufferedImage(imageBounds.width, imageBounds.height, BufferedImage.TYPE_INT_RGB);

    Graphics2D gr = image.createGraphics();
    gr.setPaint(Color.WHITE);
    gr.fill(imageBounds);

    try {
        renderer.paint(gr, imageBounds, mapBounds);
        File fileToSave = new File(file);
        ImageIO.write(image, "jpeg", fileToSave);

    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

재 투영 방법은 저의 발명품입니다. 그것은 약간의 해킹이지만 이미지를 특정 투영으로 출력하는 유일한 방법입니다.

private static Layer reproject(Layer layer, CoordinateReferenceSystem mapCRS) throws Exception {

    SimpleFeatureSource featureSource = (SimpleFeatureSource) layer.getFeatureSource();


  //Define coordinate transformation
    CoordinateReferenceSystem dataCRS = featureSource.getSchema().getCoordinateReferenceSystem();
    boolean lenient = true; // allow for some error due to different datums
    MathTransform transform = CRS.findMathTransform(dataCRS, mapCRS, lenient);


  //Create new feature collection
    SimpleFeatureCollection copy = FeatureCollections.newCollection("internal");
    SimpleFeatureType featureType = SimpleFeatureTypeBuilder.retype(featureSource.getSchema(), mapCRS);
    SimpleFeatureIterator iterator = featureSource.getFeatures().features();
    try {

        while (iterator.hasNext()) {

            SimpleFeature feature = iterator.next();
            Geometry geometry = (Geometry) feature.getDefaultGeometry();
            Geometry geometry2 = JTS.transform(geometry, transform);
            copy.add( SimpleFeatureBuilder.build( featureType, new Object[]{ geometry2 }, null) );
        }

    }
    catch (Exception e) {
        e.printStackTrace();
    }
    finally {
        iterator.close();
    }


  //Return new layer
    Style style = SLD.createLineStyle(Color.BLACK, 1);
    layer = new FeatureLayer(copy, style);
    layer.setTitle("Graticules");
    return layer;
}

출력이 정말 나쁩니다.

재 투영 결과

그래서 몇 가지 다른 질문이 있다고 생각합니다.

  1. 이것이 올바른 접근입니까? 레이어를 수동으로 재 투영해야합니까, 아니면 MapViewport가이 작업을 수행해야합니까?
  2. 출력을 특정 AOI에 클립하려면 어떻게해야합니까? MapViewport.setBounds (envelope) 메소드를 사용하여 경계를 설정하려고 시도했지만 saveImage 메소드가 경계를 무시하는 것 같습니다.
  3. 위도 선을 호로 렌더링하려면 어떻게해야합니까? 누락 된 변환 설정이 있습니까?

GeoTools 8.7을 사용하고 있습니다.

답변:


1

1)지도가 재 투영을 처리해야합니다. 예제 는 빠른 시작 을 참조하십시오 .

2) 맵에 현재 경계가 아닌 maxBounds인지 묻고 CRS의 DomainOfValidity로 클립하여 불쾌한 기묘함을 피할 수 있습니다.

3) 격자를 어떻게 생성하는지 잘 모르겠지만 그리드 모듈 을 사용 하면 선을 조밀화하여 호가되도록 할 수 있습니다.

편집 내가 GeoServer에서 states.shp를 사용하면 다음과 같은 결과를 얻습니다.

여기에 이미지 설명을 입력하십시오

here 코드를 사용 하십시오 .

편집 종료

마지막으로 투영 처리가 최근 개선되어 GeoTools 12 또는 13으로 이동할 수 있습니다.

예제 맵


2

이안의 대답은 정확하며 그와 같이 표시했습니다. 관심을 가질만한 다른 사람을 위해 완전성을 위해 ...


질문 1

아니요, 레이어를 수동으로 재 투영 할 필요는 없습니다. 뷰포트에 투영을 지정하면 충분합니다. 예:

    MapViewport vp = map.getViewport();
    CoordinateReferenceSystem crs = CRS.decode("EPSG:5070");
    vp.setCoordinateReferenceSystem(crs);

질문 2

지도 클립하려면 뷰포트 경계를 설정해야합니다 saveImage 기능을 업데이트합니다. 다음은 범위를 투영 범위로 설정하는 방법에 대한 예입니다.

    Extent crsExtent = crs.getDomainOfValidity();
    for (GeographicExtent element : crsExtent.getGeographicElements()) {
        if (element instanceof GeographicBoundingBox) {
            GeographicBoundingBox bounds = (GeographicBoundingBox) element;
            ReferencedEnvelope bbox = new ReferencedEnvelope(
                bounds.getSouthBoundLatitude(),
                bounds.getNorthBoundLatitude(),
                bounds.getWestBoundLongitude(),
                bounds.getEastBoundLongitude(),

                CRS.decode("EPSG:4326")
            );
            ReferencedEnvelope envelope = bbox.transform(crs, true);
            vp.setBounds(envelope);
        }
    }

뷰포트 경계를 설정하는 것 외에도 map.getMaxBounds () 대신 뷰포트 경계를 사용하도록 saveImage 함수를 수정해야합니다.

변화:

mapBounds = map.getMaxBounds();

이에:

mapBounds = map.getViewport().getBounds();

출력은 다음과 같습니다.

우리


질문 3

Ian의 제안 덕분에 줄 문자열을 조밀하여 위도 선을 구부릴 수있었습니다. 다음은 원래 게시물에서 참조 된 getGraticules () 메서드의 주요 내용입니다.

  //Add lines of latitude
    for (int y=-90; y<=90; y+=15){
        java.util.ArrayList<Coordinate> coords = new java.util.ArrayList<Coordinate>();
        for (double x=-135; x<=-45; x+=0.5){
            coords.add(new Coordinate(y,x,0));
        }
        LineString line = new LineString(coords.toArray(new Coordinate[coords.size()]), precisionModel, 4326);
        collection.add( SimpleFeatureBuilder.build( TYPE, new Object[]{ line }, null) );
    }

출력은 다음과 같습니다.

재 투영 2의 출력

이 접근 방식이 효과가 있지만 변환 설정이나 선을 호로 만드는 무언가를 원했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.