GeoTools ile harita oluşturma ve görüntüye kaydetme [kapalı]


9

GeoTools ile bir harita oluşturmak ve bir görüntüye (örn. JPEG) kaydetmek istiyorum. Gereksinimlerim basit:

  1. İki katmanla bir dünya haritası oluşturun: Siyasi sınırlar ve bir teori. Katmanlar farklı kaynaklardan ve farklı projeksiyonlardan.
  2. Haritayı farklı projeksiyonlara gönderin (örn. "EPSG: 5070", "EPSG: 4326", "EPSG: 54012", "EPSG: 54009", vb.)
  3. Çıkışı farklı AOI'lere klipsleyin (örn. -124,79 ila -66,9 yalnız, 24,4 ila 49,4 lat).

Bunu program aracılığıyla API aracılığıyla yapmak istiyorum. Şimdiye kadar sınırlı bir başarı elde ettim. Bu yaklaşımı kullanarak çeşitli projeksiyonlarda bir harita ve çıktı oluşturmayı öğrendim:

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

Kaydetme yöntemi doğrudan GeoTools web sitesinden yapılır :

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

Reproject yöntemi benim buluşumdur. Biraz kesmek ama belirli bir projeksiyon için bir görüntü çıktısını bulabilmemin tek yolu bu.

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

Çıktı gerçekten kötü:

Yeniden projeksiyondan çıktı

Sanırım birkaç farklı sorum var:

  1. Bu doğru bir yaklaşım mı? Gerçekten katmanları manuel olarak yeniden yazdırmam gerekir mi yoksa MapViewport'un bunu benim için yapması gerekiyor mu?
  2. Çıkışı belirli bir AOI'ye nasıl kırpırım? MapViewport.setBounds (zarf) yöntemini kullanarak sınırlarını ayarlamayı denedim ama saveImage yöntemi sınırlarını yok saymak gibi görünüyor.
  3. Enlem çizgilerimin yay olarak görüntülenmesini nasıl sağlayabilirim? Kaçırdığım bir dönüşüm ayarı var mı?

GeoTools 8.7 kullanıyorum.

Yanıtlar:


1

1) harita sizin için yeniden projeksiyonu ele almalıdır. Örnek için QuickStart'a bakın .

2) haritanın maxBounds için geçerli sınırlar olmadığını sorarsınız ve hoş olmayan tuhaflığı önlemek için CRS'nin DomainOfValidity tarafından kırpmak isteyebilirsiniz.

3) Memnuniyetinizi nasıl oluşturduğunuzdan emin değilim, ancak gridler modülünü kullanırsanız, hatları yay haline getirmek için çizgileri yoğunlaştırabilirsiniz.

Düzenle states.shp (GeoServer) kullanırsanız ben bunu elde:

resim açıklamasını buraya girin

Buradaki kodu kullanarak .

düzenlemeyi bitir

Son olarak, projeksiyon kullanımı yakın zamanda geliştirildi, bu nedenle GeoTools 12 veya 13'e geçmek isteyebilirsiniz.

örnek harita


2

Ian'ın cevabı doğrudur ve ben böyle işaretledim. İlgilenebilecek herkes için bütünlük uğruna ...


Soru 1

Hayır, katmanları manuel olarak yeniden yapmanız gerekmez. Görünüm penceresindeki izdüşümü belirtmek yeterli olmalıdır. Misal:

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

soru 2

Haritayı klibi için, size görünüm sınırlarını ayarlamak gerekir VE saveimage işlevini güncelleyin. Aşağıda, projeksiyon uzantılarına sınırların nasıl ayarlanacağına dair bir örnek verilmiştir:

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

Viewport sınırlarını ayarlamaya ek olarak, saveImage işlevi map.getMaxBounds () yerine viewport sınırlarını kullanacak şekilde değiştirilmelidir.

Değişiklik:

mapBounds = map.getMaxBounds();

Buna:

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

İşte çıktı:

BİZE


Soru 3

Ian'ın önerisi sayesinde, çizgi dizesini yoğunlaştırarak enlem çizgilerini eğriye alabildim. Orijinal yayında başvurulan getGraticules () yönteminden anahtar snippit:

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

Çıktı aşağıdaki gibidir:

Yeniden projeksiyondan çıktı 2

Her ne kadar bu yaklaşım işe yarıyor olsa da, bir dönüşüm ortamı veya benim için çizgileri açacak bir şey umuyordum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.