The patch realizes the reimplementation of the add-pois-to-areas option as discussed in thread http://www.mkgmap.org.uk/pipermail/mkgmap-dev/2011q3/012391.html

Some notes:
* Each POI created by the new Area2POIHook is tagged with mkgmap:area2poi=true * For each multipolygon one POI is created at the center of the largest polygon (largest = biggest covered area; size of inner polygons is subtracted). This means that the POI may be located outside the multipolygon area. * The old implementation contained some additional rules which have not been implemented in the new implementation. I wonder if they are really required:
  1. Skip road name POIs
     =>  Probably you don't have a rule for that in your points file
  2. Skip cities without name
     => I don't know. Maybe that check is relevant.
  3. Skip areas which contains a POI with the same name
=> If you follow the rule "don't tag for renders" there should be an area OR a POI but not both.

The patch is tested a little but if you are interested in you can help me by sending your opinion if it's a good replacement for the old implementation or what can be improved.

Have fun!
WanMil
Index: src/uk/me/parabola/mkgmap/reader/osm/ElementSaver.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/ElementSaver.java	(revision 2045)
+++ src/uk/me/parabola/mkgmap/reader/osm/ElementSaver.java	(working copy)
@@ -550,6 +550,10 @@
 		return wayMap;
 	}
 
+	public Map<Long, Relation> getRelations() {
+		return relationMap;
+	}
+	
 	/**
 	 * Get the bounding box.  This is either the one that was explicitly included in the input
 	 * file, or if none was given, the calculated one.
Index: src/uk/me/parabola/mkgmap/reader/osm/OsmMapDataSource.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/OsmMapDataSource.java	(revision 2045)
+++ src/uk/me/parabola/mkgmap/reader/osm/OsmMapDataSource.java	(working copy)
@@ -56,6 +56,7 @@
 			new RoutingHook(),
 			new HighwayHooks(),
 			new LocationHook(),
+			new Areas2POIHook(),
 	};
 	protected OsmConverter converter;
 	private final Set<String> usedTags = new HashSet<String>();
Index: src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java	(revision 2045)
+++ src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java	(working copy)
@@ -1,6 +1,7 @@
 package uk.me.parabola.mkgmap.reader.osm;
 
-import java.awt.*;
+import java.awt.Polygon;
+import java.awt.Rectangle;
 import java.awt.geom.Area;
 import java.awt.geom.Line2D;
 import java.util.ArrayList;
@@ -15,9 +16,9 @@
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Queue;
 import java.util.Set;
+import java.util.Map.Entry;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.logging.Level;
 
@@ -40,6 +41,9 @@
 	public static final String STYLE_FILTER_LINE = "polyline";
 	public static final String STYLE_FILTER_POLYGON = "polygon";
 	
+	/** A tag that is set with value true on each polygon that is created by the mp processing */
+	public static final String MP_CREATED_TAG = "mkgmap:mp_created";
+	
 	private final Map<Long, Way> tileWayMap;
 	private final Map<Long, String> roleMap = new HashMap<Long, String>();
 	private Map<Long, Way> mpPolygons = new HashMap<Long, Way>();
@@ -49,12 +53,17 @@
 	protected ArrayList<JoinedWay> polygons;
 	protected Set<JoinedWay> intersectingPolygons;
 	
+	protected double largestSize;
+	protected JoinedWay largestOuterPolygon;
+	
 	protected Set<Way> outerWaysForLineTagging;
 	protected Map<String, String> outerTags;
 
 	private final uk.me.parabola.imgfmt.app.Area bbox;
 	protected Area bboxArea;
 	
