Fix indexing of multipolygon with multiple outer rings
issue 1265
This commit is contained in:
parent
0a8669edae
commit
107c814dca
3 changed files with 225 additions and 231 deletions
|
@ -11,6 +11,8 @@ import net.osmand.osm.MapUtils;
|
|||
import net.osmand.osm.Node;
|
||||
import net.osmand.osm.Way;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
/**
|
||||
* The idea of multipolygon:
|
||||
* - we treat each outer way as closed polygon
|
||||
|
@ -33,6 +35,11 @@ public class Multipolygon {
|
|||
*/
|
||||
private List<Way> outerWays, innerWays;
|
||||
|
||||
/**
|
||||
* an optional id of the multipolygon
|
||||
*/
|
||||
private long id;
|
||||
|
||||
/**
|
||||
* create a multipolygon with these outer and inner rings
|
||||
* the rings have to be well formed or data inconsistency will happen
|
||||
|
@ -69,6 +76,32 @@ public class Multipolygon {
|
|||
public Multipolygon(){
|
||||
outerWays = new ArrayList<Way> ();
|
||||
innerWays = new ArrayList<Way> ();
|
||||
id = 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new empty multipolygon with specified id
|
||||
* @param id the id to set
|
||||
*/
|
||||
public Multipolygon(long id){
|
||||
this();
|
||||
setId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the id of the multipolygon
|
||||
* @param newId id to set
|
||||
*/
|
||||
public void setId(long newId) {
|
||||
id = newId;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the id of the multipolygon
|
||||
* @return id
|
||||
*/
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -122,7 +155,7 @@ public class Multipolygon {
|
|||
* get the Inner Rings
|
||||
* @return the inner rings
|
||||
*/
|
||||
private SortedSet<Ring> getInnerRings() {
|
||||
public SortedSet<Ring> getInnerRings() {
|
||||
groupInRings();
|
||||
return innerRings;
|
||||
}
|
||||
|
@ -131,7 +164,7 @@ public class Multipolygon {
|
|||
* get the outer rings
|
||||
* @return outer rings
|
||||
*/
|
||||
private SortedSet<Ring> getOuterRings() {
|
||||
public SortedSet<Ring> getOuterRings() {
|
||||
groupInRings();
|
||||
return outerRings;
|
||||
}
|
||||
|
@ -178,6 +211,26 @@ public class Multipolygon {
|
|||
return zeroSizeIfNull(getOuterWays()) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* chekc if all rings are closed
|
||||
* @return true if all rings are closed by nature, false otherwise
|
||||
*/
|
||||
public boolean areRingsComplete() {
|
||||
SortedSet<Ring> set = getOuterRings();
|
||||
for (Ring r : set) {
|
||||
if (!r.isClosed()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
set = getInnerRings();
|
||||
for (Ring r : set) {
|
||||
if (!r.isClosed()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* return 0 if the list is null
|
||||
* @param l the list to check
|
||||
|
@ -272,16 +325,23 @@ public class Multipolygon {
|
|||
|
||||
/**
|
||||
* Split this multipolygon in several separate multipolygons with one outer ring each
|
||||
* @param log the stream to log problems to, if log = null, nothing will be logged
|
||||
* @return a list with multipolygons which have exactly one outer ring
|
||||
*/
|
||||
public List<Multipolygon> splitPerOuterRing() {
|
||||
public List<Multipolygon> splitPerOuterRing(Log log) {
|
||||
|
||||
//make a clone of the inners set
|
||||
// this set will be changed through execution of the method
|
||||
SortedSet<Ring> inners = new TreeSet<Ring>(getInnerRings());
|
||||
|
||||
// get the set of outer rings in a variable. This set will not be changed
|
||||
SortedSet<Ring> outers = getOuterRings();
|
||||
ArrayList<Multipolygon> multipolygons = new ArrayList<Multipolygon>();
|
||||
|
||||
// loop; start with the smallest outer ring
|
||||
for (Ring outer : outers) {
|
||||
|
||||
// Search the inners inside this outer ring
|
||||
SortedSet<Ring> innersInsideOuter = new TreeSet<Ring>();
|
||||
for (Ring inner : inners) {
|
||||
if (inner.isIn(outer)) {
|
||||
|
@ -289,15 +349,31 @@ public class Multipolygon {
|
|||
}
|
||||
}
|
||||
|
||||
SortedSet<Ring> thisOuter = new TreeSet<Ring>();
|
||||
// the inners should belong to this outer, so remove them from the list to check
|
||||
inners.removeAll(innersInsideOuter);
|
||||
|
||||
SortedSet<Ring> thisOuter = new TreeSet<Ring>();
|
||||
thisOuter.add(outer);
|
||||
|
||||
// create a new multipolygon with this outer and a list of inners
|
||||
Multipolygon m = new Multipolygon(thisOuter, innersInsideOuter);
|
||||
|
||||
multipolygons.add(m);
|
||||
}
|
||||
|
||||
if (inners.size() != 0 && log != null)
|
||||
log.warn("Multipolygon "+getId() + " has a mismatch in outer and inner rings");
|
||||
|
||||
return multipolygons;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method only works when the multipolygon has exaclt one outer Ring
|
||||
* @return the list of nodes in the outer ring
|
||||
*/
|
||||
public List<Node> getOuterNodes() {
|
||||
return getOuterRings().first().getBorder().getNodes();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -86,6 +86,20 @@ public class Ring implements Comparable<Ring>{
|
|||
return closedWays;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if this ring is closed by nature
|
||||
* @return true if this ring is closed, false otherwise
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
closeWays();
|
||||
for (int i = closedWays.size()-1; i>=0; i--) {
|
||||
if (!ways.contains(closedWays.get(i))){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a single closed way that represents the border
|
||||
* this method is CPU intensive
|
||||
|
@ -176,15 +190,38 @@ public class Ring implements Comparable<Ring>{
|
|||
closedWays = multiLine;
|
||||
|
||||
long[] endNodes = getMultiLineEndNodes(multiLine);
|
||||
|
||||
|
||||
if (endNodes[0] != endNodes[1]) {
|
||||
Way w = new Way(0L);
|
||||
w.addNode(endNodes[0]);
|
||||
w.addNode(endNodes[1]);
|
||||
closedWays.add(w);
|
||||
if(multiLine.get(0).getNodes() == null) {
|
||||
Way w = new Way(0L);
|
||||
w.addNode(endNodes[0]);
|
||||
w.addNode(endNodes[1]);
|
||||
closedWays.add(w);
|
||||
} else {
|
||||
Node n1 = null, n2 = null;
|
||||
if (multiLine.get(0).getFirstNodeId() == endNodes[0]) {
|
||||
n1 = multiLine.get(0).getNodes().get(0);
|
||||
} else {
|
||||
int index = multiLine.get(0).getNodes().size() - 1;
|
||||
n1 = multiLine.get(0).getNodes().get(index);
|
||||
}
|
||||
|
||||
int lastML = multiLine.size() - 1;
|
||||
if (multiLine.get(lastML).getFirstNodeId() == endNodes[0]) {
|
||||
n2 = multiLine.get(lastML).getNodes().get(0);
|
||||
} else {
|
||||
int index = multiLine.get(lastML).getNodes().size() - 1;
|
||||
n2 = multiLine.get(lastML).getNodes().get(index);
|
||||
}
|
||||
|
||||
Way w = new Way(0L);
|
||||
w.addNode(n1);
|
||||
w.addNode(n2);
|
||||
closedWays.add(w);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
@ -206,7 +243,9 @@ public class Ring implements Comparable<Ring>{
|
|||
* logging this creates a whole bunch of log lines for all ways
|
||||
* part of a multipolygon but not in the map
|
||||
*/
|
||||
if (toAdd.getNodeIds().size() < 2) continue;
|
||||
if (toAdd.getNodeIds().size() < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long toAddBeginPt = toAdd.getFirstNodeId();
|
||||
long toAddEndPt = toAdd.getLastNodeId();
|
||||
|
@ -332,6 +371,19 @@ public class Ring implements Comparable<Ring>{
|
|||
return new long[] {multiLine.get(0).getFirstNodeId(), multiLine.get(0).getLastNodeId()};
|
||||
}
|
||||
|
||||
if (multiLine.size() == 2) {
|
||||
// ring of two elements, arbitrary choice of the end nodes
|
||||
if(multiLine.get(0).getFirstNodeId() == multiLine.get(1).getFirstNodeId() &&
|
||||
multiLine.get(0).getLastNodeId() == multiLine.get(1).getLastNodeId()) {
|
||||
return new long[] {multiLine.get(0).getFirstNodeId(), multiLine.get(0).getFirstNodeId()};
|
||||
} else if(multiLine.get(0).getFirstNodeId() == multiLine.get(1).getLastNodeId() &&
|
||||
multiLine.get(0).getLastNodeId() == multiLine.get(1).getFirstNodeId()) {
|
||||
return new long[] {multiLine.get(0).getFirstNodeId(), multiLine.get(0).getFirstNodeId()};
|
||||
}
|
||||
}
|
||||
|
||||
// For all other multiLine lenghts, or for non-closed multiLines with two elements, proceed
|
||||
|
||||
long n1 = 0, n2 = 0;
|
||||
|
||||
if (multiLine.get(0).getFirstNodeId() == multiLine.get(1).getFirstNodeId() ||
|
||||
|
@ -352,7 +404,6 @@ public class Ring implements Comparable<Ring>{
|
|||
n2 = multiLine.get(lastIdx).getFirstNodeId();
|
||||
}
|
||||
|
||||
|
||||
return new long[] {n1, n2};
|
||||
}
|
||||
|
||||
|
|
|
@ -26,8 +26,9 @@ import net.osmand.Algoritms;
|
|||
import net.osmand.IProgress;
|
||||
import net.osmand.binary.OsmandOdb.MapData;
|
||||
import net.osmand.binary.OsmandOdb.MapDataBlock;
|
||||
import net.osmand.data.Boundary;
|
||||
import net.osmand.data.MapAlgorithms;
|
||||
import net.osmand.data.Multipolygon;
|
||||
import net.osmand.data.Ring;
|
||||
import net.osmand.data.preparation.MapZooms.MapZoomPair;
|
||||
import net.osmand.osm.Entity;
|
||||
import net.osmand.osm.Entity.EntityId;
|
||||
|
@ -113,234 +114,100 @@ public class IndexVectorMapCreator extends AbstractIndexPartCreator {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* index a multipolygon into the database
|
||||
* only multipolygons without admin_level and with type=multipolygon are indexed
|
||||
* broken multipolygons are also indexed, inner ways are sometimes left out, broken rings are split and closed
|
||||
* broken multipolygons will normally be logged
|
||||
* @param e the entity to index
|
||||
* @param ctx the database context
|
||||
* @throws SQLException
|
||||
*/
|
||||
private void indexMultiPolygon(Entity e, OsmDbAccessorContext ctx) throws SQLException {
|
||||
if (e instanceof Relation && "multipolygon".equals(e.getTag(OSMTagKey.TYPE))) { //$NON-NLS-1$
|
||||
if(e.getTag(OSMTagKey.ADMIN_LEVEL) != null) {
|
||||
// don't index boundaries as multipolygon (only areas ideally are multipolygon)
|
||||
return;
|
||||
}
|
||||
ctx.loadEntityRelation((Relation) e);
|
||||
Map<Entity, String> entities = ((Relation) e).getMemberEntities();
|
||||
// Don't handle things that aren't multipolygon, and nothing administrative
|
||||
if (! (e instanceof Relation) ||
|
||||
! "multipolygon".equals(e.getTag(OSMTagKey.TYPE)) ||
|
||||
e.getTag(OSMTagKey.ADMIN_LEVEL) != null ) return;
|
||||
|
||||
boolean outerFound = false;
|
||||
for (Entity es : entities.keySet()) {
|
||||
if (es instanceof Way) {
|
||||
boolean inner = "inner".equals(entities.get(es)); //$NON-NLS-1$
|
||||
if (!inner) {
|
||||
outerFound = true;
|
||||
// This is incorrect (it should be intersection of all boundaries)
|
||||
// Currently it causes an issue with coastline (if one line is coastline)
|
||||
// for (String t : es.getTagKeySet()) {
|
||||
// e.putTag(t, es.getTag(t));
|
||||
// }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!outerFound) {
|
||||
logMapDataWarn.warn("Probably map bug: Multipoligon id=" + e.getId() + " contains only inner ways : "); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
return;
|
||||
}
|
||||
ctx.loadEntityRelation((Relation) e);
|
||||
Map<Entity, String> entities = ((Relation) e).getMemberEntities();
|
||||
|
||||
renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(0).getMaxZoom(), typeUse, addtypeUse, namesUse, tempNameUse);
|
||||
if (typeUse.size() > 0) {
|
||||
List<List<Way>> completedRings = new ArrayList<List<Way>>();
|
||||
List<List<Way>> incompletedRings = new ArrayList<List<Way>>();
|
||||
for (Entity es : entities.keySet()) {
|
||||
if (es instanceof Way) {
|
||||
if (!((Way) es).getNodeIds().isEmpty()) {
|
||||
combineMultiPolygons((Way) es, completedRings, incompletedRings);
|
||||
}
|
||||
}
|
||||
}
|
||||
// skip incompleted rings and do not add whole relation ?
|
||||
if (!incompletedRings.isEmpty()) {
|
||||
logMapDataWarn.warn("In multipolygon " + e.getId() + " there are incompleted ways : " + incompletedRings);
|
||||
return;
|
||||
// completedRings.addAll(incompletedRings);
|
||||
}
|
||||
// create a multipolygon object for this
|
||||
Multipolygon original = new Multipolygon(e.getId());
|
||||
|
||||
// skip completed rings that are not one type
|
||||
for (List<Way> l : completedRings) {
|
||||
boolean innerType = "inner".equals(entities.get(l.get(0))); //$NON-NLS-1$
|
||||
for (Way way : l) {
|
||||
boolean inner = "inner".equals(entities.get(way)); //$NON-NLS-1$
|
||||
if (innerType != inner) {
|
||||
logMapDataWarn
|
||||
.warn("Probably map bug: Multipoligon contains outer and inner ways.\n" + //$NON-NLS-1$
|
||||
"Way:"
|
||||
+ way.getId()
|
||||
+ " is strange part of completed ring. InnerType:" + innerType + " way inner: " + inner + " way inner string:" + entities.get(way)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// That check is not strictly needed on preproccessing step because client can handle it
|
||||
Node nodeOut = checkOuterWaysEncloseInnerWays(completedRings, entities);
|
||||
if (nodeOut != null) {
|
||||
logMapDataWarn.warn("Map bug: Multipoligon contains 'inner' way point outside of 'outer' border.\n" + //$NON-NLS-1$
|
||||
"Multipolygon id : " + e.getId() + ", inner node out id : " + nodeOut.getId()); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
List<Node> outerWaySrc = new ArrayList<Node>();
|
||||
List<List<Node>> innerWays = new ArrayList<List<Node>>();
|
||||
|
||||
TIntArrayList typeToSave = new TIntArrayList(typeUse);
|
||||
long baseId = 0;
|
||||
for (List<Way> l : completedRings) {
|
||||
boolean innerType = "inner".equals(entities.get(l.get(0))); //$NON-NLS-1$
|
||||
if (!innerType && !outerWaySrc.isEmpty()) {
|
||||
logMapDataWarn.warn("Map bug: Multipoligon contains many 'outer' borders.\n" + //$NON-NLS-1$
|
||||
"Multipolygon id : " + e.getId() + ", outer way id : " + l.get(0).getId()); //$NON-NLS-1$
|
||||
return;
|
||||
}
|
||||
List<Node> toCollect;
|
||||
if (innerType) {
|
||||
toCollect = new ArrayList<Node>();
|
||||
innerWays.add(toCollect);
|
||||
} else {
|
||||
toCollect = outerWaySrc;
|
||||
}
|
||||
|
||||
for (Way way : l) {
|
||||
toCollect.addAll(way.getNodes());
|
||||
if (!innerType) {
|
||||
TIntArrayList out = multiPolygonsWays.put(way.getId(), typeToSave);
|
||||
if(out == null){
|
||||
baseId = -way.getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(baseId == 0){
|
||||
// use base id as well?
|
||||
baseId = notUsedId --;
|
||||
}
|
||||
nextZoom: for (int level = 0; level < mapZooms.size(); level++) {
|
||||
renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(level).getMaxZoom(), typeUse, addtypeUse, namesUse,
|
||||
tempNameUse);
|
||||
if (typeUse.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
long id = convertBaseIdToGeneratedId(baseId, level);
|
||||
// simplify route
|
||||
List<Node> outerWay = outerWaySrc;
|
||||
int zoomToSimplify = mapZooms.getLevel(level).getMaxZoom() - 1;
|
||||
if (zoomToSimplify < 15) {
|
||||
outerWay = simplifyCycleWay(outerWay, zoomToSimplify, zoomWaySmothness);
|
||||
if (outerWay == null) {
|
||||
continue nextZoom;
|
||||
}
|
||||
List<List<Node>> newinnerWays = new ArrayList<List<Node>>();
|
||||
for (List<Node> ls : innerWays) {
|
||||
ls = simplifyCycleWay(ls, zoomToSimplify, zoomWaySmothness);
|
||||
if (ls != null) {
|
||||
newinnerWays.add(ls);
|
||||
}
|
||||
}
|
||||
innerWays = newinnerWays;
|
||||
}
|
||||
insertBinaryMapRenderObjectIndex(mapTree[level], outerWay, innerWays, namesUse, id, true, typeUse, addtypeUse, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Node checkOuterWaysEncloseInnerWays(List<List<Way>> completedRings, Map<Entity, String> entities) {
|
||||
List<List<Way>> innerWays = new ArrayList<List<Way>>();
|
||||
Boundary outerBoundary = new Boundary();
|
||||
Node toReturn = null;
|
||||
for (List<Way> ring : completedRings) {
|
||||
boolean innerType = "inner".equals(entities.get(ring.get(0))); //$NON-NLS-1$
|
||||
if (!innerType) {
|
||||
outerBoundary.addOuterWays(ring);
|
||||
} else {
|
||||
innerWays.add(ring);
|
||||
}
|
||||
}
|
||||
|
||||
for (List<Way> innerRing : innerWays) {
|
||||
ring: for (Way innerWay : innerRing) {
|
||||
for (Node node : innerWay.getNodes()) {
|
||||
if (!outerBoundary.containsPoint(node.getLatitude(), node.getLongitude())) {
|
||||
if (toReturn == null) {
|
||||
toReturn = node;
|
||||
}
|
||||
completedRings.remove(innerRing);
|
||||
break ring;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
private List<Way> reverse(List<Way> l) {
|
||||
Collections.reverse(l);
|
||||
for(Way w : l){
|
||||
w.getNodeIds().reverse();
|
||||
Collections.reverse(w.getNodes());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
private List<Way> appendLists(List<Way> w1, List<Way> w2){
|
||||
w1.addAll(w2);
|
||||
return w1;
|
||||
}
|
||||
|
||||
//TODO Can the Multipolygon class be the one that replaces this?
|
||||
private void combineMultiPolygons(Way w, List<List<Way>> completedRings, List<List<Way>> incompletedRings) {
|
||||
long lId = w.getEntityIds().get(w.getEntityIds().size() - 1).getId().longValue();
|
||||
long fId = w.getEntityIds().get(0).getId().longValue();
|
||||
if (fId == lId) {
|
||||
completedRings.add(Collections.singletonList(w));
|
||||
} else {
|
||||
List<Way> l = new ArrayList<Way>();
|
||||
l.add(w);
|
||||
boolean add = true;
|
||||
for (int k = 0; k < incompletedRings.size();) {
|
||||
boolean remove = false;
|
||||
List<Way> i = incompletedRings.get(k);
|
||||
Way last = i.get(i.size() - 1);
|
||||
Way first = i.get(0);
|
||||
long lastId = last.getEntityIds().get(last.getEntityIds().size() - 1).getId().longValue();
|
||||
long firstId = first.getEntityIds().get(0).getId().longValue();
|
||||
if (fId == lastId) {
|
||||
remove = true;
|
||||
l = appendLists(i, l);
|
||||
fId = firstId;
|
||||
} else if (lId == firstId) {
|
||||
l = appendLists(l, i);
|
||||
remove = true;
|
||||
lId = lastId;
|
||||
} else if (lId == lastId) {
|
||||
l = appendLists(l, reverse(i));
|
||||
remove = true;
|
||||
lId = firstId;
|
||||
} else if (fId == firstId) {
|
||||
l = appendLists(reverse(i), l);
|
||||
remove = true;
|
||||
fId = lastId;
|
||||
}
|
||||
if (remove) {
|
||||
incompletedRings.remove(k);
|
||||
// fill the multipolygon with all ways from the Relation
|
||||
for (Entity es : entities.keySet()) {
|
||||
if (es instanceof Way) {
|
||||
boolean inner = "inner".equals(entities.get(es)); //$NON-NLS-1$
|
||||
if (inner) {
|
||||
original.addInnerWay((Way) es);
|
||||
} else {
|
||||
k++;
|
||||
}
|
||||
if (fId == lId) {
|
||||
completedRings.add(l);
|
||||
add = false;
|
||||
break;
|
||||
original.addOuterWay((Way) es);
|
||||
}
|
||||
}
|
||||
if (add) {
|
||||
incompletedRings.add(l);
|
||||
}
|
||||
|
||||
// Log if something is wrong
|
||||
if (!original.hasOpenedPolygons()) {
|
||||
logMapDataWarn.warn("Multipolygon has unclosed parts: Multipoligon id=" + e.getId()); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(0).getMaxZoom(), typeUse, addtypeUse, namesUse, tempNameUse);
|
||||
|
||||
//Don't add multipolygons with an unknown type
|
||||
if (typeUse.size() == 0) return;
|
||||
|
||||
// Log the fact that Rings aren't complete, but continue with the relation, try to close it as well as possible
|
||||
if (!original.areRingsComplete()) {
|
||||
logMapDataWarn.warn("In multipolygon " + e.getId() + " there are incompleted ways");
|
||||
}
|
||||
// Rings with different types (inner or outer) in one ring will be logged in the previous case
|
||||
// The Rings are only composed by type, so if one way gets in a different Ring, the rings will be incomplete
|
||||
|
||||
List<Multipolygon> multipolygons = original.splitPerOuterRing(logMapDataWarn);
|
||||
|
||||
|
||||
for (Multipolygon m : multipolygons) {
|
||||
|
||||
// innerWays are new closed ways
|
||||
List<List<Node>> innerWays = new ArrayList<List<Node>>();
|
||||
|
||||
for (Ring r : m.getInnerRings()) {
|
||||
innerWays.add(r.getBorder().getNodes());
|
||||
}
|
||||
|
||||
// don't use the relation ids. Create new ones
|
||||
long baseId = notUsedId --;
|
||||
nextZoom: for (int level = 0; level < mapZooms.size(); level++) {
|
||||
renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(level).getMaxZoom(), typeUse, addtypeUse, namesUse,
|
||||
tempNameUse);
|
||||
if (typeUse.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
long id = convertBaseIdToGeneratedId(baseId, level);
|
||||
// simplify route
|
||||
List<Node> outerWay = m.getOuterNodes();
|
||||
int zoomToSimplify = mapZooms.getLevel(level).getMaxZoom() - 1;
|
||||
if (zoomToSimplify < 15) {
|
||||
outerWay = simplifyCycleWay(outerWay, zoomToSimplify, zoomWaySmothness);
|
||||
if (outerWay == null) {
|
||||
continue nextZoom;
|
||||
}
|
||||
List<List<Node>> newinnerWays = new ArrayList<List<Node>>();
|
||||
for (List<Node> ls : innerWays) {
|
||||
ls = simplifyCycleWay(ls, zoomToSimplify, zoomWaySmothness);
|
||||
if (ls != null) {
|
||||
newinnerWays.add(ls);
|
||||
}
|
||||
}
|
||||
innerWays = newinnerWays;
|
||||
}
|
||||
insertBinaryMapRenderObjectIndex(mapTree[level], outerWay, innerWays, namesUse, id, true, typeUse, addtypeUse, true);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static List<Node> simplifyCycleWay(List<Node> ns, int zoom, int zoomWaySmothness) throws SQLException {
|
||||
if (checkForSmallAreas(ns, zoom + Math.min(zoomWaySmothness / 2, 3), 2, 4)) {
|
||||
return null;
|
||||
|
|
Loading…
Reference in a new issue