Hi,
the attached patch adds a sea polygon (based on the bounding box of the
map) and a multipolygon relation with all lines tagged as
natural=coastline as inner elements. The code merges coastline
components as far as possible.
The patch also contains the multipolygon patch by Rudi which wasn't
commited so far (without this patch, the rendering fails).
The sea polygon is created as "natural=sea", for which I added a mapping
to garmin type 0x32.
Caveat: I have only tested this for islands (Corsica) so far.
Best wishes
Christian
Index: src/uk/me/parabola/mkgmap/reader/osm/Way.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/Way.java (Revision 1115)
+++ src/uk/me/parabola/mkgmap/reader/osm/Way.java (Arbeitskopie)
@@ -76,6 +76,10 @@
}
}
+ public boolean isClosed() {
+ return points.get(0).equals(points.get(points.size()-1));
+ }
+
/**
* A simple representation of this way.
* @return A string with the name and start point
Index: src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java (Revision 1115)
+++ src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java (Arbeitskopie)
@@ -5,7 +5,6 @@
import java.util.List;
import java.util.Map;
-import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.imgfmt.app.Coord;
/**
@@ -23,8 +22,9 @@
* this because the type of the relation is not known until after all
* its tags are read in.
* @param other The relation to base this one on.
+ * @param wayMap Map of all ways.
*/
- public MultiPolygonRelation(Relation other) {
+ public MultiPolygonRelation(Relation other, Map<Long, Way> wayMap) {
setId(other.getId());
for (Map.Entry<Element, String> pairs: other.getRoles().entrySet()){
addElement(pairs.getValue(), pairs.getKey());
@@ -33,8 +33,16 @@
if (value != null && pairs.getKey() instanceof Way) {
Way way = (Way) pairs.getKey();
- if (value.equals("outer"))
- outer = way;
+ if (value.equals("outer")){
+ // duplicate outer way and remove tags for cascaded multipolygons
+ outer = new Way(-way.getId());
+ outer.copyTags(way);
+ for(Coord point: way.getPoints())
+ outer.addPoint(point);
+ wayMap.put(outer.getId(), outer);
+ if (way.getTags() != null)
+ way.getTags().removeAll();
+ }
else if (value.equals("inner"))
inners.add(way);
}
@@ -52,11 +60,20 @@
{
for (Way w: inners) {
if (w != null) {
- List<Coord> pts = w.getPoints();
- int[] insert = findCpa(outer.getPoints(), pts);
- if (insert[0] > 0)
- insertPoints(pts, insert[0], insert[1]);
- pts.clear();
+ int[] insert = findCpa(outer.getPoints(), w.getPoints());
+ //if (insert[0] > 0)
+ insertPoints(w, insert[0], insert[1]);
+
+ // remove tags from inner way that are available in the outer way
+ if (outer.getTags() != null){
+ for (Map.Entry<String, String> mapTags: outer.getTags().getKeyValues().entrySet()){
+ String key = mapTags.getKey();
+ String value = mapTags.getValue();
+ if (w.getTag(key) != null)
+ if (w.getTag(key).equals(value))
+ w.deleteTag(key);
+ }
+ }
}
}
}
@@ -64,22 +81,39 @@
/**
* Insert Coordinates into the outer way.
- * @param inList List of Coordinates to be inserted
+ * @param way Way to be inserted
* @param out Coordinates will be inserted after this point in the outer way.
* @param in Points will be inserted starting at this index,
* then from element 0 to (including) this element;
*/
- private void insertPoints(List<Coord> inList, int out, int in){
+ private void insertPoints(Way way, int out, int in){
List<Coord> outList = outer.getPoints();
+ List<Coord> inList = way.getPoints();
int index = out+1;
for (int i = in; i < inList.size(); i++)
outList.add(index++, inList.get(i));
- for (int i = 0; i <= in; i++)
+ for (int i = 0; i < in; i++)
outList.add(index++, inList.get(i));
-
- //with this line commented we get triangles, when uncommented some areas disappear
- // at least in mapsource, on device itself looks OK.
- outList.add(index,outList.get(out));
+
+ if (outer.getPoints().size() < 32){
+ outList.add(index++, inList.get(in));
+ outList.add(index, outList.get(out));
+ }
+ else {
+ // we shift the nodes to avoid duplicate nodes (large areas only)
+ int oLat = outList.get(out).getLatitude();
+ int oLon = outList.get(out).getLongitude();
+ int iLat = inList.get(in).getLatitude();
+ int iLon = inList.get(in).getLongitude();
+ if ((oLat - iLat) > (oLon - iLon)){
+ outList.add(index++, new Coord(iLat-1, iLon));
+ outList.add(index, new Coord(oLat-1, oLon));
+ }
+ else{
+ outList.add(index++, new Coord(iLat, iLon-1));
+ outList.add(index, new Coord(oLat, oLon-1));
+ }
+ }
}
/**
Index: src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java (Revision 1115)
+++ src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java (Arbeitskopie)
@@ -19,6 +19,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -65,6 +66,7 @@
private final Map<String, Long> fakeIdMap = new HashMap<String, Long>();
private final List<Node> exits = new ArrayList<Node>();
private final List<Way> motorways = new ArrayList<Way>();
+ private final List<Way> shoreline = new ArrayList<Way>();
private static final int MODE_NODE = 1;
private static final int MODE_WAY = 2;
@@ -92,6 +94,7 @@
private final boolean ignoreTurnRestrictions;
private final boolean linkPOIsToWays;
private final boolean routing;
+ private final boolean generateSea;
private final Double minimumArcLength;
private final String frigRoundabouts;
@@ -105,6 +108,7 @@
}
linkPOIsToWays = props.getProperty("link-pois-to-ways", false);
ignoreBounds = props.getProperty("ignore-osm-bounds", false);
+ generateSea = props.getProperty("generate-sea", false);
routing = props.containsKey("route");
String rsa = props.getProperty("remove-short-arcs", null);
if(rsa != null)
@@ -370,6 +374,8 @@
if("motorway".equals(highway) ||
"trunk".equals(highway))
motorways.add(currentWay);
+ if(generateSea && "coastline".equals(currentWay.getTag("natural")))
+ shoreline.add(currentWay);
currentWay = null;
// ways are processed at the end of the document,
// may be changed by a Relation class
@@ -399,7 +405,7 @@
String type = currentRelation.getTag("type");
if (type != null) {
if ("multipolygon".equals(type))
- currentRelation = new MultiPolygonRelation(currentRelation);
+ currentRelation = new MultiPolygonRelation(currentRelation, wayMap);
else if("restriction".equals(type)) {
if(ignoreTurnRestrictions)
@@ -446,6 +452,10 @@
}
coordMap = null;
+
+ if (generateSea)
+ generateSeaPolygon(shoreline);
+
for (Relation r : relationMap.values())
converter.convertRelation(r);
@@ -746,4 +756,95 @@
return fakeIdVal;
}
}
+
+ private void generateSeaPolygon(List<Way> shoreline) {
+ System.out.printf("generating sea\n");
+ long seaId;
+ seaId = (1L << 62) + nextFakeId++;
+ Way sea = new Way(seaId);
+ wayMap.put(seaId, sea);
+ Area bbox = mapper.getBounds();
+ Coord nw = new Coord(bbox.getMinLat(), bbox.getMinLong());
+ Coord ne = new Coord(bbox.getMinLat(), bbox.getMaxLong());
+ Coord sw = new Coord(bbox.getMaxLat(), bbox.getMinLong());
+ Coord se = new Coord(bbox.getMaxLat(), bbox.getMaxLong());
+ sea.addPoint(nw);
+ sea.addPoint(ne);
+ sea.addPoint(se);
+ sea.addPoint(sw);
+ sea.addPoint(nw);
+ sea.addTag("natural", "sea");
+
+ Relation seaRelation = new GeneralRelation((1L << 62) + nextFakeId++);
+ seaRelation.addTag("type", "multipolygon");
+ seaRelation.addElement("outer", sea);
+
+ List<Way> islands = new ArrayList();
+
+ // handle islands (closes shoreline components) first (they're easy)
+ Iterator<Way> it = shoreline.iterator();
+ while (it.hasNext()) {
+ Way w = it.next();
+ if (w.isClosed()) {
+ islands.add(w);
+ it.remove();
+ }
+ }
+ concatenateWays(shoreline);
+ // there may be more islands now
+ it = shoreline.iterator();
+ while (it.hasNext()) {
+ Way w = it.next();
+ if (w.isClosed()) {
+ System.out.println("island after concatenating\n");
+ islands.add(w);
+ it.remove();
+ }
+ }
+
+ for (Way w : islands) {
+ seaRelation.addElement("inner", w);
+ }
+ for (Way w : shoreline) {
+ seaRelation.addElement("inner", w);
+ }
+ seaRelation = new MultiPolygonRelation(seaRelation, wayMap);
+ relationMap.put(seaId, seaRelation);
+ seaRelation.processElements();
+ }
+
+ public void concatenateWays(List<Way> ways) {
+ Map<Coord, Way> beginMap = new HashMap();
+
+ for (Way w : ways) {
+ if (!w.isClosed()) {
+ List<Coord> points = w.getPoints();
+ beginMap.put(points.get(0), w);
+ }
+ }
+
+ int merged = 1;
+ while (merged > 0) {
+ merged = 0;
+ for (Way w1 : ways) {
+ List<Coord> points1 = w1.getPoints();
+ Way w2 = beginMap.get(points1.get(points1.size()-1));
+
+ if (w2 != null) {
+ System.out.printf("merging: %d %d %d\n", ways.size(), w1.getId(), w2.getId());
+ List<Coord> points2 = w2.getPoints();
+ Way wm = new Way((1L << 62) + nextFakeId++);
+ wm.getPoints().addAll(points1);
+ wm.getPoints().addAll(points2);
+ ways.remove(w1);
+ ways.remove(w2);
+ beginMap.remove(points2.get(0));
+ ways.add(wm);
+ beginMap.put(points1.get(0), wm);
+ merged++;
+ break;
+ }
+ }
+ }
+ }
}
Index: src/uk/me/parabola/mkgmap/reader/osm/Element.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/Element.java (Revision 1115)
+++ src/uk/me/parabola/mkgmap/reader/osm/Element.java (Arbeitskopie)
@@ -86,6 +86,7 @@
* element.
*/
public void copyTags(Element other) {
+ if (other.tags != null)
tags = other.tags.copy();
}
@@ -97,4 +98,8 @@
if (this.name == null)
this.name = name;
}
+
+ public Tags getTags() {
+ return tags;
+ }
}
Index: src/uk/me/parabola/mkgmap/reader/osm/Tags.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/Tags.java (Revision 1115)
+++ src/uk/me/parabola/mkgmap/reader/osm/Tags.java (Arbeitskopie)
@@ -16,7 +16,9 @@
*/
package uk.me.parabola.mkgmap.reader.osm;
+import java.util.HashMap;
import java.util.Iterator;
+import java.util.Map;
/**
* Store the tags that belong to an Element.
@@ -111,7 +113,7 @@
}
return null;
}
-
+
/**
* Make a deep copy of this object.
* @return A copy of this object.
@@ -262,4 +264,22 @@
put(e.key, e.value);
}
}
-}
+
+ public void removeAll() {
+ for (int i = 0; i < capacity; i++){
+ keys[i] = null;
+ values[i] = null;
+ }
+ size = 0;
+ }
+
+ public Map<String, String> getKeyValues() {
+ Map<String, String> tagMap = new HashMap<String, String>();
+ for (int i = 0; i < capacity; i++)
+ if (keys[i] != null && values[i] != null)
+ tagMap.put(keys[i], values[i]);
+ return tagMap;
+ }
+
+
+}
\ No newline at end of file
Index: resources/styles/default/polygons
===================================================================
--- resources/styles/default/polygons (Revision 1115)
+++ resources/styles/default/polygons (Arbeitskopie)
@@ -55,6 +55,7 @@
natural=mud [0x51 resolution 20]
natural=scrub [0x4f resolution 20]
natural=water [0x3c resolution 20]
+natural=sea [0x32 resolution 10]
natural=wood [0x50 resolution 18]
place=village [0x03 resolution 18]