The patch enables the multipolygon code to handle overlapping lines of polygons.

Please test it and give feedback to me.

WanMil
Index: src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java      
(revision 1563)
+++ src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java      
(working copy)
@@ -38,11 +38,19 @@
        private final Map<Long, String> roleMap = new HashMap<Long, String>();
 
        private ArrayList<BitSet> containsMatrix;
-       private ArrayList<JoinedWay> rings;
-       private Set<JoinedWay> intersectingRings;
+       private ArrayList<JoinedWay> polygons;
+       private Set<JoinedWay> intersectingPolygons;
 
        private final uk.me.parabola.imgfmt.app.Area bbox;
-
+       
+       /** 
+        * A point that has a lower or equal squared distance from 
+        * a line is treated as if it lies one the line.<br/>
+        * 1.0d is very exact. 2.0d covers rounding problems when converting
+        * OSM locations to mkgmap internal format. A larger value 
+        * is more tolerant against imprecise OSM data.
+        */
+       private final double OVERLAP_TOLERANCE_DISTANCE = 2.0d;
        
        /**
         * if one of these tags are contained in the multipolygon then the outer
@@ -397,6 +405,16 @@
                                        break;
                                }
                        }
+                       
+                       if (remove) {
+                               // check if the polygon contains the complete 
bounding box
+                               Rectangle bboxRect = new 
Rectangle(bbox.getMinLong(), bbox
+                                               .getMinLat(), bbox.getMaxLong() 
- bbox.getMinLong(),
+                                               bbox.getMaxLat() - 
bbox.getMinLat());
+                               if (w.getBounds().contains(bboxRect)) {
+                                       remove = false;
+                               }
+                       }
 
                        if (remove) {
                                if (log.isDebugEnabled()) {
@@ -407,72 +425,71 @@
                        }
                }
        }
-       
+
        /**
-        * Find all rings that are not contained by any other ring.
+        * Find all polygons that are not contained by any other polygon.
         * 
         * @param candidates
-        *            all rings that should be checked
+        *            all polygons that should be checked
         * @param roleFilter
         *            an additional filter
-        * @return all ring indexes that are not contained by any other ring
+        * @return all polygon indexes that are not contained by any other 
polygon
         */
