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