diff --git a/DataExtractionOSM/src/net/osmand/binary/BinaryMapDataObject.java b/DataExtractionOSM/src/net/osmand/binary/BinaryMapDataObject.java index a98994b7d6..09b9b2c9bb 100644 --- a/DataExtractionOSM/src/net/osmand/binary/BinaryMapDataObject.java +++ b/DataExtractionOSM/src/net/osmand/binary/BinaryMapDataObject.java @@ -22,6 +22,14 @@ public class BinaryMapDataObject { public BinaryMapDataObject(){ } + public BinaryMapDataObject(int[] coordinates, int[] types, int[][] polygonInnerCoordinates, long id){ + this.polygonInnerCoordinates = polygonInnerCoordinates; + this.coordinates = coordinates; + this.additionalTypes = new int[0]; + this.types = types; + this.id = id; + } + protected void setCoordinates(int[] coordinates) { this.coordinates = coordinates; } diff --git a/DataExtractionOSM/src/net/osmand/binary/BinaryMapIndexReader.java b/DataExtractionOSM/src/net/osmand/binary/BinaryMapIndexReader.java index 36d1cbf722..e629593a3c 100644 --- a/DataExtractionOSM/src/net/osmand/binary/BinaryMapIndexReader.java +++ b/DataExtractionOSM/src/net/osmand/binary/BinaryMapIndexReader.java @@ -534,6 +534,7 @@ public class BinaryMapIndexReader { switch (tag) { case 0: // encoding rules are required! + index.finishInitializingTags(); if(index.encodingRules.isEmpty()){ throw new IllegalStateException("Encoding rules are not defined for the map index"); } @@ -1314,6 +1315,8 @@ public class BinaryMapIndexReader { public int nameEncodingType = 0; public int refEncodingType = -1; public int coastlineEncodingType = -1; + public int coastlineBrokenEncodingType = -1; + public int landEncodingType = -1; public int onewayAttribute = -1; public int onewayReverseAttribute = -1; public TIntHashSet positiveLayers = new TIntHashSet(2); @@ -1326,6 +1329,11 @@ public class BinaryMapIndexReader { public TagValuePair decodeType(int type){ return decodingRules.get(type); } + + public void finishInitializingTags() { + coastlineBrokenEncodingType = encodingRules.size() * 2; + initMapEncodingRule(0, coastlineBrokenEncodingType, "natural", "coastline_broken"); + } private void initMapEncodingRule(int type, int id, String tag, String val) { if(!encodingRules.containsKey(tag)){ @@ -1340,6 +1348,8 @@ public class BinaryMapIndexReader { nameEncodingType = id; } else if("natural".equals(tag) && "coastline".equals(val)){ coastlineEncodingType = id; + } else if("natural".equals(tag) && "land".equals(val)){ + landEncodingType = id; } else if("oneway".equals(tag) && "yes".equals(val)){ onewayAttribute = id; } else if("oneway".equals(tag) && "-1".equals(val)){ diff --git a/DataExtractionOSM/src/net/osmand/data/MapAlgorithms.java b/DataExtractionOSM/src/net/osmand/data/MapAlgorithms.java index 936f6ffcdf..706d0fee19 100644 --- a/DataExtractionOSM/src/net/osmand/data/MapAlgorithms.java +++ b/DataExtractionOSM/src/net/osmand/data/MapAlgorithms.java @@ -1,5 +1,7 @@ package net.osmand.data; +import gnu.trove.list.TLongList; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -165,6 +167,66 @@ public class MapAlgorithms { return clockwiseSum >= 0; } + public static boolean isClockwiseWay(TLongList c) { + if (c.size() == 0) { + return true; + } + + // calculate middle Y + int mask = 0xffffffff; + long middleY = 0; + for (int i = 0; i < c.size(); i++) { + middleY += (c.get(i) & mask); + } + middleY /= (long) c.size(); + + double clockwiseSum = 0; + + boolean firstDirectionUp = false; + int previousX = Integer.MIN_VALUE; + int firstX = Integer.MIN_VALUE; + + int prevX = (int) (c.get(0) >> 32); + int prevY = (int) (c.get(0) & mask); + + for (int i = 1; i < c.size(); i++) { + int x = (int) (c.get(i) >> 32); + int y = (int) (c.get(i) & mask); + int rX = ray_intersect_x(prevX, prevY, x, y, (int) middleY); + if (rX != Integer.MIN_VALUE) { + boolean skipSameSide = (y <= middleY) == (prevY <= middleY); + if (skipSameSide) { + continue; + } + boolean directionUp = prevY >= middleY; + if (firstX == Integer.MIN_VALUE) { + firstDirectionUp = directionUp; + firstX = rX; + } else { + boolean clockwise = (!directionUp) == (previousX < rX); + if (clockwise) { + clockwiseSum += Math.abs(previousX - rX); + } else { + clockwiseSum -= Math.abs(previousX - rX); + } + } + previousX = rX; + prevX = x; + prevY = y; + } + } + if (firstX != Integer.MIN_VALUE) { + boolean clockwise = (!firstDirectionUp) == (previousX < firstX); + if (clockwise) { + clockwiseSum += Math.abs(previousX - firstX); + } else { + clockwiseSum -= Math.abs(previousX - firstX); + } + } + + return clockwiseSum >= 0; + } + // try to intersect from left to right public static double ray_intersect_lon(Node node, Node node2, double latitude, double longitude) { // a node below @@ -195,4 +257,31 @@ public class MapAlgorithms { } } } + + public static int ray_intersect_x(int prevX, int prevY, int x, int y, int middleY) { + // prev node above line + // x,y node below line + if (prevY > y) { + int tx = prevX; + int ty = prevY; + x = prevX; + y = prevY; + prevX = tx; + prevY = ty; + } + if (y == middleY || prevY == middleY) { + middleY -= 1; + } + if (prevY > middleY || y < middleY) { + return Integer.MIN_VALUE; + } else { + if (y == prevY) { + // the node on the boundary !!! + return x; + } + // that tested on all cases (left/right) + double rx = x + ((double) middleY - y) * ((double) x - prevX) / (((double) y - prevY)); + return (int) rx; + } + } } \ No newline at end of file diff --git a/DataExtractionOSM/src/net/osmand/data/preparation/IndexCreator.java b/DataExtractionOSM/src/net/osmand/data/preparation/IndexCreator.java index 0b96f9f015..98b19beb35 100644 --- a/DataExtractionOSM/src/net/osmand/data/preparation/IndexCreator.java +++ b/DataExtractionOSM/src/net/osmand/data/preparation/IndexCreator.java @@ -626,7 +626,7 @@ public class IndexCreator { long time = System.currentTimeMillis(); IndexCreator creator = new IndexCreator(new File("/home/victor/projects/OsmAnd/data/osm-gen/")); //$NON-NLS-1$ creator.setIndexMap(true); - creator.setIndexAddress(false); + creator.setIndexAddress(true); creator.setIndexPOI(false); creator.setIndexTransport(false); @@ -637,7 +637,7 @@ public class IndexCreator { creator.setZoomWaySmothness(2); MapRenderingTypes rt = MapRenderingTypes.getDefault();// new MapRenderingTypes("/home/victor/projects/OsmAnd/data/testdata/roads_rendering_types.xml"); MapZooms zooms = MapZooms.getDefault(); // MapZooms.parseZooms("15-"); - creator.setNodesDBFile(new File("/home/victor/projects/OsmAnd/data/osm-gen/nodes.tmp.odb")); +// creator.setNodesDBFile(new File("/home/victor/projects/OsmAnd/data/osm-gen/nodes.tmp.odb")); // creator.generateIndexes(new File("/home/victor/projects/OsmAnd/data/osm-maps/luxembourg.osm.pbf"), creator.generateIndexes(new File("/home/victor/projects/OsmAnd/data/osm-maps/cuba.osm.bz2"), new ConsoleProgressImplementation(1), null, zooms, rt, log); diff --git a/DataExtractionOSM/src/net/osmand/data/preparation/IndexVectorMapCreator.java b/DataExtractionOSM/src/net/osmand/data/preparation/IndexVectorMapCreator.java index 7ef842aa13..106e9b311a 100644 --- a/DataExtractionOSM/src/net/osmand/data/preparation/IndexVectorMapCreator.java +++ b/DataExtractionOSM/src/net/osmand/data/preparation/IndexVectorMapCreator.java @@ -502,13 +502,10 @@ public class IndexVectorMapCreator extends AbstractIndexPartCreator { if (e instanceof Way || e instanceof Node) { // manipulate what kind of way to load ctx.loadEntityData(e); - if(e instanceof Way && "coastline".equals(e.getTag(OSMTagKey.NATURAL))){ - coastlineProcessor.processCoastline((Way) e); - return; - } else if(true){ - // FIXME - return; - } +// if(e instanceof Way && "coastline".equals(e.getTag(OSMTagKey.NATURAL))){ +// coastlineProcessor.processCoastline((Way) e); +// return; +// } for (int level = 0; level < mapZooms.size(); level++) { boolean area = renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(level).getMaxZoom(), typeUse, addtypeUse, namesUse, tempNameUse); diff --git a/DataExtractionOSM/src/net/osmand/data/preparation/OsmDbAccessor.java b/DataExtractionOSM/src/net/osmand/data/preparation/OsmDbAccessor.java index 09a1d576b1..44eb082d71 100644 --- a/DataExtractionOSM/src/net/osmand/data/preparation/OsmDbAccessor.java +++ b/DataExtractionOSM/src/net/osmand/data/preparation/OsmDbAccessor.java @@ -190,7 +190,11 @@ public class OsmDbAccessor implements OsmDbAccessorContext { int ord = rs.getInt(2); if (ord > 0 || first) { first = false; - way.addNode(new Node(rs.getDouble(5), rs.getDouble(6), rs.getLong(1))); + if(rs.getObject(5) != null) { + way.addNode(new Node(rs.getDouble(5), rs.getDouble(6), rs.getLong(1))); + } else { + way.addNode(rs.getLong(1)); + } } if (ord == 0 && rs.getObject(3) != null) { way.putTag(rs.getString(3), rs.getString(4)); diff --git a/DataExtractionOSM/src/net/osmand/osm/MultyPolygon.java b/DataExtractionOSM/src/net/osmand/osm/MultyPolygon.java deleted file mode 100644 index 852fd5ac33..0000000000 --- a/DataExtractionOSM/src/net/osmand/osm/MultyPolygon.java +++ /dev/null @@ -1,92 +0,0 @@ -package net.osmand.osm; - -import net.osmand.binary.BinaryMapDataObject; - -public class MultyPolygon extends BinaryMapDataObject { - - // currently do not distinguish inner/outer area - // just not fill intersecting areas - // first 32 bits - x, second 32 bits - y - private long[][] lines = null; - private String[] names = null; - private String tag = null; - private String value = null; - private int layer = 0; - public MultyPolygon(){ - super(); - id = -1; - } - - @Override - public int getPointsLength() { - throw new UnsupportedOperationException(); - } - - @Override - public int getPoint31XTile(int ind) { - throw new UnsupportedOperationException(); - } - - public int getLayer() { - return layer; - } - - public void setLayer(int layer) { - this.layer = layer; - } - - public void setNames(String[] names) { - this.names = names; - } - - public String getName(int bound){ - return names[bound]; - } - - public void setType(int type){ - types = new int[]{type}; - } - - @Override - public int getPoint31YTile(int ind) { - throw new UnsupportedOperationException(); - } - - public int getBoundsCount(){ - return lines == null ? 0 : lines.length; - } - - public int getBoundPointsCount(int bound){ - return lines[bound].length; - } - - public void setLines(long[][] lines) { - this.lines = lines; - } - - - public int getPoint31XTile(int ind, int b) { - return (int)(lines[b][ind] >> 32); - } - - public int getPoint31YTile(int ind, int b) { - return (int)(lines[b][ind] & Integer.MAX_VALUE); - } - - public void setTag(String tag) { - this.tag = tag; - } - - public void setValue(String value) { - this.value = value; - } - - public String getTag() { - return tag; - } - - public String getValue() { - return value; - } -} - diff --git a/DataExtractionOSM/src/net/osmand/render/default.render.xml b/DataExtractionOSM/src/net/osmand/render/default.render.xml index da73316586..4e0e2e8f6c 100644 --- a/DataExtractionOSM/src/net/osmand/render/default.render.xml +++ b/DataExtractionOSM/src/net/osmand/render/default.render.xml @@ -198,7 +198,8 @@ - + + @@ -232,6 +233,7 @@ + @@ -822,7 +824,7 @@ - + @@ -847,9 +849,15 @@ + + + + + + - + @@ -944,12 +952,13 @@ - + + diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 70d83e62b3..8fd7f4a70d 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -534,7 +534,7 @@ A previous OsmAnd version is installed. All offline data will be supported by new the new application. But Favorite points should be exported in the old application and later imported by the new one. Build {0} successfully installed ({1}). Downloading build… - Continue to install OsmAnd - {0} from {1} {2} MB ? + Do you want to install OsmAnd - {0} from {1} {2} MB ? Retrieving the list of OsmAnd builds failed Loading OsmAnd builds… Select one of the OsmAnd builds to install @@ -676,7 +676,7 @@ Turn slightly left and go Make U-turn and go Head - Continue + No, Thanks Download regions Thank you for choosing OsmAnd. \nTo use the major features of this application, you will need some regional offline data, which you can download (use Settings, Offline Data). Afterwards, you will be able to search by address, look up POIs and find public transportation. Searching for signal… diff --git a/OsmAnd/src/net/osmand/plus/render/MapRenderRepositories.java b/OsmAnd/src/net/osmand/plus/render/MapRenderRepositories.java index 3a9485452e..e406d70fc6 100644 --- a/OsmAnd/src/net/osmand/plus/render/MapRenderRepositories.java +++ b/OsmAnd/src/net/osmand/plus/render/MapRenderRepositories.java @@ -11,7 +11,6 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.text.MessageFormat; import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -26,12 +25,13 @@ import net.osmand.LogUtil; import net.osmand.access.AccessibleToast; import net.osmand.binary.BinaryMapDataObject; import net.osmand.binary.BinaryMapIndexReader; +import net.osmand.binary.BinaryMapIndexReader.MapIndex; import net.osmand.binary.BinaryMapIndexReader.SearchRequest; import net.osmand.binary.BinaryMapIndexReader.TagValuePair; import net.osmand.data.IndexConstants; +import net.osmand.data.MapAlgorithms; import net.osmand.data.MapTileDownloader.IMapDownloaderCallback; import net.osmand.osm.MapUtils; -import net.osmand.osm.MultyPolygon; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandSettings; import net.osmand.plus.OsmandSettings.CommonPreference; @@ -304,7 +304,7 @@ public class MapRenderRepositories { ArrayList tempList = new ArrayList(); System.gc(); // to clear previous objects TLongSet ids = new TLongHashSet(); - Map> multiPolygons = new LinkedHashMap>(); + List coastLines = new ArrayList(); int leftX = MapUtils.get31TileNumberX(cLeftLongitude); int rightX = MapUtils.get31TileNumberX(cRightLongitude); int bottomY = MapUtils.get31TileNumberY(cBottomLatitude); @@ -369,24 +369,23 @@ public class MapRenderRepositories { } count++; - // TODO refactor ! - if(r.containsType(r.getMapIndex().coastlineEncodingType)) { - TagValuePair pair = r.getMapIndex().decodeType(r.getMapIndex().coastlineEncodingType); - pair = new TagValuePair(pair.tag, pair.value, 0); - if (!multiPolygons.containsKey(pair)) { - multiPolygons.put(pair, new ArrayList()); - } - multiPolygons.get(pair).add(r); + if(r.containsType(r.getMapIndex().coastlineEncodingType)){ + coastLines.add(r); + } else { + // do not mess coastline and other types + tempList.add(r); } if (checkWhetherInterrupted()) { return false; } - tempList.add(r); + } } - List pMulti = proccessMultiPolygons(multiPolygons, leftX, rightX, bottomY, topY, zoom); - tempList.addAll(pMulti); + if(!coastLines.isEmpty()) { + List pcoastlines = processCoastlines(coastLines, leftX, rightX, bottomY, topY, zoom); + tempList.addAll(pcoastlines); + } if (count > 0) { log.info(String.format("BLat=%s, TLat=%s, LLong=%s, RLong=%s, zoom=%s", //$NON-NLS-1$ cBottomLatitude, cTopLatitude, cLeftLongitude, cRightLongitude, zoom)); @@ -619,240 +618,106 @@ public class MapRenderRepositories { // bmp = null; // bmpLocation = null; } + + public Map getMetaInfoFiles() { + return files; + } - // / Manipulating with multipolygons - - public List proccessMultiPolygons(Map> multyPolygons, int leftX, int rightX, - int bottomY, int topY, int zoom) { - List listPolygons = new ArrayList(multyPolygons.size()); + /// MULTI POLYGONS (coastline) + private List processCoastlines(List coastLines, int leftX, int rightX, int bottomY, int topY, + int zoom) { List completedRings = new ArrayList(); List incompletedRings = new ArrayList(); - List completedRingNames = new ArrayList(); - List incompletedRingNames = new ArrayList(); - for (TagValuePair type : multyPolygons.keySet()) { - List directList; - List inverselist; - if (((type.additionalAttribute >> 15) & 1) == 1) { - TagValuePair directType = new TagValuePair(type.tag, type.value, type.additionalAttribute & ((1 << 15) - 1)); - if (!multyPolygons.containsKey(directType)) { - inverselist = multyPolygons.get(type); - directList = Collections.emptyList(); - } else { - // continue on inner boundaries - continue; - } - } else { - TagValuePair inverseType = new TagValuePair(type.tag, type.value, type.additionalAttribute | (1 << 15)); - directList = multyPolygons.get(type); - inverselist = Collections.emptyList(); - if (multyPolygons.containsKey(inverseType)) { - inverselist = multyPolygons.get(inverseType); - } - } - completedRings.clear(); - incompletedRings.clear(); - completedRingNames.clear(); - incompletedRingNames.clear(); - log.debug("Process multypolygon " + type.tag + " " + type.value + //$NON-NLS-1$ //$NON-NLS-2$ - " direct list : " + directList + " rev : " + inverselist); //$NON-NLS-1$ //$NON-NLS-2$ - MultyPolygon pl = processMultiPolygon(leftX, rightX, bottomY, topY, listPolygons, completedRings, incompletedRings, - completedRingNames, incompletedRingNames, type, directList, inverselist, zoom); - if (pl != null) { - listPolygons.add(pl); - } - } - return listPolygons; - } - - private MultyPolygon processMultiPolygon(int leftX, int rightX, int bottomY, int topY, List listPolygons, - List completedRings, List incompletedRings, List completedRingNames, - List incompletedRingNames, TagValuePair type, List directList, - List inverselist, int zoom) { - MultyPolygon pl = new MultyPolygon(); - // delete direction last bit (to not show point) - pl.setTag(type.tag); - pl.setValue(type.value); + List result = new ArrayList(coastLines.size()); + MapIndex mapIndex = null; long dbId = 0; - for (int km = 0; km < 2; km++) { - List list = km == 0 ? directList : inverselist; - for (BinaryMapDataObject o : list) { - int len = o.getPointsLength(); - if (len < 2) { - continue; - } - dbId = o.getId() >> 1; - TLongList coordinates = new TLongArrayList(o.getPointsLength() / 2); - int px = o.getPoint31XTile(km == 0 ? 0 : len - 1); - int py = o.getPoint31YTile(km == 0 ? 0 : len - 1); - int x = px; - int y = py; - boolean pinside = leftX <= x && x <= rightX && y >= topY && y <= bottomY; - if (pinside) { - coordinates.add((((long) x) << 32) | ((long) y)); - } - for (int i = 1; i < len; i++) { - x = o.getPoint31XTile(km == 0 ? i : len - i - 1); - y = o.getPoint31YTile(km == 0 ? i : len - i - 1); - boolean inside = leftX <= x && x <= rightX && y >= topY && y <= bottomY; - boolean lineEnded = calculateLineCoordinates(inside, x, y, pinside, px, py, leftX, rightX, bottomY, topY, coordinates); - if (lineEnded) { - processMultipolygonLine(completedRings, incompletedRings, completedRingNames, incompletedRingNames, coordinates, - o.getName()); - // create new line if it goes outside - coordinates = new TLongArrayList(); - } - px = x; - py = y; - pinside = inside; - } - processMultipolygonLine(completedRings, incompletedRings, completedRingNames, incompletedRingNames, coordinates, - o.getName()); + for (BinaryMapDataObject o : coastLines) { + int len = o.getPointsLength(); + if (len < 2) { + continue; } + mapIndex = o.getMapIndex(); + dbId = o.getId() >> 1; + TLongList coordinates = new TLongArrayList(o.getPointsLength() / 2); + int px = o.getPoint31XTile(0); + int py = o.getPoint31YTile(0); + int x = px; + int y = py; + boolean pinside = leftX <= x && x <= rightX && y >= topY && y <= bottomY; + if (pinside) { + coordinates.add((((long) x) << 32) | ((long) y)); + } + for (int i = 1; i < len; i++) { + x = o.getPoint31XTile(i); + y = o.getPoint31YTile(i); + boolean inside = leftX <= x && x <= rightX && y >= topY && y <= bottomY; + boolean lineEnded = calculateLineCoordinates(inside, x, y, pinside, px, py, leftX, rightX, bottomY, topY, coordinates); + if (lineEnded) { + combineMultipolygonLine(completedRings, incompletedRings, coordinates); + // create new line if it goes outside + coordinates = new TLongArrayList(); + } + px = x; + py = y; + pinside = inside; + } + combineMultipolygonLine(completedRings, incompletedRings, coordinates); } if (completedRings.size() == 0 && incompletedRings.size() == 0) { - return null; + return result; } if (incompletedRings.size() > 0) { - unifyIncompletedRings(incompletedRings, completedRings, completedRingNames, incompletedRingNames, leftX, rightX, bottomY, topY, - dbId, zoom); - } else { - // due to self intersection small objects (for low zooms check only coastline) - if (zoom >= 13 || ("natural".equals(type.tag) && "coastline".equals(type.value))) { //$NON-NLS-1$//$NON-NLS-2$ - boolean clockwiseFound = false; - for (TLongList c : completedRings) { - if (isClockwiseWay(c)) { - clockwiseFound = true; - break; - } - } - if (!clockwiseFound) { - // add whole bound - TLongList whole = new TLongArrayList(4); - whole.add((((long) leftX) << 32) | ((long) topY)); - whole.add((((long) rightX) << 32) | ((long) topY)); - whole.add((((long) rightX) << 32) | ((long) bottomY)); - whole.add((((long) leftX) << 32) | ((long) bottomY)); - completedRings.add(whole); - log.info("!!! Isolated island !!!"); //$NON-NLS-1$ - } - - } + unifyIncompletedRings(incompletedRings, completedRings, leftX, rightX, bottomY, topY, dbId, zoom); } - - long[][] lns = new long[completedRings.size()][]; + boolean clockwiseFound = false; + int mask = 0xffffffff; for (int i = 0; i < completedRings.size(); i++) { TLongList ring = completedRings.get(i); - lns[i] = new long[ring.size()]; - for (int j = 0; j < lns[i].length; j++) { - lns[i][j] = ring.get(j); + int[] coordinates = new int[ring.size() * 2]; + for (int j = 0; j < ring.size(); j++) { + coordinates[j * 2] = (int) (ring.get(j) >> 32); + coordinates[j * 2 + 1] = (int) (ring.get(j) & mask); } + boolean clockwise = MapAlgorithms.isClockwiseWay(ring); + clockwiseFound = clockwiseFound || clockwise; + BinaryMapDataObject o = new BinaryMapDataObject(coordinates, new int[] { clockwise ? mapIndex.coastlineEncodingType + : mapIndex.landEncodingType }, null, dbId); + o.setMapIndex(mapIndex); + o.setArea(true); + result.add(o); } - pl.setNames(completedRingNames.toArray(new String[completedRings.size()])); - pl.setLines(lns); - return pl; + + for (int i = 0; i < incompletedRings.size(); i++) { + TLongList ring = incompletedRings.get(i); + int[] coordinates = new int[ring.size() * 2]; + for (int j = 0; j < ring.size(); j++) { + coordinates[j * 2] = (int) (ring.get(j) >> 32); + coordinates[j * 2 + 1] = (int) (ring.get(j) & mask); + } + BinaryMapDataObject o = new BinaryMapDataObject(coordinates, new int[] { mapIndex.coastlineBrokenEncodingType}, null, dbId); + o.setMapIndex(mapIndex); + result.add(o); + } + if (!clockwiseFound) { + // add complete water tile + BinaryMapDataObject o = new BinaryMapDataObject(new int[] { leftX, topY, rightX, topY, rightX, bottomY, leftX, bottomY, leftX, + topY }, new int[] { mapIndex.coastlineEncodingType }, null, dbId); + log.info("!!! Isolated islands !!!"); //$NON-NLS-1$ + result.add(o); + + } + return result; } - // Copied from MapAlgorithms - private boolean isClockwiseWay(TLongList c) { - if (c.size() == 0) { - return true; - } - - // calculate middle Y - int mask = 0xffffffff; - long middleY = 0; - for (int i = 0; i < c.size(); i++) { - middleY += (c.get(i) & mask); - } - middleY /= (long) c.size(); - - double clockwiseSum = 0; - - boolean firstDirectionUp = false; - int previousX = Integer.MIN_VALUE; - int firstX = Integer.MIN_VALUE; - - int prevX = (int) (c.get(0) >> 32); - int prevY = (int) (c.get(0) & mask); - - for (int i = 1; i < c.size(); i++) { - int x = (int) (c.get(i) >> 32); - int y = (int) (c.get(i) & mask); - int rX = ray_intersect_x(prevX, prevY, x, y, (int) middleY); - if (rX != Integer.MIN_VALUE) { - boolean skipSameSide = (y <= middleY) == (prevY <= middleY); - if (skipSameSide) { - continue; - } - boolean directionUp = prevY >= middleY; - if (firstX == Integer.MIN_VALUE) { - firstDirectionUp = directionUp; - firstX = rX; - } else { - boolean clockwise = (!directionUp) == (previousX < rX); - if (clockwise) { - clockwiseSum += Math.abs(previousX - rX); - } else { - clockwiseSum -= Math.abs(previousX - rX); - } - } - previousX = rX; - prevX = x; - prevY = y; - } - } - if (firstX != Integer.MIN_VALUE) { - boolean clockwise = (!firstDirectionUp) == (previousX < firstX); - if (clockwise) { - clockwiseSum += Math.abs(previousX - firstX); - } else { - clockwiseSum -= Math.abs(previousX - firstX); - } - } - - return clockwiseSum >= 0; - } - - // Copied from MapAlgorithms - private int ray_intersect_x(int prevX, int prevY, int x, int y, int middleY) { - // prev node above line - // x,y node below line - if (prevY > y) { - int tx = prevX; - int ty = prevY; - x = prevX; - y = prevY; - prevX = tx; - prevY = ty; - } - if (y == middleY || prevY == middleY) { - middleY -= 1; - } - if (prevY > middleY || y < middleY) { - return Integer.MIN_VALUE; - } else { - if (y == prevY) { - // the node on the boundary !!! - return x; - } - // that tested on all cases (left/right) - double rx = x + ((double) middleY - y) * ((double) x - prevX) / (((double) y - prevY)); - return (int) rx; - } - } - - private void processMultipolygonLine(List completedRings, List incompletedRings, - List completedRingsNames, List incompletedRingsNames, TLongList coordinates, String name) { + private void combineMultipolygonLine(List completedRings, List incompletedRings, TLongList coordinates) { if (coordinates.size() > 0) { if (coordinates.get(0) == coordinates.get(coordinates.size() - 1)) { completedRings.add(coordinates); - completedRingsNames.add(name); } else { boolean add = true; for (int k = 0; k < incompletedRings.size();) { boolean remove = false; TLongList i = incompletedRings.get(k); - String oldName = incompletedRingsNames.get(k); if (coordinates.get(0) == i.get(i.size() - 1)) { i.addAll(coordinates.subList(1, coordinates.size())); remove = true; @@ -863,31 +728,23 @@ public class MapRenderRepositories { } if (remove) { incompletedRings.remove(k); - incompletedRingsNames.remove(k); } else { k++; } if (coordinates.get(0) == coordinates.get(coordinates.size() - 1)) { completedRings.add(coordinates); - if (oldName != null) { - completedRingsNames.add(oldName); - } else { - completedRingsNames.add(name); - } add = false; break; } } if (add) { incompletedRings.add(coordinates); - incompletedRingsNames.add(name); } } } } - private void unifyIncompletedRings(List incompletedRings, List completedRings, List completedRingNames, - List incompletedRingNames, int leftX, int rightX, int bottomY, int topY, long dbId, int zoom) { + private void unifyIncompletedRings(List incompletedRings, List completedRings, int leftX, int rightX, int bottomY, int topY, long dbId, int zoom) { int mask = 0xffffffff; Set nonvisitedRings = new LinkedHashSet(); for (int j = 0; j < incompletedRings.size(); j++) { @@ -924,7 +781,6 @@ public class MapRenderRepositories { } for (int j = 0; j < incompletedRings.size(); j++) { TLongList i = incompletedRings.get(j); - String name = incompletedRingNames.get(j); if (!nonvisitedRings.contains(j)) { continue; } @@ -1030,7 +886,6 @@ public class MapRenderRepositories { } completedRings.add(i); - completedRingNames.add(name); } } @@ -1043,6 +898,40 @@ public class MapRenderRepositories { } return res; } + + private boolean calculateLineCoordinates(boolean inside, int x, int y, boolean pinside, int px, int py, int leftX, int rightX, + int bottomY, int topY, TLongList coordinates) { + boolean lineEnded = false; + if (pinside) { + if (!inside) { + long is = calculateIntersection(x, y, px, py, leftX, rightX, bottomY, topY); + if (is == -1) { + // it is an error (!) + is = (((long) px) << 32) | ((long) py); + } + coordinates.add(is); + lineEnded = true; + } else { + coordinates.add((((long) x) << 32) | ((long) y)); + } + } else { + long is = calculateIntersection(x, y, px, py, leftX, rightX, bottomY, topY); + if (inside) { + // assert is != -1; + coordinates.add(is); + coordinates.add((((long) x) << 32) | ((long) y)); + } else if (is != -1) { + int bx = (int) (is >> 32); + int by = (int) (is & 0xffffffff); + coordinates.add(is); + is = calculateIntersection(x, y, bx, by, leftX, rightX, bottomY, topY); + coordinates.add(is); + lineEnded = true; + } + } + + return lineEnded; + } /** * @return -1 if there is no instersection or x<<32 | y @@ -1129,41 +1018,5 @@ public class MapRenderRepositories { return -1l; } - private boolean calculateLineCoordinates(boolean inside, int x, int y, boolean pinside, int px, int py, int leftX, int rightX, - int bottomY, int topY, TLongList coordinates) { - boolean lineEnded = false; - if (pinside) { - if (!inside) { - long is = calculateIntersection(x, y, px, py, leftX, rightX, bottomY, topY); - if (is == -1) { - // it is an error (!) - is = (((long) px) << 32) | ((long) py); - } - coordinates.add(is); - lineEnded = true; - } else { - coordinates.add((((long) x) << 32) | ((long) y)); - } - } else { - long is = calculateIntersection(x, y, px, py, leftX, rightX, bottomY, topY); - if (inside) { - // assert is != -1; - coordinates.add(is); - coordinates.add((((long) x) << 32) | ((long) y)); - } else if (is != -1) { - int bx = (int) (is >> 32); - int by = (int) (is & 0xffffffff); - coordinates.add(is); - is = calculateIntersection(x, y, bx, by, leftX, rightX, bottomY, topY); - coordinates.add(is); - lineEnded = true; - } - } - return lineEnded; - } - - public Map getMetaInfoFiles() { - return files; - } } diff --git a/OsmAnd/src/net/osmand/plus/render/OsmandRenderer.java b/OsmAnd/src/net/osmand/plus/render/OsmandRenderer.java index d094bcbd90..8a72b6caa2 100644 --- a/OsmAnd/src/net/osmand/plus/render/OsmandRenderer.java +++ b/OsmAnd/src/net/osmand/plus/render/OsmandRenderer.java @@ -15,7 +15,6 @@ import net.osmand.binary.BinaryMapDataObject; import net.osmand.binary.BinaryMapIndexReader.TagValuePair; import net.osmand.data.MapTileDownloader.IMapDownloaderCallback; import net.osmand.osm.MapRenderingTypes; -import net.osmand.osm.MultyPolygon; import net.osmand.plus.render.NativeOsmandLibrary.NativeSearchResult; import net.osmand.plus.render.TextRenderer.TextDrawInfo; import net.osmand.render.RenderingRuleProperty; @@ -385,49 +384,34 @@ public class OsmandRenderer { TIntObjectHashMap orderMap = new TIntObjectHashMap(); if (render != null) { render.clearState(); - + for (int i = 0; i < sz; i++) { BinaryMapDataObject o = objects.get(i); int sh = i << 8; - if (o instanceof MultyPolygon) { - //FIXME Multipolygon : 1 -// int layer = ((MultyPolygon) o).getLayer(); -// render.setTagValueZoomLayer(((MultyPolygon) o).getTag(), ((MultyPolygon) o).getValue(), rc.zoom, layer); -// render.setIntFilter(render.ALL.R_ORDER_TYPE, MapRenderingTypes.POLYGON_TYPE); -// if(render.search(RenderingRulesStorage.ORDER_RULES)) { -// int order = render.getIntPropertyValue(render.ALL.R_ORDER); -// put(orderMap, order, sh, init); -// if(render.isSpecified(render.ALL.R_SHADOW_LEVEL)){ -// rc.shadowLevelMin = Math.min(rc.shadowLevelMin, order); -// rc.shadowLevelMax = Math.max(rc.shadowLevelMax, order); -// render.clearValue(render.ALL.R_SHADOW_LEVEL); -// } -// } - } else { - for (int j = 0; j < o.getTypes().length; j++) { - // put(orderMap, BinaryMapDataObject.getOrder(o.getTypes()[j]), sh + j, init); - int wholeType = o.getTypes()[j]; - - int layer = 0; - if (o.getPointsLength() > 1) { - layer = o.getSimpleLayer(); - } - TagValuePair pair = o.getMapIndex().decodeType(wholeType); - if (pair != null) { - render.setTagValueZoomLayer(pair.tag, pair.value, rc.zoom, layer); - render.setBooleanFilter(render.ALL.R_AREA, o.isArea()); - render.setBooleanFilter(render.ALL.R_POINT, o.getPointsLength() == 1); - render.setBooleanFilter(render.ALL.R_CYCLE, o.isCycle()); - if (render.search(RenderingRulesStorage.ORDER_RULES)) { - o.setObjectType(render.getIntPropertyValue(render.ALL.R_OBJECT_TYPE)); - int order = render.getIntPropertyValue(render.ALL.R_ORDER); - put(orderMap, order, sh + j, init); - if (render.isSpecified(render.ALL.R_SHADOW_LEVEL)) { - rc.shadowLevelMin = Math.min(rc.shadowLevelMin, order); - rc.shadowLevelMax = Math.max(rc.shadowLevelMax, order); - render.clearValue(render.ALL.R_SHADOW_LEVEL); - } + for (int j = 0; j < o.getTypes().length; j++) { + // put(orderMap, BinaryMapDataObject.getOrder(o.getTypes()[j]), sh + j, init); + int wholeType = o.getTypes()[j]; + + int layer = 0; + if (o.getPointsLength() > 1) { + layer = o.getSimpleLayer(); + } + + TagValuePair pair = o.getMapIndex().decodeType(wholeType); + if (pair != null) { + render.setTagValueZoomLayer(pair.tag, pair.value, rc.zoom, layer); + render.setBooleanFilter(render.ALL.R_AREA, o.isArea()); + render.setBooleanFilter(render.ALL.R_POINT, o.getPointsLength() == 1); + render.setBooleanFilter(render.ALL.R_CYCLE, o.isCycle()); + if (render.search(RenderingRulesStorage.ORDER_RULES)) { + o.setObjectType(render.getIntPropertyValue(render.ALL.R_OBJECT_TYPE)); + int order = render.getIntPropertyValue(render.ALL.R_ORDER); + put(orderMap, order, sh + j, init); + if (render.isSpecified(render.ALL.R_SHADOW_LEVEL)) { + rc.shadowLevelMin = Math.min(rc.shadowLevelMin, order); + rc.shadowLevelMax = Math.max(rc.shadowLevelMax, order); + render.clearValue(render.ALL.R_SHADOW_LEVEL); } } @@ -452,21 +436,14 @@ public class OsmandRenderer { protected void drawObj(BinaryMapDataObject obj, RenderingRuleSearchRequest render, Canvas canvas, RenderingContext rc, int l, boolean renderText, boolean drawOnlyShadow) { rc.allObjects++; - if (obj instanceof MultyPolygon) { - // TODO - if(!drawOnlyShadow){ - drawMultiPolygon(obj, render, canvas, rc); - } - } else { - int type = obj.getObjectType(); - TagValuePair pair = obj.getMapIndex().decodeType(obj.getTypes()[l]); - if (type == MapRenderingTypes.POINT_TYPE && !drawOnlyShadow) { - drawPoint(obj, render, canvas, rc, pair, renderText); - } else if (type == MapRenderingTypes.POLYLINE_TYPE) { - drawPolyline(obj, render, canvas, rc, pair, obj.getSimpleLayer(), drawOnlyShadow); - } else if (type == MapRenderingTypes.POLYGON_TYPE && !drawOnlyShadow) { - drawPolygon(obj, render, canvas, rc, pair); - } + int type = obj.getObjectType(); + TagValuePair pair = obj.getMapIndex().decodeType(obj.getTypes()[l]); + if (type == MapRenderingTypes.POINT_TYPE && !drawOnlyShadow) { + drawPoint(obj, render, canvas, rc, pair, renderText); + } else if (type == MapRenderingTypes.POLYLINE_TYPE) { + drawPolyline(obj, render, canvas, rc, pair, obj.getSimpleLayer(), drawOnlyShadow); + } else if (type == MapRenderingTypes.POLYGON_TYPE && !drawOnlyShadow) { + drawPolygon(obj, render, canvas, rc, pair); } } @@ -491,70 +468,11 @@ public class OsmandRenderer { return calcPoint(o.getPoint31XTile(ind), o.getPoint31YTile(ind), rc); } - private PointF calcMultiPolygonPoint(MultyPolygon o, int i, int b, RenderingContext rc){ - rc.pointCount ++; - float tx = o.getPoint31XTile(i, b)/ rc.tileDivisor; - float ty = o.getPoint31YTile(i, b) / rc.tileDivisor; - float dTileX = tx - rc.leftX; - float dTileY = ty - rc.topY; - float x = rc.cosRotateTileSize * dTileX - rc.sinRotateTileSize * dTileY; - float y = rc.sinRotateTileSize * dTileX + rc.cosRotateTileSize * dTileY; - rc.tempPoint.set(x, y); - if(rc.tempPoint.x >= 0 && rc.tempPoint.x < rc.width && - rc.tempPoint.y >= 0 && rc.tempPoint.y < rc.height){ - rc.pointInsideCount++; - } - return rc.tempPoint; - } public void clearCachedResources(){ shaders.clear(); } - private void drawMultiPolygon(BinaryMapDataObject obj, RenderingRuleSearchRequest render, Canvas canvas, RenderingContext rc) { - String tag = ((MultyPolygon)obj).getTag(); - String value = ((MultyPolygon)obj).getValue(); - if(render == null || tag == null){ - return; - } - render.setInitialTagValueZoom(tag, value, rc.zoom); - boolean rendered = render.search(RenderingRulesStorage.POLYGON_RULES); - if(!rendered || !updatePaint(render, paint, 0, true, rc)){ - return; - } - rc.visible++; - Path path = new Path(); - for (int i = 0; i < ((MultyPolygon) obj).getBoundsCount(); i++) { - int cnt = ((MultyPolygon) obj).getBoundPointsCount(i); - float xText = 0; - float yText = 0; - for (int j = 0; j < cnt; j++) { - PointF p = calcMultiPolygonPoint((MultyPolygon) obj, j, i, rc); - xText += p.x; - yText += p.y; - if (j == 0) { - path.moveTo(p.x, p.y); - } else { - path.lineTo(p.x, p.y); - } - } - if (cnt > 0) { - textRenderer.renderText(obj, render, rc, new TagValuePair(tag, value, 0), xText / cnt, yText / cnt, null, null); - } - } - canvas.drawPath(path, paint); - // for test purpose -// paint.setStyle(Style.STROKE); -// paint.setStrokeWidth(1.5f); -// paint.setColor(Color.BLACK); -// paint.setPathEffect(null); -// canvas.drawPath(path, paint); - - if (updatePaint(render, paint, 1, false, rc)) { - canvas.drawPath(path, paint); - } - } - private void drawPolygon(BinaryMapDataObject obj, RenderingRuleSearchRequest render, Canvas canvas, RenderingContext rc, TagValuePair pair) { if(render == null || pair == null){ return;