Tạo bản đồ và lưu nó vào hình ảnh với GeoTools [đã đóng]


9

Tôi muốn tạo một bản đồ với GeoTools và lưu nó vào một hình ảnh (ví dụ JPEG). Yêu cầu của tôi rất đơn giản:

  1. Tạo một bản đồ thế giới với 2 lớp: Ranh giới chính trị và một mạng lưới. Các lớp là từ các nguồn khác nhau và dự đoán khác nhau.
  2. Xuất bản đồ thành các phép chiếu khác nhau (ví dụ: "EPSG: 5070", "EPSG: 4326", "EPSG: 54012", "EPSG: 54009", v.v.)
  3. Cắt đầu ra cho các AOI khác nhau (ví dụ: -124,79 đến -66,9 lon, 24,4 đến 49,4 lat).

Tôi muốn làm điều này theo chương trình, thông qua API. Cho đến nay, tôi đã có thành công hạn chế. Tôi đã học cách tạo ra một bản đồ và đầu ra trong các phép chiếu khác nhau bằng cách sử dụng phương pháp này:

//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);

Phương thức lưu trực tiếp từ trang web 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);
    }
}

Phương pháp reproject là phát minh của tôi. Đó là một chút hack nhưng đó là cách duy nhất tôi có thể tìm thấy để đưa hình ảnh ra một hình chiếu cụ thể.

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;
}

Đầu ra rất tệ:

Đầu ra từ sự từ chối

Vì vậy, tôi đoán tôi có một vài câu hỏi khác nhau:

  1. Đây có phải là sự chấp thuận đúng đắn? Tôi có thực sự cần phải định nghĩa lại các lớp theo cách thủ công hay MapViewport có nghĩa vụ phải làm điều này cho tôi không?
  2. Làm cách nào để cắt đầu ra cho AOI cụ thể? Tôi đã thử thiết lập giới hạn bằng phương thức MapViewport.setBound (phong bì) nhưng phương thức saveImage dường như bỏ qua giới hạn.
  3. Làm cách nào để có được các đường vĩ độ của tôi hiển thị dưới dạng cung? Có một thiết lập biến đổi mà tôi đang thiếu?

Tôi đang sử dụng GeoTools 8.7.

Câu trả lời:


1

1) bản đồ sẽ xử lý sự từ chối cho bạn. Xem QuickStart để biết ví dụ.

2) bạn yêu cầu bản đồ cho maxBound không phải giới hạn hiện tại và bạn có thể muốn cắt theo DomainOfValids của CRS để tránh sự kỳ lạ khó chịu.

3) Tôi không chắc chắn làm thế nào bạn tạo ra lưới điện của mình nhưng nếu bạn sử dụng mô-đun lưới, bạn có thể tăng cường các đường để làm cho chúng trở thành vòng cung.

Chỉnh sửa Nếu tôi sử dụng các trạng thái.shp (từ GeoServer), tôi nhận được điều này:

nhập mô tả hình ảnh ở đây

sử dụng mã ở đây .

kết thúc chỉnh sửa

Cuối cùng, việc xử lý trình chiếu đã được cải thiện gần đây, do đó bạn có thể muốn chuyển sang GeoTools 12 hoặc 13.

bản đồ ví dụ


2

Câu trả lời của Ian là chính xác, và tôi đã đánh dấu nó như vậy. Vì lợi ích của sự hoàn thiện cho bất kỳ ai khác có thể quan tâm ...


Câu hỏi 1

Không, bạn không phải thủ công lại các lớp. Chỉ định hình chiếu trên khung nhìn sẽ đủ. Thí dụ:

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

Câu hỏi 2

Để cắt bản đồ, bạn cần đặt giới hạn khung nhìn cập nhật chức năng saveImage. Dưới đây là một ví dụ về cách đặt giới hạn cho phạm vi chiếu:

    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);
        }
    }

Ngoài việc đặt giới hạn chế độ xem, nên lưu sửa đổi hàm saveImage để sử dụng giới hạn chế độ xem thay vì map.getMaxBound ().

Thay đổi:

mapBounds = map.getMaxBounds();

Về điều này:

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

Đây là đầu ra:

CHÚNG TA


Câu 3

Nhờ đề xuất của Ian, tôi đã có thể làm cho các đường vĩ độ bị cong bằng cách tăng cường chuỗi dòng. Đây là đoạn trích chính từ phương thức getGraticules () được tham chiếu trong bài viết gốc:

  //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) );
    }

Đầu ra như sau:

Đầu ra từ từ chối 2

Mặc dù cách tiếp cận này có hiệu quả, tôi đã hy vọng cho một thiết lập biến đổi hoặc một cái gì đó sẽ xoay quanh các dòng cho tôi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.