dhis2-devs team mailing list archive
-
dhis2-devs team
-
Mailing list archive
-
Message #23347
[Branch ~dhis2-devs-core/dhis2/trunk] Rev 11356: PNG maps, simplified model
Merge authors:
Lars Helge Øverland (larshelge)
------------------------------------------------------------
revno: 11356 [merge]
committer: Lars Helge Øverland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Sat 2013-07-06 22:19:30 +0200
message:
PNG maps, simplified model
removed:
dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/GeoToolsMap.java
dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/GeoToolsMapObject.java
modified:
dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/GeoToolsMapGenerationService.java
dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/InternalMap.java
dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/InternalMapObject.java
dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/MapUtils.java
dhis-2/dhis-services/dhis-service-mapgeneration/src/test/java/org/hisp/dhis/mapgenerator/GeoToolsMapObjectTest.java
dhis-2/dhis-services/dhis-service-mapgeneration/src/test/java/org/hisp/dhis/mapgenerator/GeoToolsMapTest.java
--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk
Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== removed file 'dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/GeoToolsMap.java'
--- dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/GeoToolsMap.java 2013-07-06 16:56:35 +0000
+++ dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/GeoToolsMap.java 1970-01-01 00:00:00 +0000
@@ -1,298 +0,0 @@
-package org.hisp.dhis.mapgeneration;
-
-/*
- * Copyright (c) 2004-2012, University of Oslo
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * * Neither the name of the HISP project nor the names of its contributors may
- * be used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-import java.awt.Color;
-import java.awt.Graphics2D;
-import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.image.BufferedImage;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.geotools.data.DataUtilities;
-import org.geotools.feature.DefaultFeatureCollection;
-import org.geotools.feature.SchemaException;
-import org.geotools.feature.simple.SimpleFeatureBuilder;
-import org.geotools.geometry.jts.ReferencedEnvelope;
-import org.geotools.map.FeatureLayer;
-import org.geotools.map.Layer;
-import org.geotools.map.MapContent;
-import org.geotools.renderer.GTRenderer;
-import org.geotools.renderer.lite.StreamingRenderer;
-import org.geotools.styling.SLD;
-import org.geotools.styling.Style;
-import org.opengis.feature.simple.SimpleFeature;
-import org.opengis.feature.simple.SimpleFeatureType;
-
-import com.vividsolutions.jts.geom.Geometry;
-import com.vividsolutions.jts.geom.MultiPolygon;
-import com.vividsolutions.jts.geom.Point;
-import com.vividsolutions.jts.geom.Polygon;
-
-/**
- * This class can be used to render map objects onto a map image. The projection
- * is transformed automatically to "EPSG 3785".
- *
- * @author Kjetil Andresen <kjetand@xxxxxxxxxx>
- * @author Olai Solheim <olais@xxxxxxxxxx>
- */
-public class GeoToolsMap
- extends InternalMap
-{
- private static final String CIRCLE = "Circle";
- private static final String POINT = "Point";
- private static final String POLYGON = "Polygon";
- private static final String MULTI_POLYGON = "MultiPolygon";
- private static final String GEOMETRIES = "geometries";
-
- // The flat list of map objects in this map.
- private List<GeoToolsMapObject> mapObjects;
-
- /**
- * Creates an empty map.
- */
- public GeoToolsMap()
- {
- this.mapObjects = new LinkedList<GeoToolsMapObject>();
- }
-
- /**
- * Creates a map with the given initial map layer.
- *
- * @param layer the initial map layer
- */
- public GeoToolsMap( InternalMapLayer layer )
- {
- this.mapObjects = new LinkedList<GeoToolsMapObject>();
- this.addMapLayer( layer );
- }
-
- /**
- * Creates a map with the given initial map layers.
- *
- * @param layers the list of initial map layers
- */
- public GeoToolsMap( List<InternalMapLayer> layers )
- {
- this.mapObjects = new LinkedList<GeoToolsMapObject>();
- this.addAllMapLayers( layers );
- }
-
- /**
- * Adds a map object to this map.
- *
- * @param mapObject the map object
- */
- public void addMapObject( GeoToolsMapObject mapObject )
- {
- this.mapObjects.add( mapObject );
- }
-
- /**
- * Adds all map objects contained in the list.
- *
- * @param mapObjects the list of map objects
- */
- public void addMapObjects( List<GeoToolsMapObject> mapObjects )
- {
- this.mapObjects.addAll( mapObjects );
- }
-
- // -------------------------------------------------------------------------
- // InternalMap implementation
- // -------------------------------------------------------------------------
-
- public void addMapLayer( InternalMapLayer layer )
- {
- for ( InternalMapObject mapObject : layer.getAllMapObjects() )
- {
- addMapObject( (GeoToolsMapObject) mapObject );
- }
- }
-
- public void addAllMapLayers( List<InternalMapLayer> layers )
- {
- for ( InternalMapLayer layer : layers )
- {
- for ( InternalMapObject mapObject : layer.getAllMapObjects() )
- {
- addMapObject( (GeoToolsMapObject) mapObject );
- }
- }
- }
-
- public BufferedImage render()
- {
- return render( DEFAULT_MAP_WIDTH );
- }
-
- public BufferedImage render( int imageWidth )
- {
- MapContent map = new MapContent();
-
- // Convert map objects to features, and add them to the map
- for ( GeoToolsMapObject mapObject : mapObjects )
- {
- try
- {
- map.addLayer( createFeatureLayerFromMapObject( mapObject ) );
- }
- catch ( SchemaException ex )
- {
- throw new RuntimeException( "Could not add map object: " + mapObject.toString() + ": " + ex.getMessage() );
- }
- }
-
- // Create a renderer for this map
- GTRenderer renderer = new StreamingRenderer();
- renderer.setMapContent( map );
-
- // Calculate image height
- // TODO Might want to add a margin of say 25 pixels surrounding the map
- ReferencedEnvelope mapBounds = map.getMaxBounds();
- double imageHeightFactor = mapBounds.getSpan( 1 ) / mapBounds.getSpan( 0 );
- Rectangle imageBounds = new Rectangle( 0, 0, imageWidth, (int) Math.ceil( imageWidth * imageHeightFactor ) );
-
- // Create an image and get the graphics context from it
- BufferedImage image = new BufferedImage( imageBounds.width, imageBounds.height, BufferedImage.TYPE_INT_ARGB );
- Graphics2D g = (Graphics2D) image.getGraphics();
-
- // Draw a background if the background color is specified
- // NOTE It will be transparent otherwise, which is desired
- if ( backgroundColor != null )
- {
- g.setColor( backgroundColor );
- g.fill( imageBounds );
- }
-
- // Enable anti-aliasing if specified
- if ( isAntiAliasingEnabled )
- {
- g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
- }
- else
- {
- g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
- }
-
- // Render the map
- renderer.paint( g, imageBounds, mapBounds );
-
- map.dispose();
-
- return image;
- }
-
- // -------------------------------------------------------------------------
- // Internal
- // -------------------------------------------------------------------------
-
- /**
- * Creates a feature layer based on a map object.
- */
- private Layer createFeatureLayerFromMapObject( GeoToolsMapObject mapObject )
- throws SchemaException
- {
- SimpleFeatureType featureType = createFeatureType( mapObject.getGeometry() );
- SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder( featureType );
- DefaultFeatureCollection featureCollection = new DefaultFeatureCollection();
-
- Style style = null;
-
- featureBuilder.add( mapObject.getGeometry() );
- SimpleFeature feature = featureBuilder.buildFeature( null );
-
- featureCollection.add( feature );
-
- // Create style for this map object
- if ( mapObject.getGeometry() instanceof Point )
- {
- style = SLD.createPointStyle( CIRCLE, mapObject.getStrokeColor(), mapObject.getFillColor(),
- mapObject.getFillOpacity(), mapObject.getRadius() );
- }
- else if ( mapObject.getGeometry() instanceof Polygon || mapObject.getGeometry() instanceof MultiPolygon )
- {
- style = SLD.createPolygonStyle( mapObject.getStrokeColor(), mapObject.getFillColor(),
- mapObject.getFillOpacity() );
- }
- else
- {
- style = SLD.createSimpleStyle( featureType );
- }
-
- return new FeatureLayer( featureCollection, style );
- }
-
- /**
- * Creates a feature type for a GeoTools geometric primitive.
- */
- private SimpleFeatureType createFeatureType( Geometry geom )
- throws SchemaException
- {
- String type = "";
-
- if ( geom instanceof Point )
- {
- type = POINT;
- }
- else if ( geom instanceof Polygon )
- {
- type = POLYGON;
- }
- else if ( geom instanceof MultiPolygon )
- {
- type = MULTI_POLYGON;
- }
- else
- {
- throw new IllegalArgumentException();
- }
-
- return DataUtilities.createType( GEOMETRIES, "geometry:" + type + ":srid=3785" );
- }
-
- /**
- * Creates an image with text indicating an error.
- */
- @SuppressWarnings( "unused" )
- private BufferedImage createErrorImage( String error )
- {
- String str = "Error creating map image: " + error;
- BufferedImage image = new BufferedImage( 500, 25, BufferedImage.TYPE_INT_RGB );
- Graphics2D g = image.createGraphics();
-
- g.setColor( Color.WHITE );
- g.fill( new Rectangle( 500, 25 ) );
-
- g.setColor( Color.RED );
- g.drawString( str, 1, 12 );
-
- return image;
- }
-}
=== modified file 'dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/GeoToolsMapGenerationService.java'
--- dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/GeoToolsMapGenerationService.java 2013-07-06 16:06:50 +0000
+++ dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/GeoToolsMapGenerationService.java 2013-07-06 20:17:27 +0000
@@ -99,8 +99,8 @@
// Build internal representation of a map using GeoTools, then render it
// to an image
- GeoToolsMap gtMap = new GeoToolsMap( mapLayer );
- BufferedImage mapImage = gtMap.render( height );
+ InternalMap map = new InternalMap( mapLayer );
+ BufferedImage mapImage = MapUtils.render( map, height );
// Build the legend set, then render it to an image
LegendSet legendSet = new LegendSet( mapLayer );
@@ -268,11 +268,11 @@
return mapValues;
}
- private GeoToolsMapObject buildSingleGeoToolsMapObjectForMapLayer( InternalMapLayer mapLayer,
+ private InternalMapObject buildSingleGeoToolsMapObjectForMapLayer( InternalMapLayer mapLayer,
double mapValue, OrganisationUnit orgUnit )
{
// Create and setup an internal map object
- GeoToolsMapObject mapObject = new GeoToolsMapObject();
+ InternalMapObject mapObject = new InternalMapObject();
mapObject.setName( orgUnit.getName() );
mapObject.setValue( mapValue );
mapObject.setFillOpacity( mapLayer.getOpacity() );
@@ -281,7 +281,7 @@
// Build and set the GeoTools-specific geometric primitive that outlines
// the org unit on the map
- mapObject.buildAndApplyGeometryForOrganisationUnit( orgUnit );
+ mapObject.setGeometry( InternalMapObject.buildAndApplyGeometryForOrganisationUnit( orgUnit ) );
// Add the map object to the map layer
mapLayer.addMapObject( mapObject );
=== removed file 'dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/GeoToolsMapObject.java'
--- dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/GeoToolsMapObject.java 2012-03-30 13:07:32 +0000
+++ dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/GeoToolsMapObject.java 1970-01-01 00:00:00 +0000
@@ -1,170 +0,0 @@
-package org.hisp.dhis.mapgeneration;
-
-/*
- * Copyright (c) 2004-2012, University of Oslo
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * * Neither the name of the HISP project nor the names of its contributors may
- * be used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-import org.hisp.dhis.organisationunit.OrganisationUnit;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.vividsolutions.jts.geom.Geometry;
-
-/**
- * This is an extension of InternalMapObject that describes map objects specific
- * to the GeoTools platform.
- *
- * It encapsulates all the members of InternalMapObject with the extension to
- * support addition of a single GeoTools geometric primitive that can be given
- * to the GeoTools renderer directly to render the map, in addition to using the
- * members of its superclass InternalMapObject.
- *
- * @author Olai Solheim <olais@xxxxxxxxxx>
- */
-public class GeoToolsMapObject
- extends InternalMapObject
-{
- private Geometry geometry;
-
- /**
- * Gets the geometry for this map object which is any of the GeoTools
- * primitives.
- *
- * @return the GeoTools geometric primitive
- */
- public Geometry getGeometry()
- {
- return this.geometry;
- }
-
- /**
- * Sets the geometry for this map object which is any of the GeoTools
- * primitives.
- *
- * @param geometry the GeoTools geometric primitive
- */
- public void setGeometry( Geometry geometry )
- {
- this.geometry = geometry;
- }
-
- /**
- * Builds the GeoTools geometric primitive for a given organisation unit and
- * sets it for this map object.
- *
- * Quick guide to how geometry is stored in DHIS:
- *
- * Geometry for org units is stored in the DB as [[[[0.32, -33.87], [23.99,
- * -43.02], ...]]], and may be retrieved by calling the getCoordinates
- * method of OrganisationUnit.
- *
- * The coordinates vary according to feature type, which can be found with a
- * call to getFeatureType of OrganisationUnit. It varies between the
- * following structures (names are omitted in the actual coordinates
- * string):
- *
- * multipolygon = [ polygon0 = [ shell0 = [ point0 = [0.32, -33.87], point1
- * = [23.99, -43.02], point2 = [...]], hole0 = [...], hole1 = [...]],
- * polygon1 = [...] polygon2 = [...]] polygon = [ shell0 = [ point0 = [0.32,
- * -33.87], point1 = [23.99, -43.02]], hole0 = [...], hole1 = [...]]
- *
- * point = [0.32, -33.87]
- *
- * Multi-polygons are stored as an array of polygons. Polygons are stored as
- * an array of linear-rings, where the first linear-ring is the shell, and
- * remaining linear-rings are the holes in the polygon. Linear-rings are
- * stored as an array of points, which in turn is stored as an array of
- * (two) components as a floating point type.
- *
- * There are three types of geometry that may be stored in a DHIS org unit:
- * point, polygon, and multi-polygon. This method supports all three.
- *
- * NOTE However, as of writing, there is a bug in DHIS OrganisationUnit
- * where when getFeatureType reports type Polygon, getCoordinates really
- * returns coordinates in the format of type MultiPolygon.
- *
- * @param orgUnit the organisation unit
- */
- public void buildAndApplyGeometryForOrganisationUnit( OrganisationUnit orgUnit )
- {
- // The final GeoTools primitive
- Geometry primitive = null;
-
- // The DHIS coordinates as string
- String coords = orgUnit.getCoordinates();
-
- // The json root that is parsed from the coordinate string
- JsonNode root = null;
-
- try
- {
- // Create a parser for the json and parse it into root
- JsonParser parser = new ObjectMapper().getJsonFactory().createJsonParser( coords );
- root = parser.readValueAsTree();
- }
- catch ( Exception ex )
- {
- throw new RuntimeException( ex );
- }
-
- // Use the factory to build the correct type based on the feature type
- // Polygon is treated similarly as MultiPolygon
- if ( OrganisationUnit.FEATURETYPE_POINT.equals( orgUnit.getFeatureType() ) )
- {
- primitive = GeoToolsPrimitiveFromJsonFactory.createPointFromJson( root );
- }
- else if ( OrganisationUnit.FEATURETYPE_POLYGON.equals( orgUnit.getFeatureType() ) )
- {
- primitive = GeoToolsPrimitiveFromJsonFactory.createMultiPolygonFromJson( root );
- }
- else if ( OrganisationUnit.FEATURETYPE_MULTIPOLYGON.equals( orgUnit.getFeatureType() ) )
- {
- primitive = GeoToolsPrimitiveFromJsonFactory.createMultiPolygonFromJson( root );
- }
- else
- {
- throw new RuntimeException( "Not sure what to do with the feature type '" + orgUnit.getFeatureType() + "'" );
- }
-
- // Set the geometry for this map object
- this.geometry = primitive;
- }
-
- /**
- * Returns a string representing this object, e.g. "GeoToolsMapObject {
- * name: "Khambia", value: 34.22, radius: 1.00, fillColor:
- * java.awt.Color(255, 255, 255), fillOpacity: 0.75, strokeColor:
- * java.awt.Color(0, 0, 0), strokeWidth: 2, geometry: MULTIPOLYGON(((5.2
- * 5.3)(8.2 9.5)(13.2 98.2))) }".
- */
- public String toString()
- {
- return String.format( "GeoToolsMapObject {" + " name: \"%s\"," + " value: %.2f," + " radius: %d,"
- + " fillColor: %s," + " fillOpacity: %.2f" + " strokeColor: %s," + " strokeWidth: %d" + " geometry: %s"
- + "}", name, value, radius, fillColor, fillOpacity, strokeColor, strokeWidth, geometry );
- }
-}
=== modified file 'dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/InternalMap.java'
--- dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/InternalMap.java 2011-12-13 09:43:27 +0000
+++ dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/InternalMap.java 2013-07-06 20:17:27 +0000
@@ -28,7 +28,8 @@
*/
import java.awt.Color;
-import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.LinkedList;
import java.util.List;
/**
@@ -43,87 +44,61 @@
* @author Kjetil Andresen <kjetand@xxxxxxxxxx>
* @author Olai Solheim <olais@xxxxxxxxxx>
*/
-public abstract class InternalMap
+public class InternalMap
{
- // The background color used by this map.
protected Color backgroundColor = null;
- // True if anti-aliasing is enabled, false otherwise.
protected boolean isAntiAliasingEnabled = true;
-
- // The default map image width, in pixels.
- protected static final int DEFAULT_MAP_WIDTH = 500;
-
- /**
- * Gets the background color of this map.
- *
- * @return the background color, or null if not set
- */
+
+ private List<InternalMapObject> mapObjects = new ArrayList<InternalMapObject>();
+
+ public InternalMap()
+ {
+ }
+
public Color getBackgroundColor()
{
- return this.backgroundColor;
+ return backgroundColor;
}
- /**
- * Sets the background color of this map.
- *
- * Setting this to null enables a transparent background.
- *
- * @param backgroundColor the background color
- */
public void setBackgroundColor( Color backgroundColor )
{
this.backgroundColor = backgroundColor;
}
- /**
- * Returns true if anti-aliasing is enabled for rendering, false otherwise.
- *
- * @return true if anti-aliasing is enabled, false otherwise
- */
public boolean isAntiAliasingEnabled()
{
- return this.isAntiAliasingEnabled;
- }
-
- /**
- * Sets if anti-aliasing should be enabled for rendering.
- *
- * @param b true to enable anti-aliasing, false to disable
- */
- public void setAntiAliasingEnabled( boolean b )
- {
- this.isAntiAliasingEnabled = b;
- }
-
- /**
- * Adds a map layer to this map.
- *
- * @param layer the layer
- */
- public abstract void addMapLayer( InternalMapLayer layer );
-
- /**
- * Adds all map layers contained in the list.
- *
- * @param layers the list of layers
- */
- public abstract void addAllMapLayers( List<InternalMapLayer> layers );
-
- /**
- * Renders all map objects contained in this map to an image with the
- * default image width.
- *
- * @return the java.awt.image.BufferedImage representing this map
- */
- public abstract BufferedImage render();
-
- /**
- * Renders all map objects contained in this map to an image with the
- * specified width.
- *
- * @param width the desired width of the map
- * @return the java.awt.image.BufferedImage representing this map
- */
- public abstract BufferedImage render( int imageWidth );
+ return isAntiAliasingEnabled;
+ }
+
+ public void setAntiAliasingEnabled( boolean isAntiAliasingEnabled )
+ {
+ this.isAntiAliasingEnabled = isAntiAliasingEnabled;
+ }
+
+ public List<InternalMapObject> getMapObjects()
+ {
+ return mapObjects;
+ }
+
+ public void setMapObjects( List<InternalMapObject> mapObjects )
+ {
+ this.mapObjects = mapObjects;
+ }
+
+ //TODO remove
+
+ public InternalMap( InternalMapLayer layer )
+ {
+ this.mapObjects = new LinkedList<InternalMapObject>();
+ this.addMapLayer( layer );
+ }
+
+ public void addMapLayer( InternalMapLayer layer )
+ {
+ for ( InternalMapObject mapObject : layer.getAllMapObjects() )
+ {
+ this.mapObjects.add( mapObject );
+ }
+ }
}
=== modified file 'dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/InternalMapObject.java'
--- dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/InternalMapObject.java 2011-12-13 09:43:27 +0000
+++ dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/InternalMapObject.java 2013-07-06 20:19:05 +0000
@@ -29,6 +29,13 @@
import java.awt.Color;
+import org.hisp.dhis.organisationunit.OrganisationUnit;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.vividsolutions.jts.geom.Geometry;
+
/**
* An internal representation of a map object in a map layer.
*
@@ -44,7 +51,7 @@
*
* @author Olai Solheim <olais@xxxxxxxxxx>
*/
-public abstract class InternalMapObject
+public class InternalMapObject
{
protected String name;
@@ -63,189 +70,207 @@
protected InternalMapLayer mapLayer;
protected Interval interval;
+
+ private Geometry geometry;
+
+ // -------------------------------------------------------------------------
+ // Constructors
+ // -------------------------------------------------------------------------
+
+ public InternalMapObject()
+ {
+ }
+
+ // -------------------------------------------------------------------------
+ // Logic
+ // -------------------------------------------------------------------------
/**
- * Gets the name of this map object.
- *
- * @return the name
+ * Builds the GeoTools geometric primitive for a given organisation unit and
+ * sets it for this map object.
+ *
+ * Quick guide to how geometry is stored in DHIS:
+ *
+ * Geometry for org units is stored in the DB as [[[[0.32, -33.87], [23.99,
+ * -43.02], ...]]], and may be retrieved by calling the getCoordinates
+ * method of OrganisationUnit.
+ *
+ * The coordinates vary according to feature type, which can be found with a
+ * call to getFeatureType of OrganisationUnit. It varies between the
+ * following structures (names are omitted in the actual coordinates
+ * string):
+ *
+ * multipolygon = [ polygon0 = [ shell0 = [ point0 = [0.32, -33.87], point1
+ * = [23.99, -43.02], point2 = [...]], hole0 = [...], hole1 = [...]],
+ * polygon1 = [...] polygon2 = [...]] polygon = [ shell0 = [ point0 = [0.32,
+ * -33.87], point1 = [23.99, -43.02]], hole0 = [...], hole1 = [...]]
+ *
+ * point = [0.32, -33.87]
+ *
+ * Multi-polygons are stored as an array of polygons. Polygons are stored as
+ * an array of linear-rings, where the first linear-ring is the shell, and
+ * remaining linear-rings are the holes in the polygon. Linear-rings are
+ * stored as an array of points, which in turn is stored as an array of
+ * (two) components as a floating point type.
+ *
+ * There are three types of geometry that may be stored in a DHIS org unit:
+ * point, polygon, and multi-polygon. This method supports all three.
+ *
+ * NOTE However, as of writing, there is a bug in DHIS OrganisationUnit
+ * where when getFeatureType reports type Polygon, getCoordinates really
+ * returns coordinates in the format of type MultiPolygon.
+ *
+ * @param orgUnit the organisation unit
*/
+ public static Geometry buildAndApplyGeometryForOrganisationUnit( OrganisationUnit orgUnit )
+ {
+ // The final GeoTools primitive
+ Geometry primitive = null;
+
+ // The DHIS coordinates as string
+ String coords = orgUnit.getCoordinates();
+
+ // The json root that is parsed from the coordinate string
+ JsonNode root = null;
+
+ try
+ {
+ // Create a parser for the json and parse it into root
+ JsonParser parser = new ObjectMapper().getJsonFactory().createJsonParser( coords );
+ root = parser.readValueAsTree();
+ }
+ catch ( Exception ex )
+ {
+ throw new RuntimeException( ex );
+ }
+
+ // Use the factory to build the correct type based on the feature type
+ // Polygon is treated similarly as MultiPolygon
+ if ( OrganisationUnit.FEATURETYPE_POINT.equals( orgUnit.getFeatureType() ) )
+ {
+ primitive = GeoToolsPrimitiveFromJsonFactory.createPointFromJson( root );
+ }
+ else if ( OrganisationUnit.FEATURETYPE_POLYGON.equals( orgUnit.getFeatureType() ) )
+ {
+ primitive = GeoToolsPrimitiveFromJsonFactory.createMultiPolygonFromJson( root );
+ }
+ else if ( OrganisationUnit.FEATURETYPE_MULTIPOLYGON.equals( orgUnit.getFeatureType() ) )
+ {
+ primitive = GeoToolsPrimitiveFromJsonFactory.createMultiPolygonFromJson( root );
+ }
+ else
+ {
+ throw new RuntimeException( "Not sure what to do with the feature type '" + orgUnit.getFeatureType() + "'" );
+ }
+
+ return primitive;
+ }
+
+ // -------------------------------------------------------------------------
+ // Getters and setters
+ // -------------------------------------------------------------------------
+
public String getName()
{
return this.name;
}
- /**
- * Sets the name of this map object.
- *
- * @param name the name
- */
public void setName( String name )
{
this.name = name;
}
- /**
- * Gets the value for this map object.
- *
- * @return the value
- */
public double getValue()
{
return this.value;
}
- /**
- * Sets the value for this map object.
- *
- * @param value the value
- */
public void setValue( double value )
{
this.value = value;
}
- /**
- * Gets the radius for this map object (if point).
- *
- * @return the radius
- */
public int getRadius()
{
return this.radius;
}
- /**
- * Sets the radius for this map object (if point).
- *
- * @param radius the fill color
- */
public void setRadius( int radius )
{
this.radius = radius;
}
- /**
- * Gets the fill color for this map object.
- *
- * @return the fill color
- */
public Color getFillColor()
{
return this.fillColor;
}
- /**
- * Sets the fill color for this map object.
- *
- * @param fillColor the fill color
- */
public void setFillColor( Color fillColor )
{
this.fillColor = fillColor;
}
- /**
- * Gets the fill opacity for this object.
- *
- * @return the fill opacity
- */
public float getFillOpacity()
{
return this.fillOpacity;
}
- /**
- * Sets the fill opacity for this object.
- *
- * @param fillOpacity the fill opacity
- */
public void setFillOpacity( float fillOpacity )
{
this.fillOpacity = fillOpacity;
}
- /**
- * Gets the stroke color for this map object.
- *
- * @return the stroke color
- */
public Color getStrokeColor()
{
return this.strokeColor;
}
- /**
- * Sets the stroke color for this map object.
- *
- * @param strokeColor the stroke color
- */
public void setStrokeColor( Color strokeColor )
{
this.strokeColor = strokeColor;
}
- /**
- * Gets the stroke width for this map object.
- *
- * @return the stroke width
- */
public int getStrokeWidth()
{
return this.strokeWidth;
}
- /**
- * Sets the stroke width for this map object.
- *
- * @param strokeWidth
- */
public void setStrokeWidth( int strokeWidth )
{
this.strokeWidth = strokeWidth;
}
- /**
- * Gets the map layer this map object is associated with.
- *
- * @return the map layer
- */
public InternalMapLayer getMapLayer()
{
return this.mapLayer;
}
- /**
- * Sets the map layer this object is associated with.
- *
- * @param mapLayer the map layer
- */
public void setMapLayer( InternalMapLayer mapLayer )
{
this.mapLayer = mapLayer;
}
- /**
- * Gets the interval this map object is associated with.
- *
- * @return the interval
- */
public Interval getInterval()
{
return this.interval;
}
- /**
- * Sets the interval this map object is associated with and updates this map
- * object with the properties (e.g. fill color) from the given interval.
- *
- * @param interval the interval
- */
public void setInterval( Interval interval )
{
this.interval = interval;
this.fillColor = interval.getColor();
}
+ public Geometry getGeometry()
+ {
+ return this.geometry;
+ }
+
+ public void setGeometry( Geometry geometry )
+ {
+ this.geometry = geometry;
+ }
+
/**
* Returns a string representing this object, e.g. "InternalMapObject {
* name: "Khambia", value: 34.22, radius: 1.00, fillColor:
=== modified file 'dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/MapUtils.java'
--- dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/MapUtils.java 2012-03-30 13:07:32 +0000
+++ dhis-2/dhis-services/dhis-service-mapgeneration/src/main/java/org/hisp/dhis/mapgeneration/MapUtils.java 2013-07-06 20:17:27 +0000
@@ -28,8 +28,34 @@
*/
import java.awt.Color;
-
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+
+import org.geotools.data.DataUtilities;
+import org.geotools.feature.DefaultFeatureCollection;
+import org.geotools.feature.SchemaException;
+import org.geotools.feature.simple.SimpleFeatureBuilder;
+import org.geotools.geometry.jts.ReferencedEnvelope;
+import org.geotools.map.FeatureLayer;
+import org.geotools.map.Layer;
+import org.geotools.map.MapContent;
+import org.geotools.renderer.GTRenderer;
+import org.geotools.renderer.lite.StreamingRenderer;
+import org.geotools.styling.SLD;
+import org.geotools.styling.Style;
+import org.hisp.dhis.organisationunit.OrganisationUnit;
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+
+import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.MultiPolygon;
+import com.vividsolutions.jts.geom.Point;
+import com.vividsolutions.jts.geom.Polygon;
/**
* Utility class.
@@ -40,7 +66,15 @@
{
private static final String COLOR_PREFIX = "#";
private static final int COLOR_RADIX = 16;
+
+ private static final String CIRCLE = "Circle";
+ private static final String POINT = "Point";
+ private static final String POLYGON = "Polygon";
+ private static final String MULTI_POLYGON = "MultiPolygon";
+ private static final String GEOMETRIES = "geometries";
+ private static final int DEFAULT_MAP_WIDTH = 500;
+
/**
* Linear interpolation of int.
*
@@ -131,4 +165,152 @@
{
return json != null && json.size() > 0;
}
+
+ // -------------------------------------------------------------------------
+ // Map
+ // -------------------------------------------------------------------------
+
+ public static BufferedImage render( InternalMap map )
+ {
+ return render( map, DEFAULT_MAP_WIDTH );
+ }
+
+ public static BufferedImage render( InternalMap map, int imageWidth )
+ {
+ MapContent mapContent = new MapContent();
+
+ // Convert map objects to features, and add them to the map
+ for ( InternalMapObject mapObject : map.getMapObjects() )
+ {
+ try
+ {
+ mapContent.addLayer( createFeatureLayerFromMapObject( mapObject ) );
+ }
+ catch ( SchemaException ex )
+ {
+ throw new RuntimeException( "Could not add map object: " + mapObject.toString() + ": " + ex.getMessage() );
+ }
+ }
+
+ // Create a renderer for this map
+ GTRenderer renderer = new StreamingRenderer();
+ renderer.setMapContent( mapContent );
+
+ // Calculate image height
+ // TODO Might want to add a margin of say 25 pixels surrounding the map
+ ReferencedEnvelope mapBounds = mapContent.getMaxBounds();
+ double imageHeightFactor = mapBounds.getSpan( 1 ) / mapBounds.getSpan( 0 );
+ Rectangle imageBounds = new Rectangle( 0, 0, imageWidth, (int) Math.ceil( imageWidth * imageHeightFactor ) );
+
+ // Create an image and get the graphics context from it
+ BufferedImage image = new BufferedImage( imageBounds.width, imageBounds.height, BufferedImage.TYPE_INT_ARGB );
+ Graphics2D g = (Graphics2D) image.getGraphics();
+
+ // Draw a background if the background color is specified
+ // NOTE It will be transparent otherwise, which is desired
+ if ( map.getBackgroundColor() != null )
+ {
+ g.setColor( map.getBackgroundColor() );
+ g.fill( imageBounds );
+ }
+
+ // Enable anti-aliasing if specified
+ if ( map.isAntiAliasingEnabled() )
+ {
+ g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
+ }
+ else
+ {
+ g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
+ }
+
+ // Render the map
+ renderer.paint( g, imageBounds, mapBounds );
+
+ mapContent.dispose();
+
+ return image;
+ }
+
+ /**
+ * Creates a feature layer based on a map object.
+ */
+ public static Layer createFeatureLayerFromMapObject( InternalMapObject mapObject )
+ throws SchemaException
+ {
+ SimpleFeatureType featureType = createFeatureType( mapObject.getGeometry() );
+ SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder( featureType );
+ DefaultFeatureCollection featureCollection = new DefaultFeatureCollection();
+
+ Style style = null;
+
+ featureBuilder.add( mapObject.getGeometry() );
+ SimpleFeature feature = featureBuilder.buildFeature( null );
+
+ featureCollection.add( feature );
+
+ // Create style for this map object
+ if ( mapObject.getGeometry() instanceof Point )
+ {
+ style = SLD.createPointStyle( CIRCLE, mapObject.getStrokeColor(), mapObject.getFillColor(),
+ mapObject.getFillOpacity(), mapObject.getRadius() );
+ }
+ else if ( mapObject.getGeometry() instanceof Polygon || mapObject.getGeometry() instanceof MultiPolygon )
+ {
+ style = SLD.createPolygonStyle( mapObject.getStrokeColor(), mapObject.getFillColor(),
+ mapObject.getFillOpacity() );
+ }
+ else
+ {
+ style = SLD.createSimpleStyle( featureType );
+ }
+
+ return new FeatureLayer( featureCollection, style );
+ }
+
+ /**
+ * Creates a feature type for a GeoTools geometric primitive.
+ */
+ public static SimpleFeatureType createFeatureType( Geometry geom )
+ throws SchemaException
+ {
+ String type = "";
+
+ if ( geom instanceof Point )
+ {
+ type = POINT;
+ }
+ else if ( geom instanceof Polygon )
+ {
+ type = POLYGON;
+ }
+ else if ( geom instanceof MultiPolygon )
+ {
+ type = MULTI_POLYGON;
+ }
+ else
+ {
+ throw new IllegalArgumentException();
+ }
+
+ return DataUtilities.createType( GEOMETRIES, "geometry:" + type + ":srid=3785" );
+ }
+
+ /**
+ * Creates an image with text indicating an error.
+ */
+ public static BufferedImage createErrorImage( String error )
+ {
+ String str = "Error creating map image: " + error;
+ BufferedImage image = new BufferedImage( 500, 25, BufferedImage.TYPE_INT_RGB );
+ Graphics2D g = image.createGraphics();
+
+ g.setColor( Color.WHITE );
+ g.fill( new Rectangle( 500, 25 ) );
+
+ g.setColor( Color.RED );
+ g.drawString( str, 1, 12 );
+
+ return image;
+ }
}
=== modified file 'dhis-2/dhis-services/dhis-service-mapgeneration/src/test/java/org/hisp/dhis/mapgenerator/GeoToolsMapObjectTest.java'
--- dhis-2/dhis-services/dhis-service-mapgeneration/src/test/java/org/hisp/dhis/mapgenerator/GeoToolsMapObjectTest.java 2011-12-09 10:29:57 +0000
+++ dhis-2/dhis-services/dhis-service-mapgeneration/src/test/java/org/hisp/dhis/mapgenerator/GeoToolsMapObjectTest.java 2013-07-06 20:17:27 +0000
@@ -5,7 +5,7 @@
import java.awt.Color;
import org.hisp.dhis.DhisSpringTest;
-import org.hisp.dhis.mapgeneration.GeoToolsMapObject;
+import org.hisp.dhis.mapgeneration.InternalMapObject;
import org.junit.Ignore;
import org.junit.Test;
@@ -15,12 +15,12 @@
public class GeoToolsMapObjectTest
extends DhisSpringTest
{
- private GeoToolsMapObject geoToolsMapObject;
+ private InternalMapObject geoToolsMapObject;
@Override
public void setUpTest()
{
- geoToolsMapObject = new GeoToolsMapObject();
+ geoToolsMapObject = new InternalMapObject();
}
@Test
=== modified file 'dhis-2/dhis-services/dhis-service-mapgeneration/src/test/java/org/hisp/dhis/mapgenerator/GeoToolsMapTest.java'
--- dhis-2/dhis-services/dhis-service-mapgeneration/src/test/java/org/hisp/dhis/mapgenerator/GeoToolsMapTest.java 2012-11-20 17:04:08 +0000
+++ dhis-2/dhis-services/dhis-service-mapgeneration/src/test/java/org/hisp/dhis/mapgenerator/GeoToolsMapTest.java 2013-07-06 20:00:33 +0000
@@ -7,7 +7,7 @@
import java.awt.Color;
import org.hisp.dhis.DhisSpringTest;
-import org.hisp.dhis.mapgeneration.GeoToolsMap;
+import org.hisp.dhis.mapgeneration.InternalMap;
import org.junit.Ignore;
import org.junit.Test;
@@ -17,12 +17,12 @@
public class GeoToolsMapTest
extends DhisSpringTest
{
- private GeoToolsMap geoToolsMap;
+ private InternalMap geoToolsMap;
@Override
public void setUpTest()
{
- geoToolsMap = new GeoToolsMap();
+ geoToolsMap = new InternalMap();
}
@Test