-       private BitSet findOutmostRings(BitSet candidates, BitSet roleFilter) {
+       private BitSet findOutmostPolygons(BitSet candidates, BitSet 
roleFilter) {
                BitSet realCandidates = ((BitSet) candidates.clone());
                realCandidates.and(roleFilter);
-               return findOutmostRings(realCandidates);
+               return findOutmostPolygons(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>
+        * Finds all polygons that are not contained by any other polygons and 
that match
+        * to the given role. All polygons 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
+        *            indexes of the polygons that should be used
+        * @return the bits of all outmost polygons are set to true
         */
-       private BitSet findOutmostRings(BitSet candidates) {
-               BitSet outmostRings = new BitSet();
+       private BitSet findOutmostPolygons(BitSet candidates) {
+               BitSet outmostPolygons = 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
+                       // check if the candidateIndex polygon is not contained 
by any
+                       // other candidate polygon
                        boolean isOutmost = true;
                        for (int otherCandidateIndex = 
candidates.nextSetBit(0); otherCandidateIndex >= 0; otherCandidateIndex = 
candidates
                                        .nextSetBit(otherCandidateIndex + 1)) {
                                if (contains(otherCandidateIndex, 
candidateIndex)) {
-                                       // candidateIndex is not an outermost 
ring because it is
-                                       // contained by
-                                       // the otherCandidateIndex ring
+                                       // candidateIndex is not an outermost 
polygon because it is
+                                       // contained by the otherCandidateIndex 
polygon
                                        isOutmost = false;
                                        break;
                                }
                        }
                        if (isOutmost) {
-                               // this is an outmost ring
+                               // this is an outmost polygon
                                // put it to the bitset
-                               outmostRings.set(candidateIndex);
+                               outmostPolygons.set(candidateIndex);
                        }
                }
 
-               return outmostRings;
+               return outmostPolygons;
        }
 
-       private ArrayList<RingStatus> getRingStatus(BitSet outmostRings,
+       private ArrayList<PolygonStatus> getPolygonStatus(BitSet 
outmostPolygons,
                        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));
+               ArrayList<PolygonStatus> polygonStatusList = new 
ArrayList<PolygonStatus>();
+               for (int polyIndex = outmostPolygons.nextSetBit(0); polyIndex 
>= 0; polyIndex = outmostPolygons
+                               .nextSetBit(polyIndex + 1)) {
+                       // polyIndex is the polygon that is not contained by 
any other
+                       // polygon
+                       JoinedWay polygon = polygons.get(polyIndex);
+                       polygonStatusList.add(new PolygonStatus(outer, 
polyIndex, polygon));
                }
-               return ringStatusList;
+               return polygonStatusList;
        }
 
        /**
@@ -494,14 +511,14 @@
                                                "in multipolygon", getId());
                        }
                }
-
-               rings = joinWays(allWays);
-               closeWays(rings);
-               removeUnclosedWays(rings);
-               // now we have closed ways == rings only
+               
+               polygons = joinWays(allWays);
+               closeWays(polygons);
+               removeUnclosedWays(polygons);
+               // now we have closed ways == polygons only
 
-               // check if we have at least one ring left
-               if (rings.isEmpty()) {
+               // check if we have at least one polygon left
+               if (polygons.isEmpty()) {
                        // do nothing
                        log.warn("Multipolygon " + toBrowseURL()
                                        + " does not contain a closed 
polygon.");
@@ -509,132 +526,134 @@
                        return;
                }
 
-               removeWaysOutsideBbox(rings);
+               removeWaysOutsideBbox(polygons);
 
-               if (rings.isEmpty()) {
+               if (polygons.isEmpty()) {
                        // do nothing
                        log.info("Multipolygon " + toBrowseURL()
                                        + " is completely outside the bounding 
box. It is not processed.");
                        cleanup();
                        return;
                }
-               
-               // the intersectingRings marks all intersecting/overlapping 
rings
-               intersectingRings = new HashSet<JoinedWay>();
-               createContainsMatrix(rings);
+
+               // the intersectingPolygons marks all intersecting/overlapping 
polygons
+               intersectingPolygons = new HashSet<JoinedWay>();
+               createContainsMatrix(polygons);
 
-               BitSet unfinishedRings = new BitSet(rings.size());
-               unfinishedRings.set(0, rings.size());
+               BitSet unfinishedPolygons = new BitSet(polygons.size());
+               unfinishedPolygons.set(0, polygons.size());
 
-               // create bitsets which rings belong to the outer and to the 
inner role
-               BitSet innerRings = new BitSet();
-               BitSet outerRings = new BitSet();
+               // create bitsets which polygons belong to the outer and to the 
inner role
+               BitSet innerPolygons = new BitSet();
+               BitSet outerPolygons = new BitSet();
                int wi = 0;
-               for (Way w : rings) {
+               for (Way w : polygons) {
                        String role = getRole(w);
                        if ("inner".equals(role)) {
-                               innerRings.set(wi);
+                               innerPolygons.set(wi);
                        } else if ("outer".equals(role)) {
-                               outerRings.set(wi);
+                               outerPolygons.set(wi);
                        } else {
                                // unknown role => it could be both
-                               innerRings.set(wi);
-                               outerRings.set(wi);
+                               innerPolygons.set(wi);
+                               outerPolygons.set(wi);
                        }
                        wi++;
                }
 
-               if (outerRings.isEmpty()) {
-                       log.warn("Multipolygon",toBrowseURL(),"does not contain 
any way tagged with role=outer.");
+               if (outerPolygons.isEmpty()) {
+                       log.warn("Multipolygon", toBrowseURL(),
+                               "does not contain any way tagged with 
role=outer.");
                        cleanup();
                        return;
                }
-               
-               Queue<RingStatus> ringWorkingQueue = new 
LinkedBlockingQueue<RingStatus>();
 
-               BitSet outmostRings = findOutmostRings(unfinishedRings, 
outerRings);
-               if (outmostRings.isEmpty()) {
-                       // WanMil: do not process these rings
-                       // this would probably cause wrong mps. Issue a warning 
later in the code
-                       
-//                     // 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));
+               Queue<PolygonStatus> polygonWorkingQueue = new 
LinkedBlockingQueue<PolygonStatus>();
+
+               BitSet outmostPolygons = 
findOutmostPolygons(unfinishedPolygons, outerPolygons);
+               if (outmostPolygons.isEmpty()) {
+                       // WanMil: do not process these polygons
+                       // this would probably cause wrong mps. Issue a warning 
later in the
+                       // code
+
+                       // // there's no outmost outer polygon
+                       // // maybe this is a tile problem
+                       // // try to continue with the inner polygons
+                       // outmostPolygons = 
findOutmostPolygons(unfinishedPolygons, innerPolygons);
+                       // 
polygonWorkingQueue.addAll(getPolygonStatus(outmostPolygons, false));
                } else {
-                       ringWorkingQueue.addAll(getRingStatus(outmostRings, 
true));
+                       
polygonWorkingQueue.addAll(getPolygonStatus(outmostPolygons, true));
                }
 
-               while (ringWorkingQueue.isEmpty() == false) {
+               while (polygonWorkingQueue.isEmpty() == false) {
 
-                       // the ring is not contained by any other unfinished 
ring
-                       RingStatus currentRing = ringWorkingQueue.poll();
+                       // the polygon is not contained by any other unfinished 
polygon
+                       PolygonStatus currentPolygon = 
polygonWorkingQueue.poll();
 
                        // QA: check that all ways carry the role "outer/inner" 
and
                        // issue warnings
-                       checkRoles(currentRing.ring.getOriginalWays(),
-                                       (currentRing.outer ? "outer" : 
"inner"));
+                       checkRoles(currentPolygon.polygon.getOriginalWays(),
+                               (currentPolygon.outer ? "outer" : "inner"));
 
-                       // this ring is now processed and should not be used by 
any
+                       // this polygon is now processed and should not be used 
by any
                        // further step
-                       unfinishedRings.clear(currentRing.index);
+                       unfinishedPolygons.clear(currentPolygon.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
+                       BitSet polygonContains = new BitSet();
+                       
polygonContains.or(containsMatrix.get(currentPolygon.index));
+                       // use only polygon that are contained by the polygon
+                       polygonContains.and(unfinishedPolygons);
+                       // polygonContains is the intersection of the 
unfinished and
+                       // the contained polygons
 
                        // 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));
+                       // these are all polygons that are in the main polygon
+                       // and that are not contained by any other polygon
+                       BitSet holeIndexes = 
findOutmostPolygons(polygonContains,
+                               (currentPolygon.outer ? innerPolygons : 
outerPolygons));
 
-                       ArrayList<RingStatus> holes = getRingStatus(holeIndexes,
-                                       !currentRing.outer);
+                       ArrayList<PolygonStatus> holes = 
getPolygonStatus(holeIndexes,
+                               !currentPolygon.outer);
 
-                       // these rings must all be checked for inner rings
-                       ringWorkingQueue.addAll(holes);
+                       // these polygons must all be checked for inner polygons
+                       polygonWorkingQueue.addAll(holes);
 
-                       // check if the ring has tags and therefore should be 
processed
-                       boolean processRing = currentRing.outer
-                                       || hasPolygonTags(currentRing.ring);
+                       // check if the polygon has tags and therefore should 
be processed
+                       boolean processPolygon = currentPolygon.outer
+                                       || 
hasPolygonTags(currentPolygon.polygon);
 
-                       if (processRing) {
+                       if (processPolygon) {
 
                                List<Way> innerWays = new 
ArrayList<Way>(holes.size());
-                               for (RingStatus holeRingStatus : holes) {
-                                       innerWays.add(holeRingStatus.ring);
+                               for (PolygonStatus polygonHoleStatus : holes) {
+                                       
innerWays.add(polygonHoleStatus.polygon);
                                }
 
-                               List<Way> singularOuterPolygons = 
cutOutInnerRings(
-                                               currentRing.ring, innerWays);
+                               List<Way> singularOuterPolygons = 
cutOutInnerPolygons(
+                                       currentPolygon.polygon, innerWays);
 
-                               if (currentRing.ring.getOriginalWays().size() 
== 1) {
+                               if 
(currentPolygon.polygon.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();
+                                       
currentPolygon.polygon.removeAllTagsDeep();
                                } else {
                                        // remove all polygons tags from the 
original ways
                                        // sometimes the ways seem to be 
autoclosed later on
                                        // in mkgmap
-                                       for (Way w : 
currentRing.ring.getOriginalWays()) {
+                                       for (Way w : 
currentPolygon.polygon.getOriginalWays()) {
                                                for (String polygonTag : 
polygonTags) {
                                                        w.deleteTag(polygonTag);
                                                }
                                        }
                                }
 
-                               boolean useRelationTags = currentRing.outer
+                               boolean useRelationTags = currentPolygon.outer
                                                && hasPolygonTags(this);
                                if (useRelationTags) {
                                        // the multipolygon contains tags that 
overwhelm the
-                                       // tags of the outer ring
+                                       // tags of the outer polygon
                                        for (Way p : singularOuterPolygons) {
                                                p.copyTags(this);
                                        }
@@ -648,20 +667,20 @@
                        }
                }
 
-               if (log.isLoggable(Level.WARNING) && unfinishedRings.isEmpty() 
== false) {
-                       log.warn("Multipolygon", toBrowseURL(),"contains 
errors.");
-                       
-                       runIntersectionCheck(unfinishedRings);
-                       runWrongInnerRingCheck(unfinishedRings, innerRings);
-                       
+               if (log.isLoggable(Level.WARNING) && 
unfinishedPolygons.isEmpty() == false) {
+                       log.warn("Multipolygon", toBrowseURL(), "contains 
errors.");
+
+                       runIntersectionCheck(unfinishedPolygons);
+                       runWrongInnerPolygonCheck(unfinishedPolygons, 
innerPolygons);
+
                        // we have at least one ring that could not be processed
                        // Probably we have intersecting or overlapping polygons
                        // one possible reason is if the relation overlaps the 
tile
                        // bounds
                        // => issue a warning
-                       List<JoinedWay> lostWays = 
getWaysFromRinglist(unfinishedRings);
+                       List<JoinedWay> lostWays = 
getWaysFromPolygonList(unfinishedPolygons);
                        for (JoinedWay w : lostWays) {
-                               log.warn("Polygon",w.getId(),"is not processed 
due to an unknown reason.");
+                               log.warn("Polygon", w, "is not processed due to 
an unknown reason.");
                                logWayURLs(Level.WARNING, "-", w);
                        }
                }
@@ -671,16 +690,16 @@
 
        
        private void runIntersectionCheck(BitSet unfinishedRings) {
-               if (intersectingRings.isEmpty()) {
+               if (intersectingPolygons.isEmpty()) {
                        // nothing to do
                        return;
                }
 
-               log.warn("Some polygons are intersecting or overlapping. This 
is not yet supported.");
+               log.warn("Some polygons are intersecting. This is not allowed 
in multipolygons.");
 
                boolean oneOufOfBbox = false;
-               for (JoinedWay polygon : intersectingRings) {
-                       int pi = rings.indexOf(polygon);
+               for (JoinedWay polygon : intersectingPolygons) {
+                       int pi = polygons.indexOf(polygon);
                        unfinishedRings.clear(pi);
 
                        boolean outOfBbox = false;
@@ -698,95 +717,94 @@
                        log.warn("Some of these intersections/overlaps may be 
caused by incomplete data on bounding box edges (*).");
                }
        }
-       
-       private void runWrongInnerRingCheck(BitSet unfinishedRings, BitSet 
innerRings) {
+
+       private void runWrongInnerPolygonCheck(BitSet unfinishedPolygons,
+                       BitSet innerPolygons) {
                // find all unfinished inner rings that are not contained by any
-               BitSet wrongInnerRings = findOutmostRings(unfinishedRings, 
innerRings);
+               BitSet wrongInnerPolygons = 
findOutmostPolygons(unfinishedPolygons, innerPolygons);
                if (log.isDebugEnabled()) {
-                       log.debug("unfinished", unfinishedRings);
-                       log.debug("inner", innerRings);
+                       log.debug("unfinished", unfinishedPolygons);
+                       log.debug("inner", innerPolygons);
                        // other ring
-                       log.debug("wrong", wrongInnerRings);
+                       log.debug("wrong", wrongInnerPolygons);
                }
-               if (wrongInnerRings.isEmpty()==false) {
+               if (wrongInnerPolygons.isEmpty() == false) {
                        // we have an inner ring that is not contained by any 
outer ring
                        // check if
-                       for (int wiIndex = wrongInnerRings.nextSetBit(0); 
wiIndex >= 0; wiIndex = wrongInnerRings
+                       for (int wiIndex = wrongInnerPolygons.nextSetBit(0); 
wiIndex >= 0; wiIndex = wrongInnerPolygons
                                        .nextSetBit(wiIndex + 1)) {
-                               BitSet containedRings = new BitSet();
-                               containedRings.or(unfinishedRings);
-                               containedRings.and(containsMatrix.get(wiIndex));
+                               BitSet containedPolygons = new BitSet();
+                               containedPolygons.or(unfinishedPolygons);
+                               
containedPolygons.and(containsMatrix.get(wiIndex));
 
-                               Way innerWay = rings.get(wiIndex);
-                               if (containedRings.isEmpty()) {
+                               Way innerWay = polygons.get(wiIndex);
+                               if (containedPolygons.isEmpty()) {
                                        log.warn("Polygon",     innerWay, 
"carries role", getRole(innerWay),
                                                "but is not inside any outer 
polygon. Potentially it does not belong to this multipolygon.");
                                } else {
                                        log.warn("Polygon",     innerWay, 
"carries role", getRole(innerWay),
-                                           "but is not inside any outer 
polygon. Potentially the roles are interchanged with the following",
-                                           
(containedRings.cardinality()>1?"ways":"way"),".");
-                                       
-                                       for (int wrIndex = 
containedRings.nextSetBit(0); wrIndex >= 0; 
-                                               wrIndex = 
containedRings.nextSetBit(wrIndex+1)) {
-                                               logWayURLs(Level.WARNING, "-", 
rings.get(wrIndex));
-                                               unfinishedRings.set(wrIndex);
-                                               wrongInnerRings.set(wrIndex);
+                                               "but is not inside any outer 
polygon. Potentially the roles are interchanged with the following",
+                                               
(containedPolygons.cardinality() > 1 ? "ways" : "way"), ".");
+
+                                       for (int wrIndex = 
containedPolygons.nextSetBit(0); wrIndex >= 0; wrIndex = containedPolygons
+                                                       .nextSetBit(wrIndex + 
1)) {
+                                               logWayURLs(Level.WARNING, "-", 
polygons.get(wrIndex));
+                                               unfinishedPolygons.set(wrIndex);
+                                               wrongInnerPolygons.set(wrIndex);
                                        }
                                }
-                               
-                               unfinishedRings.clear(wiIndex);
-                               wrongInnerRings.clear(wiIndex);
+
+                               unfinishedPolygons.clear(wiIndex);
+                               wrongInnerPolygons.clear(wiIndex);
                        }
                }
-               
        }
-       
+
        private void cleanup() {
                roleMap.clear();
                containsMatrix = null;
-               rings = null;
+               polygons = null;
        }
 
        /**
-        * Cut out all inner rings from the outer ring. This will divide the 
outer
-        * ring in several polygons.
+        * Cut out all inner polygons from the outer polygon. This will divide 
the outer
+        * polygon 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
+        * @param outerPolygon
+        *            the outer polygon
+        * @param innerPolygons
+        *            a list of inner polygons
+        * @return a list of polygons that make the outer polygon cut by the 
inner
+        *         polygons
         */
-       private List<Way> cutOutInnerRings(Way outerRing, List<Way> innerRings) 
{
+       private List<Way> cutOutInnerPolygons(Way outerPolygon, List<Way> 
innerPolygons) {
                // 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
+               // of the outerPolygon
                List<Area> outerAreas = new ArrayList<Area>();
 
-               // 1st create an Area object of the outerRing and put it to the 
list
-               List<Area> oa = createAreas(outerRing);
+               // 1st create an Area object of the outerPolygon and put it to 
the list
+               List<Area> oa = createAreas(outerPolygon);
 
                // the polygons will be later clipped in the style converter
                // so it is not necessary to clip it here
                outerAreas.addAll(oa);
 
                List<Area> innerAreas = new ArrayList<Area>();
-               for (Way innerRing : innerRings) {
-                       innerAreas.addAll(createAreas(innerRing));
+               for (Way innerPolygon : innerPolygons) {
+                       innerAreas.addAll(createAreas(innerPolygon));
                }
 
-               // go through all innerRings (holes) and cut them from the 
outerRing
+               // go through all innerPolygons (holes) and cut them from the 
outerPolygon
                for (Area innerArea : innerAreas) {
 
                        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().intersects(
-                                               innerArea.getBounds()) == 
false) {
+                               if 
(outerArea.getBounds().intersects(innerArea.getBounds()) == false) {
                                        outerAfterThisStep.add(outerArea);
                                        continue;
                                }
@@ -843,16 +861,16 @@
                }
 
                // convert the java.awt.geom.Area back to the mkgmap way
-               List<Way> cutOuterRing = new ArrayList<Way>(outerAreas.size());
+               List<Way> cutOuterPolygon = new 
ArrayList<Way>(outerAreas.size());
                for (Area area : outerAreas) {
                        Way w = singularAreaToWay(area, 
FakeIdGenerator.makeFakeId());
                        if (w != null) {
-                               w.copyTags(outerRing);
-                               cutOuterRing.add(w);
+                               w.copyTags(outerPolygon);
+                               cutOuterPolygon.add(w);
                        }
                }
 
-               return cutOuterRing;
+               return cutOuterPolygon;
        }
 
        /**
@@ -945,9 +963,16 @@
                Area area = new Area(createPolygon(w.getPoints()));
                List<Area> areaList = areaToSingularAreas(area);
                if (areaList.size() > 1) {
-                       log.warn("Polygon", w.getId(), "intersects itself.");
-                       log.warn("The polygon is composed of");
-                       logWayURLs(Level.WARNING, "-", w);
+                       if (bbox.allInsideBoundary(w.getPoints())) {
+                               log.warn("Polygon", w.getId(), "intersects 
itself. It is splitted into", areaList.size(), "polygons.");
+                               log.warn("The polygon is composed of");
+                               logWayURLs(Level.WARNING, "-", w);
+                       } else {
+                               log.info("Polygon", w.getId(),
+                                       "intersects itself and the tile bounds. 
Maybe the polygon is not completely contained in the tile.");
+                               log.info("The polygon is composed of");
+                               logWayURLs(Level.INFO, "-", w);
+                       }
                }
                return areaList;
        }
@@ -1028,7 +1053,7 @@
                // 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) {
+                       if (checkRole.equals(realRole) == false && 
"".equals(realRole) == false) {
                                if (tempWay instanceof JoinedWay) {
                                        log.warn("Polygon composed of ways", 
((JoinedWay) tempWay).getOriginalIds(), "carries role", realRole,
                                                "but should carry role", 
checkRole);
@@ -1071,7 +1096,7 @@
                }
 
                for (int rowIndex = 0; rowIndex < polygonList.size(); 
rowIndex++) {
-                       JoinedWay potentialOuterRing = 
polygonList.get(rowIndex);
+                       JoinedWay potentialOuterPolygon = 
polygonList.get(rowIndex);
                        BitSet containsColumns = containsMatrix.get(rowIndex);
                        BitSet finishedCol = finishedMatrix.get(rowIndex);
 
@@ -1085,28 +1110,27 @@
 
                                JoinedWay innerPolygon = 
polygonList.get(colIndex);
 
-                               if (potentialOuterRing.getBounds().intersects(
-                                               innerPolygon.getBounds()) == 
false) {
+                               if 
(potentialOuterPolygon.getBounds().intersects(
+                                       innerPolygon.getBounds()) == false) {
                                        // both polygons do not intersect
                                        // we can flag both matrix elements as 
finished
                                        
finishedMatrix.get(colIndex).set(rowIndex);
                                        
finishedMatrix.get(rowIndex).set(colIndex);
                                } else {
-                                       boolean contains = 
contains(potentialOuterRing,
-                                                       innerPolygon);
+                                       boolean contains = 
contains(potentialOuterPolygon,
+                                               innerPolygon);
 
                                        if (contains) {
                                                containsColumns.set(colIndex);
 
-                                               // we also know that the inner 
ring does not contain the
-                                               // outer ring
+                                               // we also know that the inner 
polygon does not contain the
+                                               // outer polygon
                                                // 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
+                                               // additionally we know that 
the outer polygon contains all
+                                               // polygons that are contained 
by the inner polygon
                                                
containsColumns.or(containsMatrix.get(colIndex));
                                                finishedCol.or(containsColumns);
                                        }
@@ -1119,8 +1143,8 @@
                if (log.isDebugEnabled()) {
                        long t2 = System.currentTimeMillis();
                        log.debug("createMatrix for", polygonList.size(), 
"polygons took",
-                                       (t2 - t1), "ms");
-                       
+                               (t2 - t1), "ms");
+
                        log.debug("Containsmatrix");
                        for (BitSet b : containsMatrix) {
                                log.debug(b);
@@ -1129,41 +1153,102 @@
        }
 
        /**
-        * Checks if the ring with ringIndex1 contains the ring with ringIndex2.
+        * Checks if the polygon with polygonIndex1 contains the ring with 
polygonIndex2.
         * 
-        * @return true if ring(ringIndex1) contains ring(ringIndex2)
+        * @return true if polygon(polygonIndex1) contains 
polygon(polygonIndex2)
         */
-       private boolean contains(int ringIndex1, int ringIndex2) {
-               return containsMatrix.get(ringIndex1).get(ringIndex2);
+       private boolean contains(int polygonIndex1, int polygonIndex2) {
+               return containsMatrix.get(polygonIndex1).get(polygonIndex2);
        }
 
        /**
-        * Checks if ring1 contains ring2.
+        * Checks if polygon1 contains polygon2.
         * 
-        * @param ring1
+        * @param polygon1
         *            a closed way
-        * @param ring2 a 2nd closed way
-        * @return true if ring1 contains ring2
+        * @param polygon2
+        *            a 2nd closed way
+        * @return true if polygon1 contains polygon2
         */
-       private boolean contains(JoinedWay ring1, JoinedWay ring2) {
-               // TODO this is a simple algorithm
-               // might be improved
-
-               if (ring1.isClosed() == false) {
+       private boolean contains(JoinedWay polygon1, JoinedWay polygon2) {
+               if (polygon1.isClosed() == false) {
+                       return false;
+               }
+               // check if the bounds of polygon2 are completely 
inside/enclosed the bounds
+               // of polygon1
+               if (polygon1.getBounds().contains(polygon2.getBounds()) == 
false) {
                        return false;
                }
 
-               Polygon p = createPolygon(ring1.getPoints());
+               Polygon p = createPolygon(polygon1.getPoints());
+               // check first if one point of polygon2 is in polygon1
 
-               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;
+               // ignore intersections outside the bounding box
+               // so it is necessary to check if there is at least one
+               // point of polygon2 in polygon1 ignoring all points outside 
the bounding box
+               boolean onePointContained = false;
+               boolean allOnLine = true;
+               for (Coord px : polygon2.getPoints()) {
+                       if (p.contains(px.getLongitude(), px.getLatitude())) {
+                               // there's one point that is in polygon1 and in 
the bounding
+                               // box => polygon1 may contain polygon2
+                               onePointContained = true;
+                               if (locatedOnLine(px, polygon1.getPoints()) == 
false) {
+                                       allOnLine = false;
+                                       break;
+                               }
+                       } else if (bbox.contains(px)) {
+                               // we have to check if the point is on one line 
of the polygon1
+                               
+                               if (locatedOnLine(px, polygon1.getPoints()) == 
false) {
+                                       // there's one point that is not in 
polygon1 but inside the
+                                       // bounding box => polygon1 does not 
contain polygon2
+                                       allOnLine = false;
+                                       return false;
+                               } 
+                       }
+               }
+               
+               if (allOnLine) {
+                       onePointContained = false;
+                       // all points of polygon2 lie on lines of polygon1
+                       // => the middle of each line polygon must NOT lie 
outside polygon1
+                       ArrayList<Coord> middlePoints2 = new 
ArrayList<Coord>(polygon2.getPoints().size());
+                       Coord p1 = null;
+                       for (Coord p2 : polygon2.getPoints()) {
+                               if (p1 != null) {
+                                       int mLat = 
p1.getLatitude()+(int)Math.round((p2.getLatitude()-p1.getLatitude())/2d);
+                                       int mLong = 
p1.getLongitude()+(int)Math.round((p2.getLongitude()-p1.getLongitude())/2d);
+                                       Coord pm = new Coord(mLat, mLong);
+                                       middlePoints2.add(pm);
+                               }
+                               p1 = p2;
+                       }
+                       
+                       for (Coord px : middlePoints2) {
+                               if (p.contains(px.getLongitude(), 
px.getLatitude())) {
+                                       // there's one point that is in 
polygon1 and in the bounding
+                                       // box => polygon1 may contain polygon2
+                                       onePointContained = true;
+                                       break;
+                               } else if (bbox.contains(px)) {
+                                       // we have to check if the point is on 
one line of the polygon1
+                                       
+                                       if (locatedOnLine(px, 
polygon1.getPoints()) == false) {
+                                               // there's one point that is 
not in polygon1 but inside the
+                                               // bounding box => polygon1 
does not contain polygon2
+                                               return false;
+                                       } 
+                               }
+                       }                       
                }
-               // TODO check if p0 is on any of the lines of ring2
 
-               Iterator<Coord> it1 = ring1.getPoints().iterator();
+               if (onePointContained == false) {
+                       // no point of polygon2 is in polygon1 => polygon1 does 
not contain polygon2
+                       return false;
+               }
+               
+               Iterator<Coord> it1 = polygon1.getPoints().iterator();
                Coord p1_1 = it1.next();
                Coord p1_2 = null;
 
@@ -1171,7 +1256,7 @@
                        p1_2 = p1_1;
                        p1_1 = it1.next();
 
-                       if (ring2.linePossiblyIntersectsWay(p1_1, p1_2) == 
false) {
+                       if (polygon2.linePossiblyIntersectsWay(p1_1, p1_2) == 
false) {
                                // don't check it - this segment of the outer 
polygon
                                // definitely does not intersect the way
                                continue;
@@ -1183,7 +1268,7 @@
                        int latMax = Math.max(p1_1.getLatitude(), 
p1_2.getLatitude());
 
                        // check all lines of way1 and way2 for intersections
-                       Iterator<Coord> it2 = ring2.getPoints().iterator();
+                       Iterator<Coord> it2 = polygon2.getPoints().iterator();
                        Coord p2_1 = it2.next();
                        Coord p2_2 = null;
 
@@ -1227,28 +1312,32 @@
                                                || (prevLatField == 0 && 
prevLonField == 0);
 
                                boolean intersects = intersectionPossible
-                                               && 
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());
-
-                               // TODO check if one of the points only touches 
the other ring
-
+                                       && linesCutEachOther(p1_1, p1_2, p2_1, 
p2_2);
+                               
                                if (intersects) {
-                                       if ((ring1.isClosedArtificially() && 
it1.hasNext() == false)
-                                                       || 
(ring2.isClosedArtificially() && it2.hasNext() == false)) {
+                                       if ((polygon1.isClosedArtificially() && 
it1.hasNext() == false)
+                                                       || 
(polygon2.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.info("Polygon", ring1, "may 
contain polygon", ring2,
+                                               // one of the polygons is 
closed by this mp code and the
+                                               // closing segment causes the 
intersection
+                                               log.info("Polygon", polygon1, 
"may contain polygon", polygon2,
                                                        ". Ignoring artificial 
generated intersection.");
+                                       } else if ((bbox.contains(p1_1) == 
false)
+                                                       || (bbox.contains(p1_2) 
== false)
+                                                       || (bbox.contains(p2_1) 
== false)
+                                                       || (bbox.contains(p2_2) 
== false)) {
+                                               // at least one point is 
outside the bounding box
+                                               // we ignore the intersection 
because the ways may not
+                                               // be complete
+                                               // due to removals of the tile 
splitter or osmosis
+                                               log.info("Polygon", polygon1, 
"may contain polygon", polygon2,
+                                                       ". Ignoring because at 
least one point is outside the bounding box.");
                                        } else {
-                                               // store them in the 
intersection rings set
-                                               // the error message will be 
printed out in the end of 
+                                               // store them in the 
intersection polygons set
+                                               // the error message will be 
printed out in the end of
                                                // the mp handling
-                                               intersectingRings.add(ring1);
-                                               intersectingRings.add(ring2);
+                                               
intersectingPolygons.add(polygon1);
+                                               
intersectingPolygons.add(polygon2);
                                                return false;
                                        }
                                }
@@ -1259,26 +1348,123 @@
                }
 
                // don't have any intersection
-               // => ring1 contains ring2
+               // => polygon1 contains polygon2
                return true;
        }
 
-       private List<JoinedWay> getWaysFromRinglist(BitSet selection) {
+       /**
+        * Checks if the point p is located on one line of the given points.
+        * @param p a point
+        * @param points a list of points; all consecutive points are handled 
as lines
+        * @return true if p is located on one line given by points
+        */
+       private boolean locatedOnLine(Coord p, List<Coord> points) {
+               Coord cp1 = null;
+               for (Coord cp2 : points) {
+                       if (p.equals(cp2)) {
+                               return true;
+                       }
+
+                       try {
+                               if (cp1 == null) {
+                                       // first init
+                                       continue;
+                               }
+
+                               if (p.getLongitude() < 
Math.min(cp1.getLongitude(), cp2
+                                               .getLongitude())) {
+                                       continue;
+                               }
+                               if (p.getLongitude() > 
Math.max(cp1.getLongitude(), cp2
+                                               .getLongitude())) {
+                                       continue;
+                               }
+                               if (p.getLatitude() < 
Math.min(cp1.getLatitude(), cp2
+                                               .getLatitude())) {
+                                       continue;
+                               }
+                               if (p.getLatitude() > 
Math.max(cp1.getLatitude(), cp2
+                                               .getLatitude())) {
+                                       continue;
+                               }
+
+                               double dist = 
Line2D.ptSegDistSq(cp1.getLongitude(), cp1
+                                               .getLatitude(), 
cp2.getLongitude(), cp2.getLatitude(),
+                                       p.getLongitude(), p.getLatitude());
+
+                               if (dist <= OVERLAP_TOLERANCE_DISTANCE) {
+                                       log.debug("Point", p, "is located on 
line between", cp1, "and",
+                                               cp2, ". Distance:", dist);
+                                       return true;
+                               }
+                       } finally {
+                               cp1 = cp2;
+                       }
+               }
+               return false;
+       }
+       
+       /**
+        * Check if the line p1_1 to p1_2 cuts line p2_1 to p2_2 in two pieces 
and vice versa.
+        * This is a form of intersection check where it is allowed that one 
line ends on the
+        * other line or that the two lines overlap.
+        * @param p1_1 first point of line 1
+        * @param p1_2 second point of line 1
+        * @param p2_1 first point of line 2
+        * @param p2_2 second point of line 2
+        * @return true if both lines intersect somewhere in the middle of each 
other
+        */
+       private boolean linesCutEachOther(Coord p1_1, Coord p1_2, Coord p2_1,
+                       Coord p2_2) {
+               int width1 = p1_2.getLongitude() - p1_1.getLongitude();
+               int width2 = p2_2.getLongitude() - p2_1.getLongitude();
+
+               int height1 = p1_2.getLatitude() - p1_1.getLatitude();
+               int height2 = p2_2.getLatitude() - p2_1.getLatitude();
+
+               int denominator = ((height2 * width1) - (width2 * height1));
+               if (denominator == 0) {
+                       // the lines are parallel
+                       // they might overlap but this is ok for this test
+                       return false;
+               }
+               
+               int x1Mx3 = p1_1.getLongitude() - p2_1.getLongitude();
+               int y1My3 = p1_1.getLatitude() - p2_1.getLatitude();
+
+               double isx = (double)((width2 * y1My3) - (height2 * x1Mx3))
+                               / denominator;
+               if (isx < 0 || isx > 1) {
+                       return false;
+               }
+               
+               double isy = (double)((width1 * y1My3) - (height1 * x1Mx3))
+                               / denominator;
+
+               if (isy < 0 || isy > 1) {
+                       return false;
+               } 
+
+               return false;
+       }
+
+       private List<JoinedWay> getWaysFromPolygonList(BitSet selection) {
                if (selection.isEmpty()) {
                        return Collections.emptyList();
                }
-               List<JoinedWay> wayList = new 
ArrayList<JoinedWay>(selection.cardinality());
-               for (int i = selection.nextSetBit(0); i >= 0; i = 
selection.nextSetBit(i+1)) {
-                       wayList.add(rings.get(i));
+               List<JoinedWay> wayList = new ArrayList<JoinedWay>(selection
+                               .cardinality());
+               for (int i = selection.nextSetBit(0); i >= 0; i = 
selection.nextSetBit(i + 1)) {
+                       wayList.add(polygons.get(i));
                }
                return wayList;
        }
-       
+
        private void logWayURLs(Level level, String preMsg, Way way) {
                if (log.isLoggable(level)) {
                        if (way instanceof JoinedWay) {
                                if (((JoinedWay) 
way).getOriginalWays().isEmpty()) {
-                                       log.warn("Way",way,"does not contain 
any original ways");
+                                       log.warn("Way", way, "does not contain 
any original ways");
                                }
                                for (Way segment : ((JoinedWay) 
way).getOriginalWays()) {
                                        if (preMsg == null || preMsg.length() 
== 0) {
@@ -1442,7 +1628,7 @@
                public String toString() {
                        StringBuilder sb = new StringBuilder(200);
                        sb.append(getId());
-                       sb.append("[");
+                       sb.append("(");
                        sb.append(getPoints().size());
                        sb.append("P : (");
                        boolean first = true;
@@ -1462,15 +1648,15 @@
                }
        }
 
-       private static class RingStatus {
+       private static class PolygonStatus {
                boolean outer;
                int index;
-               JoinedWay ring;
+               JoinedWay polygon;
 
-               public RingStatus(boolean outer, int index, JoinedWay ring) {
+               public PolygonStatus(boolean outer, int index, JoinedWay 
polygon) {
                        this.outer = outer;
                        this.index = index;
-                       this.ring = ring;
+                       this.polygon = polygon;
                }
        }
 }
_______________________________________________
mkgmap-dev mailing list
mkgmap-dev@lists.mkgmap.org.uk
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev

Reply via email to