+	private Coord cOfG = null;
+	
 	/** 
 	 * A point that has a lower or equal squared distance from 
 	 * a line is treated as if it lies one the line.<br/>
@@ -101,6 +110,19 @@
 	}
 	
 
+	/**
+	 * Retrieves the center point of this multipolygon. This is set in the 
+	 * {@link #processElements()} methods so it returns <code>null</code> 
+	 * before that. It can also return <code>null</code> in case the 
+	 * multipolygon could not be processed.<br/>
+	 * The returned point may lie outside the multipolygon area. It is just
+	 * the center point of it.
+	 * 
+	 * @return the center point of this multipolygon (maybe <code>null</code>)
+	 */
+	public Coord getCofG() {
+		return cOfG;
+	}
 	
 	/**
 	 * Retrieves the mp role of the given element.
@@ -893,6 +915,20 @@
 				outerWaysForLineTagging.addAll(currentPolygon.polygon.getOriginalWays());
 			}
 			
+			// calculate the size of the polygon
+			double outerAreaSize = currentPolygon.polygon.getSizeOfArea();
+			if (outerAreaSize > largestSize) {
+				// subtract the holes
+				for (PolygonStatus hole : holes) {
+					outerAreaSize -= hole.polygon.getSizeOfArea();
+				}
+				// is it still larger than the largest known polygon?
+				if (outerAreaSize > largestSize) {
+					largestOuterPolygon = currentPolygon.polygon;
+					largestSize = outerAreaSize;
+				}
+			}
+			
 			// check if the polygon is an outer polygon or 
 			// if there are some holes
 			boolean processPolygon = currentPolygon.outer
@@ -955,7 +991,8 @@
 					
 						// mark this polygons so that only polygon style rules are applied
 						mpWay.addTag(STYLE_FILTER_TAG, STYLE_FILTER_POLYGON);
-					
+						mpWay.addTag(MP_CREATED_TAG, "true");
+						
 						getMpPolygons().put(mpWay.getId(), mpWay);
 					}
 				}
@@ -996,6 +1033,14 @@
 			}
 		}
 
+		if (hasTags(this) == false) {
+			// add tags to the multipolygon that are taken from the outer ways
+			// they may be required by some hooks (e.g. Area2POIHook)
+			for (Entry<String, String> tags : outerTags.entrySet()) {
+				addTag(tags.getKey(), tags.getValue());
+			}
+		}
+		
 		// Go through all original outer ways, create a copy, tag them
 		// with the mp tags and mark them only to be used for polyline processing
 		// This enables the style file to decide if the polygon information or
@@ -1004,6 +1049,7 @@
 			Way lineTagWay =  new Way(FakeIdGenerator.makeFakeId(), orgOuterWay.getPoints());
 			lineTagWay.setName(orgOuterWay.getName());
 			lineTagWay.addTag(STYLE_FILTER_TAG, STYLE_FILTER_LINE);
+			lineTagWay.addTag(MP_CREATED_TAG, "true");
 			for (Entry<String,String> tag : outerTags.entrySet()) {
 				lineTagWay.addTag(tag.getKey(), tag.getValue());
 				
@@ -1025,6 +1071,10 @@
 	protected void postProcessing() {
 		// copy all polygons created by the multipolygon algorithm to the global way map
 		tileWayMap.putAll(mpPolygons);
+		
+		if (largestOuterPolygon != null) {
+			cOfG = largestOuterPolygon.getCofG();
+		}
 	}
 	
 	private void runIntersectionCheck(BitSet unfinishedPolys) {
@@ -1154,7 +1204,8 @@
 		taggedInnerPolygons = null;
 		outerPolygons = null;
 		taggedOuterPolygons = null;
-
+		
+		largestOuterPolygon = null;
 	}
 
 	private CutPoint calcNextCutPoint(AreaCutData areaData) {
@@ -1949,6 +2000,7 @@
 			Way lineTagWay =  new Way(FakeIdGenerator.makeFakeId(), orgOuterWay.getPoints());
 			lineTagWay.setName(orgOuterWay.getName());
 			lineTagWay.addTag(STYLE_FILTER_TAG, STYLE_FILTER_LINE);
+			lineTagWay.addTag(MP_CREATED_TAG, "true");
 			for (Entry<String,String> tag : tags.entrySet()) {
 				lineTagWay.addTag(tag.getKey(), tag.getValue());
 				
@@ -2234,6 +2286,20 @@
 		public List<Way> getOriginalWays() {
 			return originalWays;
 		}
+		
+		/**
+		 * Retrieves a measurement of the area covered by this polygon. The 
+		 * returned value has no unit. It is just a rough comparable value
+		 * because it uses a rectangular coordinate system without correction.
+		 * @return size of the covered areas (0 if the way is not closed)
+		 */
+		public double getSizeOfArea() {
+			if (isClosed()==false) {
+				return 0;
+			}
+
+			return SeaPolygonRelation.calcArea(getPoints());
+		}
 
 		public String toString() {
 			StringBuilder sb = new StringBuilder(200);
Index: src/uk/me/parabola/mkgmap/reader/osm/SeaPolygonRelation.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/SeaPolygonRelation.java	(revision 2045)
+++ src/uk/me/parabola/mkgmap/reader/osm/SeaPolygonRelation.java	(working copy)
@@ -180,7 +180,7 @@
 		seaCoords.clear();
 	}
 
