Sorry, I forgot to include one class in the last patch so that it did not compile.

WanMil
Index: src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java        
(revision 1484)
+++ src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java        
(working copy)
@@ -20,9 +20,8 @@
 import java.io.DataInputStream;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
-import java.io.InputStreamReader;
 import java.io.IOException;
-
+import java.io.InputStreamReader;
 import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -37,6 +36,12 @@
 import java.util.SortedMap;
 import java.util.TreeMap;
 
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
 import uk.me.parabola.imgfmt.app.Area;
 import uk.me.parabola.imgfmt.app.Coord;
 import uk.me.parabola.imgfmt.app.Exit;
@@ -46,6 +51,7 @@
 import uk.me.parabola.mkgmap.general.RoadNetwork;
 import uk.me.parabola.mkgmap.reader.osm.CoordPOI;
 import uk.me.parabola.mkgmap.reader.osm.Element;
+import uk.me.parabola.mkgmap.reader.osm.FakeIdGenerator;
 import uk.me.parabola.mkgmap.reader.osm.GeneralRelation;
 import uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation;
 import uk.me.parabola.mkgmap.reader.osm.Node;
@@ -55,12 +61,6 @@
 import uk.me.parabola.mkgmap.reader.osm.Way;
 import uk.me.parabola.util.EnhancedProperties;
 
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-import org.xml.sax.helpers.DefaultHandler;
-
 /**
  * Reads and parses the OSM XML format.
  *
@@ -92,8 +92,6 @@
 
        private static final long CYCLEWAY_ID_OFFSET = 0x10000000;
 
-       private static final long FAKE_ID_BASE = 1L << 62;
-
        private Node currentNode;
        private Way currentWay;
        private Node currentNodeInWay;
@@ -106,8 +104,6 @@
        private Area bbox;
        private Runnable endTask;
 
-       private long nextFakeId = 1;
-
        private final boolean reportUndefinedNodes;
        private final boolean makeOppositeCycleways;
        private final boolean makeCycleways;
@@ -1054,14 +1050,6 @@
                super.fatalError(e);
        }
 
-       public long makeFakeId() {
-               return FAKE_ID_BASE + nextFakeId++;
-       }
-
-       public boolean isFakeId(long id) {
-               return id >= FAKE_ID_BASE;
-       }
-
        private long idVal(String id) {
                try {
                        // attempt to parse id as a number
@@ -1071,7 +1059,7 @@
                        // if that fails, fake a (hopefully) unique value
                        Long fakeIdVal = fakeIdMap.get(id);
                        if(fakeIdVal == null) {
-                               fakeIdVal = makeFakeId();
+                               fakeIdVal = FakeIdGenerator.makeFakeId();
                                fakeIdMap.put(id, fakeIdVal);
                        }
                        //System.out.printf("%s = 0x%016x\n", id, fakeIdVal);
@@ -1080,6 +1068,7 @@
        }
 
        private void generateSeaPolygon(List<Way> shoreline) {
+               
                Area seaBounds;
                if (bbox != null)
                        seaBounds = bbox;
@@ -1096,7 +1085,7 @@
                                log.info("clipping " + segment);
                                toBeRemoved.add(segment);
                                for (List<Coord> pts : clipped) {
-                                       long id = makeFakeId();
+                                       long id = FakeIdGenerator.makeFakeId();
                                        Way shore = new Way(id, pts);
                                        toBeAdded.add(shore);
                                }
@@ -1123,7 +1112,7 @@
                                // polygon so that the tile's background colour 
will
                                // match the land colour on the tiles that do 
contain
                                // some sea
-                               long landId = makeFakeId();
+                               long landId = FakeIdGenerator.makeFakeId();
                                Way land = new Way(landId);
                                land.addPoint(nw);
                                land.addPoint(sw);
@@ -1137,9 +1126,10 @@
                        return;
                }
 
-               long multiId = makeFakeId();
+               long multiId = FakeIdGenerator.makeFakeId();
                Relation seaRelation = null;
                if(generateSeaUsingMP) {
+                       log.debug("Generate seabounds relation "+multiId);
                        seaRelation = new GeneralRelation(multiId);
                        seaRelation.addTag("type", "multipolygon");
                }
@@ -1173,8 +1163,8 @@
                        if(generateSeaUsingMP)
                                seaRelation.addElement("inner", w);
                        else {
-                               if(!isFakeId(w.getId())) {
-                                       Way w1 = new Way(makeFakeId());
+                               if(!FakeIdGenerator.isFakeId(w.getId())) {
+                                       Way w1 = new 
Way(FakeIdGenerator.makeFakeId());
                                        w1.getPoints().addAll(w.getPoints());
                                        // only copy the name tags
                                        for(String tag : w)
@@ -1233,8 +1223,8 @@
                                        if(generateSeaUsingMP)
                                                seaRelation.addElement("inner", 
w);
                                        else {
-                                               if(!isFakeId(w.getId())) {
-                                                       Way w1 = new 
Way(makeFakeId());
+                                               
if(!FakeIdGenerator.isFakeId(w.getId())) {
+                                                       Way w1 = new 
Way(FakeIdGenerator.makeFakeId());
                                                        
w1.getPoints().addAll(w.getPoints());
                                                        // only copy the name 
tags
                                                        for(String tag : w)
@@ -1247,7 +1237,7 @@
                                        }
                                }
                                else if(allowSeaSectors) {
-                                       seaId = makeFakeId();
+                                       seaId = FakeIdGenerator.makeFakeId();
                                        sea = new Way(seaId);
                                        sea.getPoints().addAll(points);
                                        sea.addPoint(new 
Coord(pEnd.getLatitude(), pStart.getLongitude()));
@@ -1273,13 +1263,18 @@
                        }
                }
                if (generateSeaBackground) {
-                       seaId = makeFakeId();
+                       seaId = FakeIdGenerator.makeFakeId();
                        sea = new Way(seaId);
-                       sea.addPoint(nw);
-                       sea.addPoint(sw);
-                       sea.addPoint(se);
-                       sea.addPoint(ne);
-                       sea.addPoint(nw);
+                       // the sea background area must be a little bigger than 
all
+                       // inner land areas. this is a workaround for a mp 
shortcoming:
+                       // mp is not able to combine outer and inner if they 
intersect
+                       // or have overlaying lines
+                       // the added area will be clipped later by the style 
generator (?)
+                       sea.addPoint(new 
Coord(nw.getLatitude()-1,nw.getLongitude()-1));
+                       sea.addPoint(new 
Coord(sw.getLatitude()+1,sw.getLongitude()-1));
+                       sea.addPoint(new 
Coord(se.getLatitude()+1,se.getLongitude()+1));
+                       sea.addPoint(new 
Coord(ne.getLatitude()-1,ne.getLongitude()+1));
+                       sea.addPoint(new 
Coord(nw.getLatitude()-1,nw.getLongitude()-1));
                        sea.addTag("natural", "sea");
                        log.info("sea: ", sea);
                        wayMap.put(seaId, sea);
@@ -1290,7 +1285,7 @@
                // now construct inner ways from these segments
                NavigableSet<EdgeHit> hits = (NavigableSet<EdgeHit>) 
hitMap.keySet();
                while (!hits.isEmpty()) {
-                       long id = makeFakeId();
+                       long id = FakeIdGenerator.makeFakeId();
                        Way w = new Way(id);
                        wayMap.put(id, w);
 
@@ -1350,8 +1345,8 @@
                        if(generateSeaUsingMP)
                                seaRelation.addElement("inner", w);
                        else {
-                               if(!isFakeId(w.getId())) {
-                                       Way w1 = new Way(makeFakeId());
+                               if(!FakeIdGenerator.isFakeId(w.getId())) {
+                                       Way w1 = new 
Way(FakeIdGenerator.makeFakeId());
                                        w1.getPoints().addAll(w.getPoints());
                                        for(String tag : w)
                                                if(tag.equals("name") || 
tag.endsWith(":name"))
@@ -1483,8 +1478,8 @@
                                        log.info("merging: ", ways.size(), 
w1.getId(), w2.getId());
                                        List<Coord> points2 = w2.getPoints();
                                        Way wm;
-                                       if (!isFakeId(w1.getId())) {
-                                               wm = new Way(makeFakeId());
+                                       if 
(!FakeIdGenerator.isFakeId(w1.getId())) {
+                                               wm = new 
Way(FakeIdGenerator.makeFakeId());
                                                ways.remove(w1);
                                                ways.add(wm);
                                                wm.getPoints().addAll(points1);
Index: src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java      
(revision 1484)
+++ src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java      
(working copy)
@@ -1,174 +1,1193 @@
 package uk.me.parabola.mkgmap.reader.osm;
 
+import java.awt.Polygon;
+import java.awt.Rectangle;
+import java.awt.geom.Area;
+import java.awt.geom.Line2D;
+import java.awt.geom.PathIterator;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.logging.Level;
 
 import uk.me.parabola.imgfmt.app.Coord;
+import uk.me.parabola.log.Logger;
 
 /**
- * Representation of an OSM Multipolygon Relation.
- * This will combine the different roles into one area.
+ * Representation of an OSM Multipolygon Relation.<br/>
+ * The different way of the multipolygon are joined to polygons and inner
+ * polygons are cut out from the outer polygons.
  * 
- * @author Rene_A
+ * @author WanMil
  */
 public class MultiPolygonRelation extends Relation {
-       private Way outer;
-       private final List<Way> outers = new ArrayList<Way>();
-       private final List<Way> inners = new ArrayList<Way>();
-       private final Map<Long, Way> myWayMap;
+       private static final Logger log = Logger
+                       .getLogger(MultiPolygonRelation.class);
+
+       private final Map<Long, Way> tileWayMap;
+       private final Map<Long, String> roleMap = new HashMap<Long, String>();
+
+       private ArrayList<BitSet> containsMatrix;
+       private ArrayList<JoinedWay> rings;
 
        /**
-        * Create an instance based on an existing relation.  We need to do
-        * 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.
+        * if one of these tags are contained in the multipolygon then the outer
+        * ways use the mp tags instead of their own tags.
+        */
+       private static final List<String> polygonTags = 
Arrays.asList("boundary",
+                       "natural", "landuse", "land_area", "building", 
"waterway");
+
+       /**
+        * Create an instance based on an existing relation. We need to do 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, Map<Long, Way> wayMap) {
-               myWayMap = wayMap;
+               tileWayMap = wayMap;
+
                setId(other.getId());
+
+               if (log.isDebugEnabled()) {
+                       log.debug("Construct multipolygon", toBrowseURL());
+               }
+
                for (Map.Entry<String, Element> pair : other.getElements()) {
                        String role = pair.getKey();
                        Element el = pair.getValue();
+                       if (log.isDebugEnabled()) {
+                               log.debug(" ", role, el.toBrowseURL());
+                       }
                        addElement(role, el);
+                       roleMap.put(el.getId(), role);
+               }
+
+               setName(other.getName());
+               copyTags(other);
+       }
+
+       /**
+        * Retrieves the mp role of the given element.
+        * 
+        * @param element
+        *            the element
+        * @return the role of the element
+        */
+       private String getRole(Element element) {
+               String role = roleMap.get(element.getId());
+               if (role != null) {
+                       return role;
+               }
 
-                       if (role != null && el instanceof Way) {
-                               Way way = (Way) el;
-                               if ("outer".equals(role)) {
-                                       outers.add(way);
-                               } else if ("inner".equals(role)) {
-                                       inners.add(way);
+               for (Map.Entry<String, Element> r_e : getElements()) {
+                       if (r_e.getValue() == element) {
+                               return r_e.getKey();
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Try to join the two ways.
+        * 
+        * @param joinWay
+        *            the way to which tempWay is added in case both ways could 
be
+        *            joined and checkOnly is false.
+        * @param tempWay
+        *            the way to be added to joinWay
+        * @param checkOnly
+        *            <code>true</code> checks only and does not perform the 
join
+        *            operation
+        * @return <code>true</code> if tempWay way is (or could be) joined to
+        *         joinWay
+        */
+       private boolean joinWays(JoinedWay joinWay, JoinedWay tempWay,
+                       boolean checkOnly) {
+               // use == or equals as comparator??
+               if (joinWay.getPoints().get(0) == tempWay.getPoints().get(0)) {
+                       if (checkOnly == false) {
+                               for (Coord point : 
tempWay.getPoints().subList(1,
+                                               tempWay.getPoints().size())) {
+                                       joinWay.addPoint(0, point);
+                               }
+                               joinWay.addWay(tempWay);
+                       }
+                       return true;
+               } else if (joinWay.getPoints().get(joinWay.getPoints().size() - 
1) == tempWay
+                               .getPoints().get(0)) {
+                       if (checkOnly == false) {
+                               for (Coord point : 
tempWay.getPoints().subList(1,
+                                               tempWay.getPoints().size())) {
+                                       joinWay.addPoint(point);
+                               }
+                               joinWay.addWay(tempWay);
+                       }
+                       return true;
+               } else if (joinWay.getPoints().get(0) == 
tempWay.getPoints().get(
+                               tempWay.getPoints().size() - 1)) {
+                       if (checkOnly == false) {
+                               int insertIndex = 0;
+                               for (Coord point : 
tempWay.getPoints().subList(0,
+                                               tempWay.getPoints().size() - 
1)) {
+                                       joinWay.addPoint(insertIndex, point);
+                                       insertIndex++;
                                }
+                               joinWay.addWay(tempWay);
                        }
+                       return true;
+               } else if (joinWay.getPoints().get(joinWay.getPoints().size() - 
1) == tempWay
+                               .getPoints().get(tempWay.getPoints().size() - 
1)) {
+                       if (checkOnly == false) {
+                               int insertIndex = joinWay.getPoints().size();
+                               for (Coord point : 
tempWay.getPoints().subList(0,
+                                               tempWay.getPoints().size() - 
1)) {
+                                       joinWay.addPoint(insertIndex, point);
+                               }
+                               joinWay.addWay(tempWay);
+                       }
+                       return true;
                }
+               return false;
+       }
 
-               setName(other.getName());
-               copyTags(other);
+       /**
+        * Combine a list of way segments to a list of maximally joined ways
+        * 
+        * @param segments
+        *            a list of closed or unclosed ways
+        * @return a list of closed ways
+        */
+       private ArrayList<JoinedWay> joinWays(List<Way> segments) {
+               // TODO check if the closed polygon is valid and implement a
+               // backtracking algorithm to get other combinations
+
+               ArrayList<JoinedWay> joinedWays = new ArrayList<JoinedWay>();
+               if (segments == null || segments.size() == 0) {
+                       return joinedWays;
+               }
+
+               // go through all segments and categorize them to closed and 
unclosed
+               // list
+               ArrayList<JoinedWay> unclosedWays = new ArrayList<JoinedWay>();
+               for (Way orgSegment : segments) {
+                       JoinedWay jw = new JoinedWay(orgSegment);
+                       roleMap.put(jw.getId(), getRole(orgSegment));
+                       if (orgSegment.isClosed()) {
+                               joinedWays.add(jw);
+                       } else {
+                               unclosedWays.add(jw);
+                       }
+               }
+
+               while (unclosedWays.isEmpty() == false) {
+                       JoinedWay joinWay = unclosedWays.remove(0);
+
+                       // check if the current way is already closed or if it 
is the last
+                       // way
+                       if (joinWay.isClosed() || unclosedWays.isEmpty()) {
+                               joinedWays.add(joinWay);
+                               continue;
+                       }
+
+                       boolean joined = false;
+
+                       // if we have a way that could be joined but which has 
a wrong role
+                       // then store it here and check in the end if it's 
working
+                       JoinedWay wrongRoleWay = null;
+                       String joinRole = getRole(joinWay);
+
+                       // go through all ways and check if there is a way that 
can be
+                       // joined with it
+                       // in this case join the two ways
+                       // => add all points of tempWay to joinWay, remove 
tempWay and put
+                       // joinWay to the beginning of the list
+                       // (not optimal but understandable - can be optimized 
later)
+                       for (JoinedWay tempWay : unclosedWays) {
+                               if (tempWay.isClosed()) {
+                                       continue;
+                               }
+
+                               String tempRole = getRole(tempWay);
+                               // if a role is not 'inner' or 'outer' then it 
is used as
+                               // universal
+                               // check if the roles of the ways are matching
+                               if (("outer".equals(joinRole) == false && 
"inner"
+                                               .equals(joinRole) == false)
+                                               || ("outer".equals(tempRole) == 
false && "inner"
+                                                               
.equals(tempRole) == false)
+                                               || (joinRole != null && 
joinRole.equals(tempRole))) {
+                                       // the roles are matching => try to 
join both ways
+                                       joined = joinWays(joinWay, tempWay, 
false);
+                               } else {
+                                       // the roles are not matching => test 
if both ways would
+                                       // join
+
+                                       // as long as we don't have an 
alternative way with wrong
+                                       // role
+                                       // or if the alternative way is shorter 
then check if
+                                       // the way with the wrong role could be 
joined
+                                       if (wrongRoleWay == null
+                                                       || 
wrongRoleWay.getPoints().size() < tempWay
+                                                                       
.getPoints().size()) {
+                                               if (joinWays(joinWay, tempWay, 
true)) {
+                                                       // save this way => 
maybe we will use it in the end
+                                                       // if we don't find any 
other way
+                                                       wrongRoleWay = tempWay;
+                                               }
+                                       }
+                               }
+
+                               if (joined) {
+                                       // we have joined the way
+                                       unclosedWays.remove(tempWay);
+                                       break;
+                               }
+                       }
+
+                       if (joined == false && wrongRoleWay != null) {
+
+                               log.warn("Join ways with different roles. 
Multipolygon: "
+                                               + toBrowseURL());
+                               log.warn("Way1 Role:", getRole(joinWay));
+                               logWayURLs(Level.WARNING, "-", joinWay);
+                               log.warn("Way2 Role:", getRole(wrongRoleWay));
+                               logWayURLs(Level.WARNING, "-", wrongRoleWay);
+
+                               joined = joinWays(joinWay, wrongRoleWay, false);
+                               if (joined) {
+                                       // we have joined the way
+                                       unclosedWays.remove(wrongRoleWay);
+                                       break;
+                               }
+                       }
+
+                       if (joined) {
+                               if (joinWay.isClosed()) {
+                                       // it's closed => don't process it again
+                                       joinedWays.add(joinWay);
+                               } else if (unclosedWays.isEmpty()) {
+                                       // no more ways to join with
+                                       // it's not closed but we cannot join 
it more
+                                       joinedWays.add(joinWay);
+                               } else {
+                                       // it is not yet closed => process it 
once again
+                                       unclosedWays.add(0, joinWay);
+                               }
+                       } else {
+                               // it's not closed but we cannot join it more
+                               joinedWays.add(joinWay);
+                       }
+               }
+
+               return joinedWays;
        }
 
-       /** Process the ways in this relation.
-        * Joins way with the role "outer"
-        * Adds ways with the role "inner" to the way with the role "outer"
+       /**
+        * Try to close all unclosed ways in the given list of ways.
+        * 
+        * @param wayList
+        *            a list of ways
         */
-       public void processElements() {
+       private void closeWays(ArrayList<JoinedWay> wayList) {
+               // this is a VERY simple algorithm to close the ways
+               // need to be improved
 
-               if (outers != null) {
-                       // copy first outer way
-                       Iterator<Way> it = outers.iterator();
-                       if (it.hasNext()) {
-                               // duplicate outer way and remove tags for 
cascaded multipolygons
-                               Way tempWay = it.next();
-                               outer = new Way(-tempWay.getId());
-                               outer.copyTags(tempWay);
-                               for(Coord point: tempWay.getPoints()) {
-                                       outer.addPoint(point);
+               for (JoinedWay way : wayList) {
+                       if (way.isClosed() || way.getPoints().size() <= 3) {
+                               continue;
+                       }
+                       Coord p1 = way.getPoints().get(0);
+                       Coord p2 = way.getPoints().get(way.getPoints().size() - 
1);
+                       Line2D closingLine = new 
Line2D.Float(p1.getLongitude(), p1
+                                       .getLatitude(), p2.getLongitude(), 
p2.getLatitude());
+
+                       boolean intersects = false;
+                       Coord lastPoint = null;
+                       // don't use the first and the last point
+                       // the closing line can intersect only in one point or 
complete.
+                       // Both isn't interesting for this check
+                       for (Coord thisPoint : way.getPoints().subList(1,
+                                       way.getPoints().size() - 1)) {
+                               if (lastPoint != null) {
+                                       if 
(closingLine.intersectsLine(lastPoint.getLongitude(),
+                                                       
lastPoint.getLatitude(), thisPoint.getLongitude(),
+                                                       
thisPoint.getLatitude())) {
+                                               intersects = true;
+                                               break;
+                                       }
                                }
-                               myWayMap.put(outer.getId(), outer);
-                               tempWay.removeAllTags();
+                               lastPoint = thisPoint;
+                       }
+
+                       if (intersects == false) {
+                               // close the polygon
+                               // the new way segment does not intersect the 
rest of the
+                               // polygon
+                               log.info("Closing way", way);
+                               log.info("from", 
way.getPoints().get(0).toOSMURL());
+                               log.info("to", 
way.getPoints().get(way.getPoints().size() - 1)
+                                               .toOSMURL());
+                               // mark this ways as artifically closed
+                               way.closeWayArtificial();
+                       }
+               }
+       }
+
+       /**
+        * Removes all ways non closed ways from the given list (
+        * <code>{...@link Way#isClosed()} == false</code>)
+        * 
+        * @param wayList
+        *            list of ways
+        */
+       private void removeUnclosedWays(ArrayList<JoinedWay> wayList) {
+               Iterator<JoinedWay> it = wayList.iterator();
+               boolean first = true;
+               while (it.hasNext()) {
+                       JoinedWay tempWay = it.next();
+                       if (tempWay.isClosed() == false) {
+                               if (first) {
+                                       log.warn(
+                                               "Cannot join the following ways 
to closed polygons. MP-Relation",
+                                                                       
toBrowseURL());
+                               }
+                               logWayURLs(Level.WARNING, "- way:", tempWay);
+
                                it.remove();
+                               first = false;
+                       }
+               }
+       }
+
+       /**
+        * Find all rings that are not contained by any other ring.
+        * 
+        * @param candidates
+        *            all rings that should be checked
+        * @param roleFilter
+        *            an additional filter
+        * @return all ring indexes that are not contained by any other ring
+        */
+       private BitSet findOutmostRings(BitSet candidates, BitSet roleFilter) {
+               BitSet realCandidates = ((BitSet) candidates.clone());
+               realCandidates.and(roleFilter);
+               return findOutmostRings(realCandidates);
+       }
+
+       /**
+        * Finds all rings that are not contained by any other rings and that 
match
+        * to the given role. All rings with index given by 
<var>candidates</var>
+        * are used.
+        * 
+        * @param candidates
+        *            indexes of the rings that should be used
+        * @return the bits of all outmost rings are set to true
+        */
+       private BitSet findOutmostRings(BitSet candidates) {
+               BitSet outmostRings = new BitSet();
+
+               // go through all candidates and check if they are contained by 
any
+               // other candidate
+               for (int candidateIndex = candidates.nextSetBit(0); 
candidateIndex >= 0; candidateIndex = candidates
+                               .nextSetBit(candidateIndex + 1)) {
+                       // check if the candidateIndex ring is not contained by 
any
+                       // other candidate ring
+                       boolean isOutmost = true;
+                       for (int otherCandidateIndex = 
candidates.nextSetBit(0); otherCandidateIndex >= 0; otherCandidateIndex = 
candidates
+                                       .nextSetBit(otherCandidateIndex + 1)) {
+                               if (contains(otherCandidateIndex, 
candidateIndex)) {
+                                       // candidateIndex is not an outmost 
ring because it is
+                                       // contained by
+                                       // the otherCandidateIndex ring
+                                       isOutmost = false;
+                                       break;
+                               }
+                       }
+                       if (isOutmost) {
+                               // this is an outmost ring
+                               // put it to the bitset
+                               outmostRings.set(candidateIndex);
+                       }
+               }
+
+               return outmostRings;
+       }
+
+       private ArrayList<RingStatus> getRingStatus(BitSet outmostRings,
+                       boolean outer) {
+               ArrayList<RingStatus> ringStatusList = new 
ArrayList<RingStatus>();
+               for (int ringIndex = outmostRings.nextSetBit(0); ringIndex >= 
0; ringIndex = outmostRings
+                               .nextSetBit(ringIndex + 1)) {
+                       // ringIndex is the ring that is not contained by any 
other
+                       // ring
+                       JoinedWay ring = rings.get(ringIndex);
+                       ringStatusList.add(new RingStatus(outer, ringIndex, 
ring));
+               }
+               return ringStatusList;
+       }
+
+       /**
+        * Process the ways in this relation. Joins way with the role "outer" 
Adds
+        * ways with the role "inner" to the way with the role "outer"
+        */
+       public void processElements() {
+               log.info("Processing multipolygon", toBrowseURL());
+
+               // don't care about outer and inner declaration
+               // because this is a first try
+               ArrayList<Way> allWays = new ArrayList<Way>();
+
+               for (Map.Entry<String, Element> r_e : getElements()) {
+                       if (r_e.getValue() instanceof Way) {
+                               allWays.add((Way) r_e.getValue());
+                       } else {
+                               log.warn("Non way element", 
r_e.getValue().getId(),
+                                               "in multipolygon", getId());
                        }
-                       
-                       // if we have more than one outer way, we join them if 
they are parts of a long way
-                       it = outers.iterator();
-                       while (it.hasNext()) {
-                               Way tempWay = it.next();
-                               if (tempWay.getPoints().get(0) == 
outer.getPoints().get(outer.getPoints().size()-1)){
-                                       for(Coord point: tempWay.getPoints()){
-                                               outer.addPoint(point);
+               }
+
+               rings = joinWays(allWays);
+               closeWays(rings);
+               removeUnclosedWays(rings);
+               // now we have closed ways == rings only
+
+               // check if we have at least one ring left
+               if (rings.isEmpty()) {
+                       // do nothing
+                       log.warn("Multipolygon " + toBrowseURL()
+                                       + " does not contain a closed 
polygon.");
+                       return;
+               }
+
+               createContainsMatrix(rings);
+
+               BitSet unfinishedRings = new BitSet(rings.size());
+               unfinishedRings.set(0, rings.size());
+
+               // create bitsets which rings belong to the outer and to the 
inner role
+               BitSet innerRings = new BitSet();
+               BitSet outerRings = new BitSet();
+               int wi = 0;
+               for (Way w : rings) {
+                       String role = getRole(w);
+                       if ("inner".equals(role)) {
+                               innerRings.set(wi);
+                       } else if ("outer".equals(role)) {
+                               outerRings.set(wi);
+                       } else {
+                               // unknown role => it could be both
+                               innerRings.set(wi);
+                               outerRings.set(wi);
+                       }
+                       wi++;
+               }
+
+               Queue<RingStatus> ringWorkingQueue = new 
LinkedBlockingQueue<RingStatus>();
+
+               BitSet outmostRings = findOutmostRings(unfinishedRings, 
outerRings);
+               if (outmostRings.isEmpty()) {
+                       // there's no outmost outer ring
+                       // maybe this is a tile problem
+                       // try to continue with the inner ring
+                       outmostRings = findOutmostRings(unfinishedRings, 
innerRings);
+                       ringWorkingQueue.addAll(getRingStatus(outmostRings, 
false));
+               } else {
+                       ringWorkingQueue.addAll(getRingStatus(outmostRings, 
true));
+               }
+
+               while (ringWorkingQueue.isEmpty() == false) {
+
+                       // the ring is not contained by any other unfinished 
ring
+                       RingStatus currentRing = ringWorkingQueue.poll();
+
+                       // QA: check that all ways carry the role "outer/inner" 
and
+                       // issue warnings
+                       checkRoles(currentRing.ring.getOriginalWays(),
+                                       (currentRing.outer ? "outer" : 
"inner"));
+
+                       // this ring is now processed and should not be used by 
any
+                       // further step
+                       unfinishedRings.clear(currentRing.index);
+
+                       BitSet ringContains = new BitSet();
+                       ringContains.or(containsMatrix.get(currentRing.index));
+                       // use only rings that are contained by the ring
+                       ringContains.and(unfinishedRings);
+                       // ringContains is the intersection of the unfinished 
and
+                       // the contained rings
+
+                       // get the holes
+                       // these are all rings that are in the main ring
+                       // and that are not contained by any other ring
+                       BitSet holeIndexes = findOutmostRings(ringContains,
+                                       (currentRing.outer ? innerRings : 
outerRings));
+
+                       ArrayList<RingStatus> holes = getRingStatus(holeIndexes,
+                                       !currentRing.outer);
+
+                       // these rings must all be checked for inner rings
+                       ringWorkingQueue.addAll(holes);
+
+                       // check if the ring has tags and therefore should be 
processed
+                       boolean processRing = currentRing.outer
+                                       || hasPolygonTags(currentRing.ring);
+
+                       if (processRing) {
+
+                               List<Way> innerWays = new 
ArrayList<Way>(holes.size());
+                               for (RingStatus holeRingStatus : holes) {
+                                       innerWays.add(holeRingStatus.ring);
+                               }
+
+                               List<Way> singularOuterPolygons = 
cutOutInnerRings(
+                                               currentRing.ring, innerWays);
+
+                               if (currentRing.ring.getOriginalWays().size() 
== 1) {
+                                       // the original way was a closed 
polygon which
+                                       // has been replaced by the new cutted 
polygon
+                                       // the original way should not appear
+                                       // so we remove all tags
+                                       currentRing.ring.removeAllTagsDeep();
+                               } else {
+                                       // the ring has been composed by 
several ways
+                                       // they may contain line tags
+                                       // however all polygon tags are not 
processed
+                                       // because they are only lines and not 
polygons
+                                       // so we don't have to remove any tag
+                               }
+
+                               boolean useRelationTags = currentRing.outer
+                                               && hasPolygonTags(this);
+                               if (useRelationTags) {
+                                       // the multipolygon contains tags that 
overwhelm the
+                                       // tags of the outer ring
+                                       for (Way p : singularOuterPolygons) {
+                                               p.copyTags(this);
                                        }
-                                       tempWay.removeAllTags();
-                                       it.remove();
-                                       it = outers.iterator();
+                               }
+
+                               for (Way mpWay : singularOuterPolygons) {
+                                       // put the cutted out polygons to the
+                                       // final way map
+                                       tileWayMap.put(mpWay.getId(), mpWay);
                                }
                        }
-               
-                       for (Way w: inners) {   
-                               if (w != null && outer!= null) {
-                                       int[] insert = 
findCpa(outer.getPoints(), w.getPoints());
-                                       if (insert[0] >= 0 && insert[1] >= 0)
-                                               insertPoints(w, insert[0], 
insert[1]);
+               }
+
+               if (unfinishedRings.isEmpty() == false) {
+                       // we have at least one ring that could not be processed
+                       // Probably we have intersecting polygons
+                       // => issue a warning
+                       log.error("Multipolygon " + toBrowseURL()
+                                       + " contains intersected ways");
+                       ArrayList<RingStatus> ringList = 
getRingStatus(unfinishedRings,
+                                       true);
+                       for (RingStatus ring : ringList) {
+                               logWayURLs(Level.WARNING, "-", ring.ring);
+                       }
+               }
 
-                                       // remove tags from inner way that are 
available in the outer way
-                                       for (Map.Entry<String, String> mapTags: 
outer.getEntryIteratable()){
-                                               String key = mapTags.getKey();
-                                               String value = 
mapTags.getValue();
-                                               if (w.getTag(key) != null){
-                                                       if 
(w.getTag(key).equals(value))
-                                                               
w.deleteTag(key);
+               cleanup();
+       }
+
+       private void cleanup() {
+               roleMap.clear();
+               containsMatrix = null;
+               rings = null;
+       }
+
+       /**
+        * Cut out all inner rings from the outer ring. This will divide the 
outer
+        * ring in several polygons.
+        * 
+        * @param outerRing
+        *            the outer ring
+        * @param innerRings
+        *            a list of inner rings
+        * @return a list of polygons that make the outer ring cut by the inner
+        *         rings
+        */
+       private List<Way> cutOutInnerRings(Way outerRing, List<Way> innerRings) 
{
+               // we use the java.awt.geom.Area class because it's a quick
+               // implementation of what we need
+
+               // this list contains all non overlapping and singular areas
+               // of the outerRing
+               List<Area> outerAreas = new ArrayList<Area>();
+
+               // 1st create an Area object of the outerRing and put it to the 
list
+               Area oa = createArea(outerRing.getPoints());
+
+               // the polygons will be later clipped in the style converter
+               // so it is not necessary to clip it here
+               outerAreas.add(oa);
+
+               // go through all innerRings (holes) and cut them from the 
outerRing
+               for (Way innerRing : innerRings) {
+                       Area innerArea = createArea(innerRing.getPoints());
+
+                       List<Area> outerAfterThisStep = new ArrayList<Area>();
+                       for (Area outerArea : outerAreas) {
+                               // check if this outerArea is probably 
intersected by the inner
+                               // area to save computation time in case it is 
not
+                               if (outerArea.getBounds().createIntersection(
+                                               
innerArea.getBounds()).isEmpty()) {
+                                       outerAfterThisStep.add(outerArea);
+                                       continue;
+                               }
+
+                               // cut the hole
+                               outerArea.subtract(innerArea);
+                               if (outerArea.isEmpty()) {
+                                       // this outer area space can be 
abandoned
+                               } else if (outerArea.isSingular()) {
+                                       // the area is singular
+                                       // => no further splits necessary
+                                       outerAfterThisStep.add(outerArea);
+                               } else {
+                                       // 1st cut in two halfs in the middle 
of the inner area
+
+                                       // Cut the bounding box into two 
rectangles
+                                       Rectangle r1;
+                                       Rectangle r2;
+
+                                       // Get the bounds of this polygon
+                                       Rectangle innerBounds = 
innerArea.getBounds();
+                                       Rectangle outerBounds = 
outerArea.getBounds();
+                                       if (outerBounds.width > 
outerBounds.height) {
+                                               int cutWidth = (innerBounds.x - 
outerBounds.x)
+                                                               + 
innerBounds.width / 2;
+                                               r1 = new 
Rectangle(outerBounds.x, outerBounds.y,
+                                                               cutWidth, 
outerBounds.height);
+                                               r2 = new 
Rectangle(outerBounds.x + cutWidth,
+                                                               outerBounds.y, 
outerBounds.width - cutWidth,
+                                                               
outerBounds.height);
+                                       } else {
+                                               int cutHeight = (innerBounds.y 
- outerBounds.y)
+                                                               + 
innerBounds.height / 2;
+                                               r1 = new 
Rectangle(outerBounds.x, outerBounds.y,
+                                                               
outerBounds.width, cutHeight);
+                                               r2 = new 
Rectangle(outerBounds.x, outerBounds.y
+                                                               + cutHeight, 
outerBounds.width,
+                                                               
outerBounds.height - cutHeight);
+                                       }
+
+                                       // Now find the intersection of these 
two boxes with the
+                                       // original polygon. This will make two 
new areas, and each
+                                       // area will be one (or more) polygons.
+                                       Area a1 = outerArea;
+                                       Area a2 = (Area) a1.clone();
+                                       a1.intersect(new Area(r1));
+                                       a2.intersect(new Area(r2));
+
+                                       
outerAfterThisStep.addAll(areaToSingularAreas(a1));
+                                       
outerAfterThisStep.addAll(areaToSingularAreas(a2));
+                               }
+                       }
+                       outerAreas = outerAfterThisStep;
+               }
+
+               // convert the java.awt.geom.Area back to the mkgmap way
+               List<Way> cutOuterRing = new ArrayList<Way>(outerAreas.size());
+               for (Area area : outerAreas) {
+                       Way w = singularAreaToWay(area, 
FakeIdGenerator.makeFakeId());
+                       if (w != null) {
+                               w.copyTags(outerRing);
+                               cutOuterRing.add(w);
+                       }
+               }
+
+               return cutOuterRing;
+       }
+
+       /**
+        * Convert an area that may contains multiple areas to a list of 
singular
+        * areas
+        * 
+        * @param area
+        *            an area
+        * @return list of singular areas
+        */
+       private List<Area> areaToSingularAreas(Area area) {
+               if (area.isEmpty()) {
+                       return Collections.emptyList();
+               } else if (area.isSingular()) {
+                       return Collections.singletonList(area);
+               } else {
+                       List<Area> singularAreas = new ArrayList<Area>();
+
+                       // all ways in the area MUST define outer areas
+                       // it is not possible that one of the areas define an 
inner segment
+
+                       float[] res = new float[6];
+                       PathIterator pit = area.getPathIterator(null);
+                       float[] prevPoint = new float[6];
+
+                       Polygon p = null;
+                       while (!pit.isDone()) {
+                               int type = pit.currentSegment(res);
+
+                               switch (type) {
+                               case PathIterator.SEG_LINETO:
+                                       if (Arrays.equals(res, prevPoint) == 
false) {
+                                               p.addPoint(Math.round(res[0]), 
Math.round(res[1]));
+                                       }
+                                       break;
+                               case PathIterator.SEG_CLOSE:
+                                       p.addPoint(p.xpoints[0], p.ypoints[0]);
+                                       Area a = new Area(p);
+                                       if (a.isEmpty() == false) {
+                                               singularAreas.add(a);
+                                       }
+                                       p = null;
+                                       break;
+                               case PathIterator.SEG_MOVETO:
+                                       if (p != null) {
+                                               Area a2 = new Area(p);
+                                               if (a2.isEmpty() == false) {
+                                                       singularAreas.add(a2);
                                                }
                                        }
+                                       p = new Polygon();
+                                       p.addPoint(Math.round(res[0]), 
Math.round(res[1]));
+                                       break;
+                               default:
+                                       log.warn(toBrowseURL(), "Unsupported 
path iterator type"
+                                                       + type, ". This is an 
mkgmap error.");
                                }
+
+                               System.arraycopy(res, 0, prevPoint, 0, 6);
+                               pit.next();
                        }
+                       return singularAreas;
                }
        }
-       
+
+       /**
+        * Create a polygon from a list of points.
+        * 
+        * @param points
+        *            list of points
+        * @return the polygon
+        */
+       private Polygon createPolygon(List<Coord> points) {
+               Polygon polygon = new Polygon();
+               for (Coord co : points) {
+                       polygon.addPoint(co.getLongitude(), co.getLatitude());
+               }
+               return polygon;
+       }
+
+       /**
+        * Create an area from a list of points.
+        * 
+        * @param points
+        *            list of points
+        * @return the area
+        */
+       private Area createArea(List<Coord> points) {
+               return new Area(createPolygon(points));
+       }
+
        /**
-        * Insert Coordinates into the outer way.
-        * @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;
+        * Convert an area to an mkgmap way
+        * 
+        * @param area
+        *            the area
+        * @param wayId
+        *            the wayid for the new way
+        * @return a new mkgmap way
         */
-       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));
+       private Way singularAreaToWay(Area area, long wayId) {
+               if (area.isSingular() == false) {
+                       log
+                                       .warn(
+                                                       "singularAreaToWay 
called with non singular area. Multipolygon ",
+                                                       toBrowseURL());
                }
-               for (int i = 0; i < in; i++){
-                       outList.add(index++, inList.get(i));
+               if (area.isEmpty()) {
+                       if (log.isDebugEnabled()) {
+                               log.debug("Empty area.", toBrowseURL());
+                       }
+                       return null;
                }
 
-               // Investigate and see if we can do the first alternative here 
by
-               // changing the polygon splitter.  If not then always do the 
alternative
-               // and remove unused code.
-               if (outer.getPoints().size() < 0 /* Always use alternative 
method for now */) {
-                       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 (Math.abs(oLat - iLat) > Math.abs(oLon - iLon)) {
-                               int delta = (oLon > iLon)? -1 : 1;
-                               outList.add(index++, new Coord(iLat + delta, 
iLon));
-                               outList.add(index, new Coord(oLat + delta, 
oLon));
-                       } else{
-                               int delta = (oLat > iLat)? 1 : -1;
-                               outList.add(index++, new Coord(iLat, iLon + 
delta));
-                               outList.add(index, new Coord(oLat, oLon + 
delta));
+               Way w = null;
+
+               float[] res = new float[6];
+               PathIterator pit = area.getPathIterator(null);
+
+               while (!pit.isDone()) {
+                       int type = pit.currentSegment(res);
+
+                       switch (type) {
+                       case PathIterator.SEG_MOVETO:
+                               w = new Way(wayId);
+                               w.addPoint(new Coord(Math.round(res[1]), 
Math.round(res[0])));
+                               break;
+                       case PathIterator.SEG_LINETO:
+                               w.addPoint(new Coord(Math.round(res[1]), 
Math.round(res[0])));
+                               break;
+                       case PathIterator.SEG_CLOSE:
+                               w.addPoint(w.getPoints().get(0));
+                               return w;
+                       default:
+                               log.warn(toBrowseURL(),
+                                               "Unsupported path iterator 
type" + type,
+                                               ". This is an mkgmap error.");
+                       }
+                       pit.next();
+               }
+               return w;
+       }
+
+       private boolean hasPolygonTags(JoinedWay way) {
+               for (Way segment : way.getOriginalWays()) {
+                       if (hasPolygonTags(segment)) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       private boolean hasPolygonTags(Element element) {
+               for (Map.Entry<String, String> tagEntry : 
element.getEntryIteratable()) {
+                       if (polygonTags.contains(tagEntry.getKey())) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * This is a QA method. All ways of the given wayList are checked if 
they
+        * they carry the checkRole. If not a warning is logged.
+        * 
+        * @param wayList
+        * @param checkRole
+        */
+       private void checkRoles(List<Way> wayList, String checkRole) {
+               // QA: check that all ways carry the role "inner" and issue 
warnings
+               for (Way tempWay : wayList) {
+                       String realRole = getRole(tempWay);
+                       if (checkRole.equals(realRole) == false) {
+                               log.warn("Way", tempWay.getId(), "carries 
role", realRole,
+                                               "but should carry role", 
checkRole);
+                       }
+               }
+       }
+
+       /**
+        * Creates a matrix which polygon contains which polygon. A polygon 
does not
+        * contain itself.
+        * 
+        * @param poplygonList
+        *            a list of polygons
+        */
+       private void createContainsMatrix(List<JoinedWay> poplygonList) {
+               containsMatrix = new ArrayList<BitSet>();
+               for (int i = 0; i < poplygonList.size(); i++) {
+                       containsMatrix.add(new BitSet());
+               }
+
+               // mark which ring contains which ring
+               for (int i = 0; i < poplygonList.size(); i++) {
+                       JoinedWay tempRing = poplygonList.get(i);
+                       BitSet bitSet = containsMatrix.get(i);
+
+                       for (int j = 0; j < poplygonList.size(); j++) {
+                               boolean contains = false;
+                               if (i == j) {
+                                       // this is special: a way does not 
contain itself for
+                                       // our usage here
+                                       contains = false;
+                               } else {
+                                       contains = contains(tempRing, 
poplygonList.get(j));
+                               }
+
+                               if (contains) {
+                                       bitSet.set(j);
+                               }
+                       }
+               }
+
+               if (log.isDebugEnabled()) {
+                       log.debug("Containsmatrix");
+                       for (BitSet b : containsMatrix) {
+                               log.debug(b);
                        }
                }
        }
-       
+
+       /*
+        * this is an alternative createContainsMatrix method seems to speed up 
only
+        * if lots of inner ways are in the mulitpolygon
+        */
+       // private void createContainsMatrix(List<JoinedWay> wayList) {
+       // long t1 = System.currentTimeMillis();
+       //              
+       // for (int i = 0; i < wayList.size(); i++) {
+       // containsMatrix.add(new BitSet());
+       // }
+       //
+       // // use this matrix to check which matrix element has been
+       // // calculated
+       // ArrayList<BitSet> finishedMatrix = new 
ArrayList<BitSet>(wayList.size());
+       //
+       // for (int i = 0; i < wayList.size(); i++) {
+       // BitSet matrixRow = new BitSet();
+       // // an element does not contain itself
+       // matrixRow.set(i);
+       // finishedMatrix.add(matrixRow);
+       // }
+       //
+       // for (int rowIndex = 0; rowIndex < wayList.size(); rowIndex++) {
+       // Way potentialOuterRing = wayList.get(rowIndex);
+       // BitSet containsColumns = containsMatrix.get(rowIndex);
+       // BitSet finishedCol = finishedMatrix.get(rowIndex);
+       //
+       // // get all non calculated columns of the matrix
+       // for (int colIndex = finishedCol.nextClearBit(0); colIndex >= 0 &&
+       // colIndex < wayList.size(); colIndex = finishedCol
+       // .nextClearBit(colIndex + 1)) {
+       //
+       // boolean contains = contains(potentialOuterRing, 
wayList.get(colIndex));
+       //
+       // if (contains) {
+       // containsColumns.set(colIndex);
+       //                                      
+       // // we also know that the inner ring does not contain the outer ring
+       // // so we can set the finished bit for this matrix element
+       // finishedMatrix.get(colIndex).set(rowIndex);
+       //                                      
+       // // additionally we know that the outer ring contains all rings
+       // // that are contained by the inner ring
+       // containsColumns.or(containsMatrix.get(colIndex));
+       // finishedCol.or(containsColumns);
+       // }
+       //
+       // // this matrix element is calculated now
+       // finishedCol.set(colIndex);
+       // }
+       // }
+       //              
+       // long t2 = System.currentTimeMillis();
+       // log.warn("createMatrix",(t2-t1),"ms");
+       // }
+
        /**
-        * find the Closest Point of Approach between two coordinate-lists      
 
-        * This will probably be moved to a Utils class
-        * @param l1 First list of points.
-        * @param l2 Second list of points.
-        * @return The first element is the index in l1, the second in l2 which 
are the closest together.
+        * Checks if the ring with ringIndex1 contains the ring with ringIndex2.
+        * 
+        * @return true if ring(ringIndex1) contains ring(ringIndex2)
         */
-       private static int[] findCpa(List<Coord> l1, List <Coord> l2) {
-               double oldDistance = Double.MAX_VALUE;
-               Coord found1 = null;
-               Coord found2 = null;
+       private boolean contains(int ringIndex1, int ringIndex2) {
+               return containsMatrix.get(ringIndex1).get(ringIndex2);
+       }
+
+       /**
+        * Checks if ring1 contains ring2.
+        * 
+        * @param ring1
+        *            a closed way
+        * @param ring2
+        * @return true if ring1 contains ring2
+        */
+       private boolean contains(JoinedWay ring1, JoinedWay ring2) {
+               // TODO this is a simple algorithm
+               // might be improved
+
+               if (ring1.isClosed() == false) {
+                       return false;
+               }
+               Polygon p = createPolygon(ring1.getPoints());
+
+               Coord p0 = ring2.getPoints().get(0);
+               if (p.contains(p0.getLongitude(), p0.getLatitude()) == false) {
+                       // we have one point that is not in way1 => way1 does 
not contain
+                       // way2
+                       return false;
+               }
 
-               for (Coord c1: l1) {
-                       for(Coord c2: l2) {
-                               double newDistance = 
c1.distanceInDegreesSquared(c2);
-                               if (newDistance < oldDistance) {
-                                       oldDistance = newDistance;
-                                       found1 = c1;
-                                       found2 = c2;                            
-                               }                               
+               // check all lines of way1 and way2 for intersections
+               Iterator<Coord> it2 = ring2.getPoints().iterator();
+               Coord p2_1 = it2.next();
+               Coord p2_2 = null;
+               while (it2.hasNext()) {
+                       p2_2 = p2_1;
+                       p2_1 = it2.next();
+
+                       Iterator<Coord> it1 = ring1.getPoints().iterator();
+                       Coord p1_1 = it1.next();
+                       Coord p1_2 = null;
+                       while (it1.hasNext()) {
+                               p1_2 = p1_1;
+                               p1_1 = it1.next();
+
+                               boolean intersects = 
Line2D.linesIntersect(p1_1.getLongitude(),
+                                               p1_1.getLatitude(), 
p1_2.getLongitude(), p1_2
+                                                               .getLatitude(), 
p2_1.getLongitude(), p2_1
+                                                               .getLatitude(), 
p2_2.getLongitude(), p2_2
+                                                               .getLatitude());
+
+                               if (intersects) {
+                                       if ((ring1.isClosedArtificially() && 
it1.hasNext() == false)
+                                                       || 
(ring2.isClosedArtificially() && it2.hasNext() == false)) {
+                                               // don't care about this 
intersection
+                                               // one of the rings is closed 
by this mp code and the
+                                               // closing way causes the 
intersection
+                                               log
+                                                               .warn("Way", 
ring1, "may contain way", ring2,
+                                                                               
". Ignoring artificial generated intersection.");
+                                       } else {
+                                               return false;
+                                       }
+                               }
                        }
                }
 
-               return new int[]{l1.indexOf(found1), l2.indexOf(found2)};
+               // don't have any intersection
+               // => ring1 contains ring2
+               return true;
+       }
+
+       private void logWayURLs(Level level, String preMsg, Way way) {
+               if (log.isLoggable(level)) {
+                       if (way instanceof JoinedWay) {
+                               for (Way segment : ((JoinedWay) 
way).getOriginalWays()) {
+                                       if (preMsg == null || preMsg.length() 
== 0) {
+                                               log.log(level, 
segment.toBrowseURL());
+                                       } else {
+                                               log.log(level, preMsg, 
segment.toBrowseURL());
+                                       }
+                               }
+                       } else {
+                               if (preMsg == null || preMsg.length() == 0) {
+                                       log.log(level, way.toBrowseURL());
+                               } else {
+                                       log.log(level, preMsg, 
way.toBrowseURL());
+                               }
+                       }
+               }
+       }
+
+       /**
+        * This is a helper class that stores that gives access to the original
+        * segments of a joined way.
+        */
+       private static class JoinedWay extends Way {
+               private final List<Way> originalWays;
+               private boolean closedArtifical = false;
+
+               public JoinedWay(Way originalWay) {
+                       super(FakeIdGenerator.makeFakeId(), new 
ArrayList<Coord>(
+                                       originalWay.getPoints()));
+                       this.originalWays = new ArrayList<Way>();
+                       addWay(originalWay);
+               }
+
+               public void addPoint(int index, Coord point) {
+                       getPoints().add(index, point);
+               }
+
+               public void addWay(Way way) {
+                       if (way instanceof JoinedWay) {
+                               for (Way w : ((JoinedWay) 
way).getOriginalWays()) {
+                                       addWay(w);
+                               }
+                       } else {
+                               if (log.isDebugEnabled()) {
+                                       log.debug("Joined", this.getId(), 
"with", way.getId());
+                               }
+                               this.originalWays.add(way);
+                               addTagsOf(way);
+                               if (getName() == null && way.getName() != null) 
{
+                                       setName(way.getName());
+                               }
+                       }
+               }
+
+               public void closeWayArtificial() {
+                       addPoint(getPoints().get(0));
+                       closedArtifical = true;
+               }
+
+               public boolean isClosedArtificially() {
+                       return closedArtifical;
+               }
+
+               private void addTagsOf(Way way) {
+                       for (Map.Entry<String, String> tag : 
way.getEntryIteratable()) {
+                               if (getTag(tag.getKey()) == null) {
+                                       addTag(tag.getKey(), tag.getValue());
+                               }
+                       }
+               }
+
+               public List<Way> getOriginalWays() {
+                       return originalWays;
+               }
+
+               public void removeAllTagsDeep() {
+                       removeOriginalTags();
+                       removeAllTags();
+               }
+
+               public void removeOriginalTags() {
+                       for (Way w : getOriginalWays()) {
+                               if (w instanceof JoinedWay) {
+                                       ((JoinedWay) w).removeAllTagsDeep();
+                               } else {
+                                       w.removeAllTags();
+                               }
+                       }
+               }
+
+               @Override
+               public String toString() {
+                       StringBuilder sb = new StringBuilder(200);
+                       sb.append(getId());
+                       sb.append("[");
+                       sb.append(getPoints().size());
+                       sb.append("P : (");
+                       boolean first = true;
+                       for (Way w : getOriginalWays()) {
+                               if (first) {
+                                       first = false;
+                               } else {
+                                       sb.append(",");
+                               }
+                               sb.append(w.getId());
+                               sb.append("[");
+                               sb.append(w.getPoints().size());
+                               sb.append("P]");
+                       }
+                       sb.append(")");
+                       return sb.toString();
+               }
+
+       }
+
+       private static class RingStatus {
+               boolean outer;
+               int index;
+               JoinedWay ring;
+
+               public RingStatus(boolean outer, int index, JoinedWay ring) {
+                       super();
+                       this.outer = outer;
+                       this.index = index;
+                       this.ring = ring;
+               }
        }
 }
Index: src/uk/me/parabola/mkgmap/reader/osm/FakeIdGenerator.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/FakeIdGenerator.java   (revision 0)
+++ src/uk/me/parabola/mkgmap/reader/osm/FakeIdGenerator.java   (revision 0)
@@ -0,0 +1,24 @@
+package uk.me.parabola.mkgmap.reader.osm;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+public class FakeIdGenerator {
+
+       public static final long START_ID = 1L << 62;
+       
+       private static final AtomicLong fakeId = new AtomicLong(START_ID);
+
+       /**
+        * Retrieves a unique id that can be used to fake OSM ids.
+        * 
+        * @return a unique id
+        */
+       public static long makeFakeId() {
+               return fakeId.incrementAndGet();
+       }
+
+       public static boolean isFakeId(long id) {
+               return id >= START_ID;
+       }
+       
+}
Index: src/uk/me/parabola/log/Logger.java
===================================================================
--- src/uk/me/parabola/log/Logger.java  (revision 1484)
+++ src/uk/me/parabola/log/Logger.java  (working copy)
@@ -118,6 +118,10 @@
                l.setLevel(Level.WARNING);
        }
 
+       public boolean isLoggable(Level level) {
+               return log.isLoggable(level);
+       }
+
        public boolean isDebugEnabled() {
                return log.isLoggable(Level.FINE);
        }
@@ -191,6 +195,16 @@
                log.log(Level.SEVERE, o.toString(), e);
        }
 
+       public void log(Level level, Object o) {
+               if (log.isLoggable(level))
+                       log.log(level, o.toString());
+       }
+
+       public void log(Level level, Object ... olist) {
+               if (log.isLoggable(Level.INFO))
+                       arrayFormat(level, olist);
+       }
+       
        /**
         * Format the list of arguments by appending them to one string, 
keeping a
         * space between them.
_______________________________________________
mkgmap-dev mailing list
mkgmap-dev@lists.mkgmap.org.uk
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev

Reply via email to