dimanche 30 novembre 2014

How to create a map and save it to an image with GeoTools


I would like to create a map with GeoTools and save it to an image (e.g. JPEG). My requirements are simple:



  1. Create a map of the world with 2 layers: Political boundaries and a graticule. The layers are from different sources and different projections.

  2. Output the map to different projections (e.g. "EPSG:5070", "EPSG:4326", "EPSG:54012", "EPSG:54009", etc.)

  3. Clip the output to different AOIs (e.g. -124.79 to -66.9 lon, 24.4 to 49.4 lat).


I want to do this programmatically, via the API. So far, I have had limited success. I have learned to create a map and output in various projections using this approach:



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


The save method is straight from the GeoTools website:



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


The reproject method is my invention. It's a bit of a hack but its the only way I could find to output an image to a specific projection.



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


The output is really bad:


Output from reprojection


So, I guess I have a couple different questions:



  1. Is this the right approch? Do I really need to reproject layers manually or is the MapViewport supposed to do this for me?

  2. How do I clip the output to a specific AOI? I have tried setting the bounds using the MapViewport.setBounds(envelope) method but the saveImage method seems to ignore the bounds.

  3. How do I get my latitude lines to render as arcs? Is there a transform setting that I am missing?


I am using GeoTools 8.7.


Thanks in advance!





Aucun commentaire:

Enregistrer un commentaire