-	private double calcArea(List<Coord> polygon) {
+	public static double calcArea(List<Coord> polygon) {
 		Way w = new Way(0, polygon);
 		if (w.clockwise() == false) {
 			polygon = new ArrayList<Coord>(polygon);
Index: src/uk/me/parabola/mkgmap/reader/osm/Areas2POIHook.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/Areas2POIHook.java	(revision 0)
+++ src/uk/me/parabola/mkgmap/reader/osm/Areas2POIHook.java	(revision 0)
@@ -0,0 +1,83 @@
+package uk.me.parabola.mkgmap.reader.osm;
+
+import uk.me.parabola.imgfmt.app.Coord;
+import uk.me.parabola.log.Logger;
+import uk.me.parabola.util.EnhancedProperties;
+
+public class Areas2POIHook extends OsmReadingHooksAdaptor {
+	private static final Logger log = Logger.getLogger(Areas2POIHook.class);
+
+	private ElementSaver saver;
+	
+	/** Name of the bool tag that is set to true if a POI is created from an area */
+	public static final String AREA2POI_TAG = "mkgmap:area2poi";
+	
+	public Areas2POIHook() {
+	}
+
+	public boolean init(ElementSaver saver, EnhancedProperties props) {
+		if (props.containsKey("add-pois-to-areas") == false) {
+			log.info("Disable Areas2POIHook because add-pois-to-areas option is not set.");
+			return false;
+		}
+		
+		this.saver = saver;
+		
+		return true;
+	}
+	
+	public void end() {
+		int ways2POI = 0;
+		
+		for (Way w : saver.getWays().values()) {
+			// check if it is an area
+			if (w.isClosed() == false) {
+				continue;
+			}
+
+			if (w.getTagCount() == 0) {
+				continue;
+			}
+			
+			// do not add POIs for polygons created by multipolygon processing
+			if (w.isBoolTag(MultiPolygonRelation.MP_CREATED_TAG)) {
+				log.debug("MP processed: Do not create POI for",w.toTagString());
+				continue;
+			}
+			
+			Node poi = new Node(FakeIdGenerator.makeFakeId(), w.getCofG());
+			poi.copyTags(w);
+			poi.deleteTag(MultiPolygonRelation.STYLE_FILTER_TAG);
+			poi.addTag(AREA2POI_TAG, "true");
+			log.debug("Create POI",poi.toTagString(),"from",w.toTagString());
+			saver.addNode(poi);
+			ways2POI++;
+		}
+		
+		log.info(ways2POI, "POIs from single areas created");
+
+		int mps2POI = 0;
+		for (Relation r : saver.getRelations().values()) {
+			
+			// create POIs for multipolygon relations only
+			if (r instanceof MultiPolygonRelation == false) {
+				continue;
+			}
+			
+			Coord point = ((MultiPolygonRelation)r).getCofG();
+			if (point == null) {
+				continue;
+			}
+			
+			Node poi = new Node(FakeIdGenerator.makeFakeId(), point);
+			poi.copyTags(r);
+			// remove the type tag which makes only sense for relations
+			poi.deleteTag("type");
+			poi.addTag(AREA2POI_TAG, "true");
+			log.debug("Create POI",poi.toTagString(),"from mp",r.toTagString());
+			saver.addNode(poi);
+			mps2POI++;
+		}
+		log.info(mps2POI, "POIs from multipolygons created");
+	}
+}
Index: src/uk/me/parabola/mkgmap/main/MapMaker.java
===================================================================
--- src/uk/me/parabola/mkgmap/main/MapMaker.java	(revision 2045)
+++ src/uk/me/parabola/mkgmap/main/MapMaker.java	(working copy)
@@ -39,7 +39,6 @@
 import uk.me.parabola.mkgmap.general.LoadableMapDataSource;
 import uk.me.parabola.mkgmap.general.MapLine;
 import uk.me.parabola.mkgmap.general.MapPoint;
-import uk.me.parabola.mkgmap.general.MapPointFastFindMap;
 import uk.me.parabola.mkgmap.general.MapRoad;
 import uk.me.parabola.mkgmap.general.MapShape;
 import uk.me.parabola.mkgmap.reader.plugin.MapReader;
@@ -58,8 +57,6 @@
 			LoadableMapDataSource src = loadFromFile(args, filename);
 			sort = args.getSort();
 
-			log.info("Making Area POIs for", filename);
-			makeAreaPOIs(args, src);			
 			log.info("Making Road Name POIs for", filename);
 			makeRoadNamePOIS(args, src);
 			return makeMap(args, src);
@@ -150,80 +147,6 @@
 		return src;
 	}
 
-	private void makeAreaPOIs(CommandArgs args, LoadableMapDataSource src) {
-		String s = args.get("add-pois-to-areas", null);
-		if (s != null) {
-			
-			MapPointFastFindMap poiMap = new MapPointFastFindMap();
-
-			for (MapPoint point : src.getPoints()) 
-			{
-				if(!point.isRoadNamePOI()) // Don't put road pois in this list
-					poiMap.put(null, point);
-			}
-			
-			for (MapShape shape : src.getShapes()) {
-				String shapeName = shape.getName();
-
-				int pointType = shape.getPoiType();
-				
-				// only make a point if the shape has a name and we know what type of point to make
-				if (pointType == 0)
-					continue;
-
-				
-				// We don't want to add unnamed cities !!
-				if(MapPoint.isCityType(pointType) && shapeName == null)
-					continue;
-				
-				// check if there is not already a poi in that shape 
-							
-				if(poiMap.findPointInShape(shape, pointType, shapeName) == null) {
-					MapPoint newPoint = new MapPoint();
-					
-					newPoint.setName(shapeName);
-					newPoint.setType(pointType);
-
-					copyAddressInformation(shape, newPoint);
-
-					newPoint.setLocation(shape.getLocation()); // TODO use centroid
-
-					src.getPoints().add(newPoint);
-
-					log.info("created POI ", shapeName, "from shape");
-				}
-			}
-		}
-
-	}
-
-	/**
-	 * Copy the address information from a shape to a POI.  Used when creating
-	 * POIs from areas.
-	 *
-	 * @param shape The shape which contains the address information.
-	 * @param newPoint The new point that will receive the address information.
-	 */
-	private void copyAddressInformation(MapShape shape, MapPoint newPoint) {
-		if (shape.getStreet() != null)
-			newPoint.setStreet(shape.getStreet());
-		if (shape.getCity() != null)
-			newPoint.setCity(shape.getCity());
-		if (shape.getZip() != null)
-			newPoint.setZip(shape.getZip());
-		if (shape.getCountry() != null)
-			newPoint.setCountry(shape.getCountry());
-		if (shape.getRegion() != null)
-			newPoint.setRegion(shape.getRegion());
-		if (shape.getPhone() != null)
-			newPoint.setPhone(shape.getPhone());
-		if (shape.getHouseNumber() != null)
-			newPoint.setHouseNumber(shape.getHouseNumber());
-		if (shape.getIsIn() != null)
-			newPoint.setIsIn(shape.getIsIn());
-	}
-
-
 	void makeRoadNamePOIS(CommandArgs args, LoadableMapDataSource src) {
 		String rnp = args.get("road-name-pois", null);
 		// are road name POIS wanted?
_______________________________________________
mkgmap-dev mailing list
mkgmap-dev@lists.mkgmap.org.uk
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev

Reply via email to