Создание карты и сохранение ее в изображение с помощью GeoTools [закрыто]

9

Я хотел бы создать карту с GeoTools и сохранить ее в изображение (например, JPEG). Мои требования просты:

  1. Создайте карту мира из 2 слоев: политические границы и сетку. Слои из разных источников и разных проекций.
  2. Выведите карту в различные проекции (например, «EPSG: 5070», «EPSG: 4326», «EPSG: 54012», «EPSG: 54009» и т. Д.)
  3. Зафиксируйте выход в различные AOI (например, от -124,79 до -66,9 лон, от 24,4 до 49,4 лт).

Я хочу сделать это программно, через 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) карта должна обрабатывать перепроектирование для вас. Смотрите QuickStart для примера.

2) вы спрашиваете у карты maxBounds, а не текущие границы, и вы можете захотеть обрезать его по DomainOfValidity, чтобы избежать неприятных странностей.

3) Я не уверен, как вы генерируете свои текстуры, но если вы используете модуль сетки, вы можете уплотнить линии, чтобы они стали дугами.

Редактировать Если я использую States.shp (из GeoServer), я получаю это:

введите описание изображения здесь

используя код здесь .

конец редактирования

Наконец, обработка проекций была недавно улучшена, поэтому вы можете перейти к 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);
        }
    }

В дополнение к установке границ области просмотра, необходимо изменить функцию saveImage, чтобы использовать границы области просмотра вместо map.getMaxBounds ().

Изменить:

mapBounds = map.getMaxBounds();

К этому:

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

Вот вывод:

НАС


Вопрос 3

Благодаря предложению Яна я смог заставить линии широты изгибаться путем уплотнения строки. Вот фрагмент кода из метода 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

Хотя этот подход работает, я надеялся на настройку трансформации или что-то, что дало бы мне линии.

Питер
источник