Version 1599 was commited by steve on 2010-03-08 09:51:17 +0000 (Mon, 08 Mar
2010)
Revert 1598 as it caused flooding.
The patch fixes the flodding problem of reverted commit 1598.
Longitude/latitude were exchanged and so the land polygon was completely
removed because the mp code thought it would be located completely
outside the bounding box.
I have tested it with GB. The tile north of Edinborough does not have
any sea, but this was caused by a wrong multiypolygon created by the
generate-sea code. I think that the incomplete island North Rona might
be the problem, because it was the only outer polygon of the
generate-sea-mp. But I haven't taken a deep look into the generate-sea
code...
The rest of GB is fine. So (cross your fingers ;-) this patch should work.
WanMil
Index: src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java
(revision 1599)
+++ src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java
(working copy)
@@ -7,15 +7,19 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
+import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
+import java.util.TreeSet;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
@@ -41,7 +45,8 @@
private Set<JoinedWay> intersectingPolygons;
private final uk.me.parabola.imgfmt.app.Area bbox;
-
+ private Area bboxArea;
+
/**
* A point that has a lower or equal squared distance from
* a line is treated as if it lies one the line.<br/>
@@ -423,10 +428,7 @@
if (remove) {
// check if the polygon contains the complete
bounding box
- Rectangle bboxRect = new
Rectangle(bbox.getMinLat(), bbox
- .getMinLong(), bbox.getMaxLat()
- bbox.getMinLat(),
- bbox.getMaxLong() -
bbox.getMinLong());
- if (w.getBounds().contains(bboxRect)) {
+ if
(w.getBounds().contains(bboxArea.getBounds())) {
remove = false;
}
}
@@ -527,6 +529,11 @@
}
}
+ // create an Area for the bbox to clip the polygons
+ bboxArea = new Area(new Rectangle(bbox.getMinLong(), bbox
+ .getMinLat(), bbox.getMaxLong() - bbox.getMinLong(),
+ bbox.getMaxLat() - bbox.getMinLat()));
+
polygons = joinWays(allWays);
closeWays(polygons);
removeUnclosedWays(polygons);
@@ -639,15 +646,20 @@
||
hasPolygonTags(currentPolygon.polygon);
if (processPolygon) {
+ List<Way> singularOuterPolygons;
+ if (holes.isEmpty()) {
+ singularOuterPolygons = Collections
+ .singletonList((Way)
new JoinedWay(currentPolygon.polygon));
+ } else {
+ List<Way> innerWays = new
ArrayList<Way>(holes.size());
+ for (PolygonStatus polygonHoleStatus :
holes) {
+
innerWays.add(polygonHoleStatus.polygon);
+ }
- List<Way> innerWays = new
ArrayList<Way>(holes.size());
- for (PolygonStatus polygonHoleStatus : holes) {
-
innerWays.add(polygonHoleStatus.polygon);
+ singularOuterPolygons =
cutOutInnerPolygons(currentPolygon.polygon,
+ innerWays);
}
-
- List<Way> singularOuterPolygons =
cutOutInnerPolygons(
- currentPolygon.polygon, innerWays);
-
+
if
(currentPolygon.polygon.getOriginalWays().size() == 1) {
// the original way was a closed
polygon which
// has been replaced by the new cutted
polygon
@@ -689,7 +701,7 @@
runIntersectionCheck(unfinishedPolygons);
runWrongInnerPolygonCheck(unfinishedPolygons,
innerPolygons);
- // we have at least one ring that could not be processed
+ // we have at least one polygon that could not be
processed
// Probably we have intersecting or overlapping polygons
// one possible reason is if the relation overlaps the
tile
// bounds
@@ -704,8 +716,7 @@
cleanup();
}
-
- private void runIntersectionCheck(BitSet unfinishedRings) {
+ private void runIntersectionCheck(BitSet unfinishedPolys) {
if (intersectingPolygons.isEmpty()) {
// nothing to do
return;
@@ -716,7 +727,7 @@
boolean oneOufOfBbox = false;
for (JoinedWay polygon : intersectingPolygons) {
int pi = polygons.indexOf(polygon);
- unfinishedRings.clear(pi);
+ unfinishedPolys.clear(pi);
boolean outOfBbox = false;
for (Coord c : polygon.getPoints()) {
@@ -736,7 +747,7 @@
private void runWrongInnerPolygonCheck(BitSet unfinishedPolygons,
BitSet innerPolygons) {
- // find all unfinished inner rings that are not contained by any
+ // find all unfinished inner polygons that are not contained by
any
BitSet wrongInnerPolygons =
findOutmostPolygons(unfinishedPolygons, innerPolygons);
if (log.isDebugEnabled()) {
log.debug("unfinished", unfinishedPolygons);
@@ -780,6 +791,49 @@
roleMap.clear();
containsMatrix = null;
polygons = null;
+ bboxArea = null;
+ intersectingPolygons = null;
+ }
+
+ private CutPoint calcNextCutPoint(AreaCutData areaData) {
+ if (areaData.innerAreas == null ||
areaData.innerAreas.isEmpty()) {
+ return null;
+ }
+
+ if (areaData.innerAreas.size() == 1) {
+ // make it short if there is only one inner area
+ Rectangle outerBounds = areaData.outerArea.getBounds();
+ CoordinateAxis axis = (outerBounds.width <
outerBounds.height ? CoordinateAxis.LONGITUDE : CoordinateAxis.LATITUDE);
+ CutPoint oneCutPoint = new CutPoint(axis);
+ oneCutPoint.addArea(areaData.innerAreas.get(0));
+ return oneCutPoint;
+ }
+
+ ArrayList<Area> innerStart = new ArrayList<Area>(
+ areaData.innerAreas);
+
+ ArrayList<CutPoint> bestCutPoints = new
ArrayList<CutPoint>(CoordinateAxis.values().length);
+
+ for (CoordinateAxis axis : CoordinateAxis.values()) {
+ CutPoint bestCutPoint = new CutPoint(axis);
+ CutPoint currentCutPoint = new CutPoint(axis);
+
+ Collections.sort(innerStart, (axis ==
CoordinateAxis.LONGITUDE ? COMP_LONG_START: COMP_LAT_START));
+
+ Iterator<Area> startIter = innerStart.iterator();
+ while (startIter.hasNext()) {
+ Area nextStart = startIter.next();
+ currentCutPoint.addArea(nextStart);
+
+ if (currentCutPoint.compareTo(bestCutPoint) >
0) {
+ bestCutPoint =
currentCutPoint.duplicate();
+ }
+ }
+ bestCutPoints.add(bestCutPoint);
+ }
+
+ return Collections.max(bestCutPoints);
+
}
/**
@@ -794,112 +848,166 @@
* polygons
*/
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
+ if (innerPolygons.isEmpty()) {
+ Way outerWay = new JoinedWay(outerPolygon);
+ if (log.isDebugEnabled()) {
+ log.debug("Way", outerPolygon.getId(),
"splitted to way", outerWay.getId());
+ }
+ return Collections.singletonList(outerWay);
+ }
+
+ // use the java.awt.geom.Area class because it's a quick
+ // implementation of what's needed
// this list contains all non overlapping and singular areas
// of the outerPolygon
- List<Area> outerAreas = new ArrayList<Area>();
-
- // 1st create an Area object of the outerPolygon and put it to
the list
- List<Area> oa = createAreas(outerPolygon);
-
- Area bboxArea = new Area(new Rectangle(bbox.getMinLong(), bbox
- .getMinLat(), bbox.getMaxLong() - bbox.getMinLong(),
- bbox.getMaxLat() - bbox.getMinLat()));
+ Queue<AreaCutData> areasToCut = new LinkedList<AreaCutData>();
+ Collection<Area> finishedAreas = new
ArrayList<Area>(innerPolygons.size());
- // clip the bounding box
- for (Area outerArea : oa) {
- if (bboxArea.contains(outerArea.getBounds())) {
- // no clipping necessary
- outerAreas.add(outerArea);
- } else {
- // the area might intersect the bounding box
- // => clip it to the bounding box
- outerArea.intersect(bboxArea);
-
outerAreas.addAll(areaToSingularAreas(outerArea));
- }
- }
-
- List<Area> innerAreas = new ArrayList<Area>();
+ // create a list of Area objects from the outerPolygon (clipped
to the bounding box)
+ List<Area> outerAreas = createAreas(outerPolygon, true);
+
+ // create the inner areas
+ List<Area> innerAreas = new
ArrayList<Area>(innerPolygons.size()+2);
for (Way innerPolygon : innerPolygons) {
- innerAreas.addAll(createAreas(innerPolygon));
+ // don't need to clip to the bounding box because
+ // these polygons are just used to cut out holes
+ innerAreas.addAll(createAreas(innerPolygon, false));
}
- // go through all innerPolygons (holes) and cut them from the
outerPolygon
- for (Area innerArea : innerAreas) {
-
- List<Area> outerAfterThisStep = new ArrayList<Area>();
+ // initialize the cut data queue
+ if (innerAreas.isEmpty()) {
+ // this is a multipolygon without any inner areas
+ // nothing to cut
+ finishedAreas.addAll(outerAreas);
+ } else if (outerAreas.size() == 1) {
+ // there is one outer area only
+ // it is checked before that all inner areas are inside
this outer area
+ AreaCutData initialCutData = new AreaCutData();
+ initialCutData.outerArea = outerAreas.get(0);
+ initialCutData.innerAreas = innerAreas;
+ areasToCut.add(initialCutData);
+ } else {
+ // multiple outer areas
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) {
- outerAfterThisStep.add(outerArea);
- continue;
+ AreaCutData initialCutData = new AreaCutData();
+ initialCutData.outerArea = outerArea;
+ initialCutData.innerAreas = new
ArrayList<Area>(innerAreas
+ .size());
+ for (Area innerArea : innerAreas) {
+ if (outerArea.getBounds().intersects(
+ innerArea.getBounds())) {
+
initialCutData.innerAreas.add(innerArea);
+ }
}
-
- // 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);
+
+ if (initialCutData.innerAreas.isEmpty()) {
+ // this is either an error
+ // or the outer area has been cut into
pieces on the tile bounds
+ finishedAreas.add(outerArea);
} else {
- // 1st cut in two halfs in the middle
of the inner area
+ areasToCut.add(initialCutData);
+ }
+ }
+ }
- // Cut the bounding box into two
rectangles
- Rectangle r1;
- Rectangle r2;
+ while (areasToCut.isEmpty() == false) {
+ AreaCutData areaCutData = areasToCut.poll();
+ CutPoint cutPoint = calcNextCutPoint(areaCutData);
+
+ if (cutPoint == null) {
+ finishedAreas.add(areaCutData.outerArea);
+ continue;
+ }
+
+ assert cutPoint.getNumberOfAreas() > 0 : "Number of cut
areas == 0 in mp "+getId();
+
+ // cut out the holes
+ for (Area cutArea : cutPoint.getAreas()) {
+ areaCutData.outerArea.subtract(cutArea);
+ }
+
+ if (areaCutData.outerArea.isEmpty()) {
+ // this outer area space can be abandoned
+ continue;
+ }
+
+ // the inner areas of the cut point have been processed
+ // they are no longer needed
+ areaCutData.innerAreas.removeAll(cutPoint.getAreas());
- // 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);
- }
+ if (areaCutData.outerArea.isSingular()) {
+ // the area is singular
+ // => no further splits necessary
+ if (areaCutData.innerAreas.isEmpty()) {
+ // this area is finished and needs no
further cutting
+
finishedAreas.add(areaCutData.outerArea);
+ } else {
+ // readd this area to further processing
+ areasToCut.add(areaCutData);
+ }
+ } else {
+ // we need to cut the area into two halfs to
get singular areas
+ Rectangle r1 =
cutPoint.getCutRectangleForArea(areaCutData.outerArea, true);
+ Rectangle r2 =
cutPoint.getCutRectangleForArea(areaCutData.outerArea, false);
- // 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));
+ // 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 = areaCutData.outerArea;
+ Area a2 = (Area) a1.clone();
+ a1.intersect(new Area(r1));
+ a2.intersect(new Area(r2));
-
outerAfterThisStep.addAll(areaToSingularAreas(a1));
-
outerAfterThisStep.addAll(areaToSingularAreas(a2));
+ if (areaCutData.innerAreas.isEmpty()) {
+
finishedAreas.addAll(areaToSingularAreas(a1));
+
finishedAreas.addAll(areaToSingularAreas(a2));
+ } else {
+ ArrayList<Area> cuttedAreas = new
ArrayList<Area>();
+
cuttedAreas.addAll(areaToSingularAreas(a1));
+
cuttedAreas.addAll(areaToSingularAreas(a2));
+
+ for (Area nextOuterArea : cuttedAreas) {
+ ArrayList<Area> nextInnerAreas
= null;
+ // go through all remaining
inner areas and check if they
+ // must be further processed
with the nextOuterArea
+ for (Area nonProcessedInner :
areaCutData.innerAreas) {
+ if
(nextOuterArea.intersects(nonProcessedInner.getBounds2D())) {
+ if
(nextInnerAreas == null) {
+
nextInnerAreas = new ArrayList<Area>();
+ }
+
nextInnerAreas.add(nonProcessedInner);
+ }
+ }
+
+ if (nextInnerAreas == null ||
nextInnerAreas.isEmpty()) {
+
finishedAreas.add(nextOuterArea);
+ } else {
+ AreaCutData outCutData
= new AreaCutData();
+ outCutData.outerArea =
nextOuterArea;
+ outCutData.innerAreas=
nextInnerAreas;
+
areasToCut.add(outCutData);
+ }
+ }
}
}
- outerAreas = outerAfterThisStep;
+
}
-
+
// convert the java.awt.geom.Area back to the mkgmap way
- List<Way> cutOuterPolygon = new
ArrayList<Way>(outerAreas.size());
- for (Area area : outerAreas) {
+ List<Way> cuttedOuterPolygon = new
ArrayList<Way>(finishedAreas.size());
+ for (Area area : finishedAreas) {
Way w = singularAreaToWay(area,
FakeIdGenerator.makeFakeId());
if (w != null) {
w.copyTags(outerPolygon);
- cutOuterPolygon.add(w);
+ cuttedOuterPolygon.add(w);
+ if (log.isDebugEnabled()) {
+ log.debug("Way", outerPolygon.getId(),
"splitted to way", w.getId());
+ }
}
}
- return cutOuterPolygon;
+ return cuttedOuterPolygon;
}
/**
@@ -986,22 +1094,18 @@
* erroneous cases properly the method might return a list of areas.
*
* @param w a closed way
+ * @param clipBbox true if the areas should be clipped to the bounding
box; false else
* @return a list of enclosed ares
*/
- private List<Area> createAreas(Way w) {
+ private List<Area> createAreas(Way w, boolean clipBbox) {
Area area = new Area(createPolygon(w.getPoints()));
+ if (clipBbox && bboxArea.contains(area.getBounds())==false) {
+ // the area intersects the bounding box => clip it
+ area.intersect(bboxArea);
+ }
List<Area> areaList = areaToSingularAreas(area);
- if (areaList.size() > 1) {
- 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);
- }
+ if (log.isDebugEnabled()) {
+ log.debug("Bbox clipped
way",w.getId()+"=>",areaList.size(),"distinct area(s).");
}
return areaList;
}
@@ -1182,7 +1286,7 @@
}
/**
- * Checks if the polygon with polygonIndex1 contains the ring with
polygonIndex2.
+ * Checks if the polygon with polygonIndex1 contains the polygon with
polygonIndex2.
*
* @return true if polygon(polygonIndex1) contains
polygon(polygonIndex2)
*/
@@ -1580,16 +1684,16 @@
// note that we increase the rectangle by 1
because intersects
// checks
// only the interior
- bounds = new Rectangle(minLat - 1, minLon - 1,
maxLat - minLat
- + 2, maxLon - minLon + 2);
+ bounds = new Rectangle(minLon - 1, minLat - 1,
maxLon - minLon
+ + 2, maxLat - minLat + 2);
}
return bounds;
}
public boolean linePossiblyIntersectsWay(Coord p1, Coord p2) {
- return getBounds().intersectsLine(p1.getLatitude(),
- p1.getLongitude(), p2.getLatitude(),
p2.getLongitude());
+ return getBounds().intersectsLine(p1.getLongitude(),
+ p1.getLatitude(), p2.getLongitude(),
p2.getLatitude());
}
public void addWay(Way way) {
@@ -1644,7 +1748,7 @@
}
}
}
-
+
public List<Long> getOriginalIds() {
ArrayList<Long> idList = new
ArrayList<Long>(getOriginalWays().size());
for (Way w : getOriginalWays()) {
@@ -1653,7 +1757,6 @@
return idList;
}
- @Override
public String toString() {
StringBuilder sb = new StringBuilder(200);
sb.append(getId());
@@ -1688,4 +1791,164 @@
this.polygon = polygon;
}
}
+
+ private static class AreaCutData {
+ Area outerArea;
+ List<Area> innerAreas;
+ }
+
+ private static class CutPoint implements Comparable<CutPoint>{
+ int startPoint = Integer.MAX_VALUE;
+ int stopPoint = Integer.MIN_VALUE;
+ TreeSet<Area> areas;
+ private final CoordinateAxis axis;
+
+ public CutPoint(CoordinateAxis axis) {
+ this.axis = axis;
+ this.areas = new TreeSet<Area>(
+ (axis == CoordinateAxis.LONGITUDE ?
COMP_LONG_STOP : COMP_LAT_STOP));
+ }
+
+ public CutPoint duplicate() {
+ CutPoint newCutPoint = new CutPoint(this.axis);
+ newCutPoint.areas.addAll(areas);
+ newCutPoint.startPoint = startPoint;
+ newCutPoint.stopPoint = stopPoint;
+ return newCutPoint;
+ }
+
+ public int getCutPoint() {
+ return startPoint + (stopPoint - startPoint) / 2;
+ }
+
+ public Rectangle getCutRectangleForArea(Area toCut, boolean
firstRect) {
+ Rectangle areaRect = toCut.getBounds();
+ if (axis == CoordinateAxis.LONGITUDE) {
+ int newWidth = getCutPoint()-areaRect.x;
+ if (firstRect) {
+ return new Rectangle(areaRect.x,
areaRect.y, newWidth, areaRect.height);
+ } else {
+ return new
Rectangle(areaRect.x+newWidth, areaRect.y, areaRect.width-newWidth,
areaRect.height);
+ }
+ } else {
+ int newHeight = getCutPoint()-areaRect.y;
+ if (firstRect) {
+ return new Rectangle(areaRect.x,
areaRect.y, areaRect.width, newHeight);
+ } else {
+ return new Rectangle(areaRect.x,
areaRect.y+newHeight, areaRect.width, areaRect.height-newHeight);
+ }
+ }
+ }
+
+ public Collection<Area> getAreas() {
+ return areas;
+ }
+
+ public void addArea(Area area) {
+ // remove all areas that do not overlap with the new
area
+ while (areas.isEmpty() == false
+ && axis.getStop(areas.first()) < axis
+ .getStart(area)) {
+ // remove the first area
+ areas.pollFirst();
+ }
+
+ areas.add(area);
+ startPoint = axis.getStart(Collections.max(areas,
+ (axis == CoordinateAxis.LONGITUDE ?
COMP_LONG_START
+ : COMP_LAT_START)));
+ stopPoint = axis.getStop(areas.first());
+ }
+
+ public int getNumberOfAreas() {
+ return this.areas.size();
+ }
+
+ public int compareTo(CutPoint o) {
+ if (this == o) {
+ return 0;
+ }
+ int ndiff = getNumberOfAreas()-o.getNumberOfAreas();
+ if (ndiff != 0) {
+ return ndiff;
+ }
+ // prefer the larger area that is splitted
+ return
(stopPoint-startPoint)-(o.stopPoint-o.startPoint);
+ }
+
+ public String toString() {
+ return axis +" "+getNumberOfAreas()+" "+startPoint+"
"+stopPoint+" "+getCutPoint();
+ }
+
+ }
+
+ private static enum CoordinateAxis {
+ LATITUDE(false), LONGITUDE(true);
+
+ private CoordinateAxis(boolean useX) {
+ this.useX = useX;
+ }
+
+ private final boolean useX;
+
+ public int getStart(Area area) {
+ return getStart(area.getBounds());
+ }
+
+ public int getStart(Rectangle rect) {
+ return (useX ? rect.x : rect.y);
+ }
+
+ public int getStop(Area area) {
+ return getStop(area.getBounds());
+ }
+
+ public int getStop(Rectangle rect) {
+ return (useX ? rect.x + rect.width : rect.y +
rect.height);
+ }
+
+ }
+
+ private static final AreaComparator COMP_LONG_START = new
AreaComparator(
+ true, CoordinateAxis.LONGITUDE);
+ private static final AreaComparator COMP_LONG_STOP = new AreaComparator(
+ false, CoordinateAxis.LONGITUDE);
+ private static final AreaComparator COMP_LAT_START = new AreaComparator(
+ true, CoordinateAxis.LATITUDE);
+ private static final AreaComparator COMP_LAT_STOP = new AreaComparator(
+ false, CoordinateAxis.LATITUDE);
+
+ private static class AreaComparator implements Comparator<Area> {
+
+ private final CoordinateAxis axis;
+ private final boolean startPoint;
+
+ public AreaComparator(boolean startPoint, CoordinateAxis axis) {
+ this.startPoint = startPoint;
+ this.axis = axis;
+ }
+
+ public int compare(Area o1, Area o2) {
+ if (o1 == o2) {
+ return 0;
+ }
+
+ if (startPoint) {
+ int cmp = axis.getStart(o1) - axis.getStart(o2);
+ if (cmp == 0) {
+ return axis.getStop(o1) -
axis.getStop(o2);
+ } else {
+ return cmp;
+ }
+ } else {
+ int cmp = axis.getStop(o1) - axis.getStop(o2);
+ if (cmp == 0) {
+ return axis.getStart(o1) -
axis.getStart(o2);
+ } else {
+ return cmp;
+ }
+ }
+ }
+
+ }
}
_______________________________________________
mkgmap-dev mailing list
mkgmap-dev@lists.mkgmap.org.uk
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev