Add coastline processing

This commit is contained in:
Victor Shcherb 2012-04-01 15:53:22 +02:00
parent 06ec6db070
commit 12b09c1300
11 changed files with 289 additions and 493 deletions

View file

@ -22,6 +22,14 @@ public class BinaryMapDataObject {
public 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) { protected void setCoordinates(int[] coordinates) {
this.coordinates = coordinates; this.coordinates = coordinates;
} }

View file

@ -534,6 +534,7 @@ public class BinaryMapIndexReader {
switch (tag) { switch (tag) {
case 0: case 0:
// encoding rules are required! // encoding rules are required!
index.finishInitializingTags();
if(index.encodingRules.isEmpty()){ if(index.encodingRules.isEmpty()){
throw new IllegalStateException("Encoding rules are not defined for the map index"); 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 nameEncodingType = 0;
public int refEncodingType = -1; public int refEncodingType = -1;
public int coastlineEncodingType = -1; public int coastlineEncodingType = -1;
public int coastlineBrokenEncodingType = -1;
public int landEncodingType = -1;
public int onewayAttribute = -1; public int onewayAttribute = -1;
public int onewayReverseAttribute = -1; public int onewayReverseAttribute = -1;
public TIntHashSet positiveLayers = new TIntHashSet(2); public TIntHashSet positiveLayers = new TIntHashSet(2);
@ -1327,6 +1330,11 @@ public class BinaryMapIndexReader {
return decodingRules.get(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) { private void initMapEncodingRule(int type, int id, String tag, String val) {
if(!encodingRules.containsKey(tag)){ if(!encodingRules.containsKey(tag)){
encodingRules.put(tag, new LinkedHashMap<String, Integer>()); encodingRules.put(tag, new LinkedHashMap<String, Integer>());
@ -1340,6 +1348,8 @@ public class BinaryMapIndexReader {
nameEncodingType = id; nameEncodingType = id;
} else if("natural".equals(tag) && "coastline".equals(val)){ } else if("natural".equals(tag) && "coastline".equals(val)){
coastlineEncodingType = id; coastlineEncodingType = id;
} else if("natural".equals(tag) && "land".equals(val)){
landEncodingType = id;
} else if("oneway".equals(tag) && "yes".equals(val)){ } else if("oneway".equals(tag) && "yes".equals(val)){
onewayAttribute = id; onewayAttribute = id;
} else if("oneway".equals(tag) && "-1".equals(val)){ } else if("oneway".equals(tag) && "-1".equals(val)){

View file

@ -1,5 +1,7 @@
package net.osmand.data; package net.osmand.data;
import gnu.trove.list.TLongList;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -165,6 +167,66 @@ public class MapAlgorithms {
return clockwiseSum >= 0; 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 // try to intersect from left to right
public static double ray_intersect_lon(Node node, Node node2, double latitude, double longitude) { public static double ray_intersect_lon(Node node, Node node2, double latitude, double longitude) {
// a node below // 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;
}
}
} }

View file

@ -626,7 +626,7 @@ public class IndexCreator {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
IndexCreator creator = new IndexCreator(new File("/home/victor/projects/OsmAnd/data/osm-gen/")); //$NON-NLS-1$ IndexCreator creator = new IndexCreator(new File("/home/victor/projects/OsmAnd/data/osm-gen/")); //$NON-NLS-1$
creator.setIndexMap(true); creator.setIndexMap(true);
creator.setIndexAddress(false); creator.setIndexAddress(true);
creator.setIndexPOI(false); creator.setIndexPOI(false);
creator.setIndexTransport(false); creator.setIndexTransport(false);
@ -637,7 +637,7 @@ public class IndexCreator {
creator.setZoomWaySmothness(2); creator.setZoomWaySmothness(2);
MapRenderingTypes rt = MapRenderingTypes.getDefault();// new MapRenderingTypes("/home/victor/projects/OsmAnd/data/testdata/roads_rendering_types.xml"); MapRenderingTypes rt = MapRenderingTypes.getDefault();// new MapRenderingTypes("/home/victor/projects/OsmAnd/data/testdata/roads_rendering_types.xml");
MapZooms zooms = MapZooms.getDefault(); // MapZooms.parseZooms("15-"); 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/luxembourg.osm.pbf"),
creator.generateIndexes(new File("/home/victor/projects/OsmAnd/data/osm-maps/cuba.osm.bz2"), creator.generateIndexes(new File("/home/victor/projects/OsmAnd/data/osm-maps/cuba.osm.bz2"),
new ConsoleProgressImplementation(1), null, zooms, rt, log); new ConsoleProgressImplementation(1), null, zooms, rt, log);

View file

@ -502,13 +502,10 @@ public class IndexVectorMapCreator extends AbstractIndexPartCreator {
if (e instanceof Way || e instanceof Node) { if (e instanceof Way || e instanceof Node) {
// manipulate what kind of way to load // manipulate what kind of way to load
ctx.loadEntityData(e); ctx.loadEntityData(e);
if(e instanceof Way && "coastline".equals(e.getTag(OSMTagKey.NATURAL))){ // if(e instanceof Way && "coastline".equals(e.getTag(OSMTagKey.NATURAL))){
coastlineProcessor.processCoastline((Way) e); // coastlineProcessor.processCoastline((Way) e);
return; // return;
} else if(true){ // }
// FIXME
return;
}
for (int level = 0; level < mapZooms.size(); level++) { for (int level = 0; level < mapZooms.size(); level++) {
boolean area = renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(level).getMaxZoom(), typeUse, addtypeUse, namesUse, boolean area = renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(level).getMaxZoom(), typeUse, addtypeUse, namesUse,
tempNameUse); tempNameUse);

View file

@ -190,7 +190,11 @@ public class OsmDbAccessor implements OsmDbAccessorContext {
int ord = rs.getInt(2); int ord = rs.getInt(2);
if (ord > 0 || first) { if (ord > 0 || first) {
first = false; first = false;
if(rs.getObject(5) != null) {
way.addNode(new Node(rs.getDouble(5), rs.getDouble(6), rs.getLong(1))); 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) { if (ord == 0 && rs.getObject(3) != null) {
way.putTag(rs.getString(3), rs.getString(4)); way.putTag(rs.getString(3), rs.getString(4));

View file

@ -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;
}
}

View file

@ -199,6 +199,7 @@
<filter appMode="pedestrian" tag="highway" value="bridleway" order="59"/> <filter appMode="pedestrian" tag="highway" value="bridleway" order="59"/>
<filter tag="highway" value="bridleway" order="35"/> <filter tag="highway" value="bridleway" order="35"/>
<filter tag="natural" value="coastline_broken" order="4"/>
</group> </group>
<group objectType="3" point="false"> <group objectType="3" point="false">
@ -232,6 +233,7 @@
<filter tag="natural" value="water" order="5"/> <filter tag="natural" value="water" order="5"/>
<filter tag="natural" value="bay" order="5"/> <filter tag="natural" value="bay" order="5"/>
<filter tag="natural" value="land" order="2"/>
<filter tag="natural" value="coastline" order="1"/> <filter tag="natural" value="coastline" order="1"/>
<filter tag="natural" value="" order="1"/> <filter tag="natural" value="" order="1"/>
</group> </group>
@ -822,7 +824,7 @@
<!-- Water, night color 330099 --> <!-- Water, night color 330099 -->
<group> <group>
<filter minzoom="1" tag="natural" value="coastline"/> <filter minzoom="1" tag="natural" value="coastline" color_2="#464646" strokeWidth_2="0.5"/>
<filter minzoom="4" tag="natural" value="water"/> <filter minzoom="4" tag="natural" value="water"/>
<filter minzoom="4" tag="natural" value="lake"/> <filter minzoom="4" tag="natural" value="lake"/>
<filter minzoom="4" tag="natural" value="bay"/> <filter minzoom="4" tag="natural" value="bay"/>
@ -847,9 +849,15 @@
</group> </group>
<group>
<filter noPolygons="true" tag="natural" value="land" color="#00ffffff"/>
<filter nightMode="true" tag="natural" value="land" color="#003333"/>
<filter tag="natural" value="land" color="#f1eae4"/>
</group>
<!-- Open nature, night color 004333 --> <!-- Open nature, night color 004333 -->
<group> <group>
<filter minzoom="10" color="#f2efe9" tag="natural" value="land"/>
<filter minzoom="12" color="#ffffc0" tag="natural" value="heath"/> <filter minzoom="12" color="#ffffc0" tag="natural" value="heath"/>
<filter minzoom="12" color="#ead8bd" tag="landuse" value="farm"/> <filter minzoom="12" color="#ead8bd" tag="landuse" value="farm"/>
<filter minzoom="12" color="#ead8bd" tag="landuse" value="farmland"/> <filter minzoom="12" color="#ead8bd" tag="landuse" value="farmland"/>
@ -950,6 +958,7 @@
<line> <line>
<filter minzoom="1" tag="natural" value="coastline_broken" color="#000000" strokeWidth_2="1.5"/>
<group> <group>
<filter hmRendered="true" tag="highway" value="motorway" maxzoom="13" color="#809bff"/> <filter hmRendered="true" tag="highway" value="motorway" maxzoom="13" color="#809bff"/>
<filter tag="highway" value="motorway" maxzoom="13" color="#a58dff"/> <filter tag="highway" value="motorway" maxzoom="13" color="#a58dff"/>

View file

@ -534,7 +534,7 @@
<string name="osmand_net_previously_installed">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.</string> <string name="osmand_net_previously_installed">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.</string>
<string name="build_installed">Build {0} successfully installed ({1}).</string> <string name="build_installed">Build {0} successfully installed ({1}).</string>
<string name="downloading_build">Downloading build&#8230;</string> <string name="downloading_build">Downloading build&#8230;</string>
<string name="install_selected_build">Continue to install OsmAnd - {0} from {1} {2} MB ?</string> <string name="install_selected_build">Do you want to install OsmAnd - {0} from {1} {2} MB ?</string>
<string name="loading_builds_failed">Retrieving the list of OsmAnd builds failed</string> <string name="loading_builds_failed">Retrieving the list of OsmAnd builds failed</string>
<string name="loading_builds">Loading OsmAnd builds&#8230;</string> <string name="loading_builds">Loading OsmAnd builds&#8230;</string>
<string name="select_build_to_install">Select one of the OsmAnd builds to install</string> <string name="select_build_to_install">Select one of the OsmAnd builds to install</string>
@ -676,7 +676,7 @@
<string name="route_tsll">Turn slightly left and go</string> <string name="route_tsll">Turn slightly left and go</string>
<string name="route_tu">Make U-turn and go</string> <string name="route_tu">Make U-turn and go</string>
<string name="route_head">Head</string> <string name="route_head">Head</string>
<string name="first_time_continue">Continue</string> <string name="first_time_continue">No, Thanks</string>
<string name="first_time_download">Download regions</string> <string name="first_time_download">Download regions</string>
<string name="first_time_msg">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.</string> <string name="first_time_msg">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.</string>
<string name="search_poi_location">Searching for signal&#8230;</string> <string name="search_poi_location">Searching for signal&#8230;</string>

View file

@ -11,7 +11,6 @@ import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
@ -26,12 +25,13 @@ import net.osmand.LogUtil;
import net.osmand.access.AccessibleToast; import net.osmand.access.AccessibleToast;
import net.osmand.binary.BinaryMapDataObject; import net.osmand.binary.BinaryMapDataObject;
import net.osmand.binary.BinaryMapIndexReader; import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapIndexReader.MapIndex;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest; import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.BinaryMapIndexReader.TagValuePair; import net.osmand.binary.BinaryMapIndexReader.TagValuePair;
import net.osmand.data.IndexConstants; import net.osmand.data.IndexConstants;
import net.osmand.data.MapAlgorithms;
import net.osmand.data.MapTileDownloader.IMapDownloaderCallback; import net.osmand.data.MapTileDownloader.IMapDownloaderCallback;
import net.osmand.osm.MapUtils; import net.osmand.osm.MapUtils;
import net.osmand.osm.MultyPolygon;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandSettings; import net.osmand.plus.OsmandSettings;
import net.osmand.plus.OsmandSettings.CommonPreference; import net.osmand.plus.OsmandSettings.CommonPreference;
@ -304,7 +304,7 @@ public class MapRenderRepositories {
ArrayList<BinaryMapDataObject> tempList = new ArrayList<BinaryMapDataObject>(); ArrayList<BinaryMapDataObject> tempList = new ArrayList<BinaryMapDataObject>();
System.gc(); // to clear previous objects System.gc(); // to clear previous objects
TLongSet ids = new TLongHashSet(); TLongSet ids = new TLongHashSet();
Map<TagValuePair, List<BinaryMapDataObject>> multiPolygons = new LinkedHashMap<TagValuePair, List<BinaryMapDataObject>>(); List<BinaryMapDataObject> coastLines = new ArrayList<BinaryMapDataObject>();
int leftX = MapUtils.get31TileNumberX(cLeftLongitude); int leftX = MapUtils.get31TileNumberX(cLeftLongitude);
int rightX = MapUtils.get31TileNumberX(cRightLongitude); int rightX = MapUtils.get31TileNumberX(cRightLongitude);
int bottomY = MapUtils.get31TileNumberY(cBottomLatitude); int bottomY = MapUtils.get31TileNumberY(cBottomLatitude);
@ -369,24 +369,23 @@ public class MapRenderRepositories {
} }
count++; count++;
// TODO refactor ! if(r.containsType(r.getMapIndex().coastlineEncodingType)){
if(r.containsType(r.getMapIndex().coastlineEncodingType)) { coastLines.add(r);
TagValuePair pair = r.getMapIndex().decodeType(r.getMapIndex().coastlineEncodingType); } else {
pair = new TagValuePair(pair.tag, pair.value, 0); // do not mess coastline and other types
if (!multiPolygons.containsKey(pair)) { tempList.add(r);
multiPolygons.put(pair, new ArrayList<BinaryMapDataObject>());
}
multiPolygons.get(pair).add(r);
} }
if (checkWhetherInterrupted()) { if (checkWhetherInterrupted()) {
return false; return false;
} }
tempList.add(r);
} }
} }
List<MultyPolygon> pMulti = proccessMultiPolygons(multiPolygons, leftX, rightX, bottomY, topY, zoom); if(!coastLines.isEmpty()) {
tempList.addAll(pMulti); List<BinaryMapDataObject> pcoastlines = processCoastlines(coastLines, leftX, rightX, bottomY, topY, zoom);
tempList.addAll(pcoastlines);
}
if (count > 0) { if (count > 0) {
log.info(String.format("BLat=%s, TLat=%s, LLong=%s, RLong=%s, zoom=%s", //$NON-NLS-1$ log.info(String.format("BLat=%s, TLat=%s, LLong=%s, RLong=%s, zoom=%s", //$NON-NLS-1$
cBottomLatitude, cTopLatitude, cLeftLongitude, cRightLongitude, zoom)); cBottomLatitude, cTopLatitude, cLeftLongitude, cRightLongitude, zoom));
@ -620,70 +619,28 @@ public class MapRenderRepositories {
// bmpLocation = null; // bmpLocation = null;
} }
// / Manipulating with multipolygons public Map<String, BinaryMapIndexReader> getMetaInfoFiles() {
return files;
}
public List<MultyPolygon> proccessMultiPolygons(Map<TagValuePair, List<BinaryMapDataObject>> multyPolygons, int leftX, int rightX, /// MULTI POLYGONS (coastline)
int bottomY, int topY, int zoom) { private List<BinaryMapDataObject> processCoastlines(List<BinaryMapDataObject> coastLines, int leftX, int rightX, int bottomY, int topY,
List<MultyPolygon> listPolygons = new ArrayList<MultyPolygon>(multyPolygons.size()); int zoom) {
List<TLongList> completedRings = new ArrayList<TLongList>(); List<TLongList> completedRings = new ArrayList<TLongList>();
List<TLongList> incompletedRings = new ArrayList<TLongList>(); List<TLongList> incompletedRings = new ArrayList<TLongList>();
List<String> completedRingNames = new ArrayList<String>(); List<BinaryMapDataObject> result = new ArrayList<BinaryMapDataObject>(coastLines.size());
List<String> incompletedRingNames = new ArrayList<String>(); MapIndex mapIndex = null;
for (TagValuePair type : multyPolygons.keySet()) {
List<BinaryMapDataObject> directList;
List<BinaryMapDataObject> 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<MultyPolygon> listPolygons,
List<TLongList> completedRings, List<TLongList> incompletedRings, List<String> completedRingNames,
List<String> incompletedRingNames, TagValuePair type, List<BinaryMapDataObject> directList,
List<BinaryMapDataObject> inverselist, int zoom) {
MultyPolygon pl = new MultyPolygon();
// delete direction last bit (to not show point)
pl.setTag(type.tag);
pl.setValue(type.value);
long dbId = 0; long dbId = 0;
for (int km = 0; km < 2; km++) { for (BinaryMapDataObject o : coastLines) {
List<BinaryMapDataObject> list = km == 0 ? directList : inverselist;
for (BinaryMapDataObject o : list) {
int len = o.getPointsLength(); int len = o.getPointsLength();
if (len < 2) { if (len < 2) {
continue; continue;
} }
mapIndex = o.getMapIndex();
dbId = o.getId() >> 1; dbId = o.getId() >> 1;
TLongList coordinates = new TLongArrayList(o.getPointsLength() / 2); TLongList coordinates = new TLongArrayList(o.getPointsLength() / 2);
int px = o.getPoint31XTile(km == 0 ? 0 : len - 1); int px = o.getPoint31XTile(0);
int py = o.getPoint31YTile(km == 0 ? 0 : len - 1); int py = o.getPoint31YTile(0);
int x = px; int x = px;
int y = py; int y = py;
boolean pinside = leftX <= x && x <= rightX && y >= topY && y <= bottomY; boolean pinside = leftX <= x && x <= rightX && y >= topY && y <= bottomY;
@ -691,13 +648,12 @@ public class MapRenderRepositories {
coordinates.add((((long) x) << 32) | ((long) y)); coordinates.add((((long) x) << 32) | ((long) y));
} }
for (int i = 1; i < len; i++) { for (int i = 1; i < len; i++) {
x = o.getPoint31XTile(km == 0 ? i : len - i - 1); x = o.getPoint31XTile(i);
y = o.getPoint31YTile(km == 0 ? i : len - i - 1); y = o.getPoint31YTile(i);
boolean inside = leftX <= x && x <= rightX && y >= topY && y <= bottomY; boolean inside = leftX <= x && x <= rightX && y >= topY && y <= bottomY;
boolean lineEnded = calculateLineCoordinates(inside, x, y, pinside, px, py, leftX, rightX, bottomY, topY, coordinates); boolean lineEnded = calculateLineCoordinates(inside, x, y, pinside, px, py, leftX, rightX, bottomY, topY, coordinates);
if (lineEnded) { if (lineEnded) {
processMultipolygonLine(completedRings, incompletedRings, completedRingNames, incompletedRingNames, coordinates, combineMultipolygonLine(completedRings, incompletedRings, coordinates);
o.getName());
// create new line if it goes outside // create new line if it goes outside
coordinates = new TLongArrayList(); coordinates = new TLongArrayList();
} }
@ -705,154 +661,63 @@ public class MapRenderRepositories {
py = y; py = y;
pinside = inside; pinside = inside;
} }
processMultipolygonLine(completedRings, incompletedRings, completedRingNames, incompletedRingNames, coordinates, combineMultipolygonLine(completedRings, incompletedRings, coordinates);
o.getName());
}
} }
if (completedRings.size() == 0 && incompletedRings.size() == 0) { if (completedRings.size() == 0 && incompletedRings.size() == 0) {
return null; return result;
} }
if (incompletedRings.size() > 0) { if (incompletedRings.size() > 0) {
unifyIncompletedRings(incompletedRings, completedRings, completedRingNames, incompletedRingNames, leftX, rightX, bottomY, topY, unifyIncompletedRings(incompletedRings, completedRings, leftX, rightX, bottomY, topY, dbId, zoom);
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; boolean clockwiseFound = false;
for (TLongList c : completedRings) { int mask = 0xffffffff;
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$
}
}
}
long[][] lns = new long[completedRings.size()][];
for (int i = 0; i < completedRings.size(); i++) { for (int i = 0; i < completedRings.size(); i++) {
TLongList ring = completedRings.get(i); TLongList ring = completedRings.get(i);
lns[i] = new long[ring.size()]; int[] coordinates = new int[ring.size() * 2];
for (int j = 0; j < lns[i].length; j++) { for (int j = 0; j < ring.size(); j++) {
lns[i][j] = ring.get(j); coordinates[j * 2] = (int) (ring.get(j) >> 32);
coordinates[j * 2 + 1] = (int) (ring.get(j) & mask);
} }
} boolean clockwise = MapAlgorithms.isClockwiseWay(ring);
pl.setNames(completedRingNames.toArray(new String[completedRings.size()])); clockwiseFound = clockwiseFound || clockwise;
pl.setLines(lns); BinaryMapDataObject o = new BinaryMapDataObject(coordinates, new int[] { clockwise ? mapIndex.coastlineEncodingType
return pl; : mapIndex.landEncodingType }, null, dbId);
o.setMapIndex(mapIndex);
o.setArea(true);
result.add(o);
} }
// Copied from MapAlgorithms for (int i = 0; i < incompletedRings.size(); i++) {
private boolean isClockwiseWay(TLongList c) { TLongList ring = incompletedRings.get(i);
if (c.size() == 0) { int[] coordinates = new int[ring.size() * 2];
return true; 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;
} }
// calculate middle Y private void combineMultipolygonLine(List<TLongList> completedRings, List<TLongList> incompletedRings, TLongList coordinates) {
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<TLongList> completedRings, List<TLongList> incompletedRings,
List<String> completedRingsNames, List<String> incompletedRingsNames, TLongList coordinates, String name) {
if (coordinates.size() > 0) { if (coordinates.size() > 0) {
if (coordinates.get(0) == coordinates.get(coordinates.size() - 1)) { if (coordinates.get(0) == coordinates.get(coordinates.size() - 1)) {
completedRings.add(coordinates); completedRings.add(coordinates);
completedRingsNames.add(name);
} else { } else {
boolean add = true; boolean add = true;
for (int k = 0; k < incompletedRings.size();) { for (int k = 0; k < incompletedRings.size();) {
boolean remove = false; boolean remove = false;
TLongList i = incompletedRings.get(k); TLongList i = incompletedRings.get(k);
String oldName = incompletedRingsNames.get(k);
if (coordinates.get(0) == i.get(i.size() - 1)) { if (coordinates.get(0) == i.get(i.size() - 1)) {
i.addAll(coordinates.subList(1, coordinates.size())); i.addAll(coordinates.subList(1, coordinates.size()));
remove = true; remove = true;
@ -863,31 +728,23 @@ public class MapRenderRepositories {
} }
if (remove) { if (remove) {
incompletedRings.remove(k); incompletedRings.remove(k);
incompletedRingsNames.remove(k);
} else { } else {
k++; k++;
} }
if (coordinates.get(0) == coordinates.get(coordinates.size() - 1)) { if (coordinates.get(0) == coordinates.get(coordinates.size() - 1)) {
completedRings.add(coordinates); completedRings.add(coordinates);
if (oldName != null) {
completedRingsNames.add(oldName);
} else {
completedRingsNames.add(name);
}
add = false; add = false;
break; break;
} }
} }
if (add) { if (add) {
incompletedRings.add(coordinates); incompletedRings.add(coordinates);
incompletedRingsNames.add(name);
} }
} }
} }
} }
private void unifyIncompletedRings(List<TLongList> incompletedRings, List<TLongList> completedRings, List<String> completedRingNames, private void unifyIncompletedRings(List<TLongList> incompletedRings, List<TLongList> completedRings, int leftX, int rightX, int bottomY, int topY, long dbId, int zoom) {
List<String> incompletedRingNames, int leftX, int rightX, int bottomY, int topY, long dbId, int zoom) {
int mask = 0xffffffff; int mask = 0xffffffff;
Set<Integer> nonvisitedRings = new LinkedHashSet<Integer>(); Set<Integer> nonvisitedRings = new LinkedHashSet<Integer>();
for (int j = 0; j < incompletedRings.size(); j++) { for (int j = 0; j < incompletedRings.size(); j++) {
@ -924,7 +781,6 @@ public class MapRenderRepositories {
} }
for (int j = 0; j < incompletedRings.size(); j++) { for (int j = 0; j < incompletedRings.size(); j++) {
TLongList i = incompletedRings.get(j); TLongList i = incompletedRings.get(j);
String name = incompletedRingNames.get(j);
if (!nonvisitedRings.contains(j)) { if (!nonvisitedRings.contains(j)) {
continue; continue;
} }
@ -1030,7 +886,6 @@ public class MapRenderRepositories {
} }
completedRings.add(i); completedRings.add(i);
completedRingNames.add(name);
} }
} }
@ -1044,6 +899,40 @@ public class MapRenderRepositories {
return res; 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 * @return -1 if there is no instersection or x<<32 | y
*/ */
@ -1129,41 +1018,5 @@ public class MapRenderRepositories {
return -1l; 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<String, BinaryMapIndexReader> getMetaInfoFiles() {
return files;
}
} }

View file

@ -15,7 +15,6 @@ import net.osmand.binary.BinaryMapDataObject;
import net.osmand.binary.BinaryMapIndexReader.TagValuePair; import net.osmand.binary.BinaryMapIndexReader.TagValuePair;
import net.osmand.data.MapTileDownloader.IMapDownloaderCallback; import net.osmand.data.MapTileDownloader.IMapDownloaderCallback;
import net.osmand.osm.MapRenderingTypes; import net.osmand.osm.MapRenderingTypes;
import net.osmand.osm.MultyPolygon;
import net.osmand.plus.render.NativeOsmandLibrary.NativeSearchResult; import net.osmand.plus.render.NativeOsmandLibrary.NativeSearchResult;
import net.osmand.plus.render.TextRenderer.TextDrawInfo; import net.osmand.plus.render.TextRenderer.TextDrawInfo;
import net.osmand.render.RenderingRuleProperty; import net.osmand.render.RenderingRuleProperty;
@ -389,21 +388,7 @@ public class OsmandRenderer {
for (int i = 0; i < sz; i++) { for (int i = 0; i < sz; i++) {
BinaryMapDataObject o = objects.get(i); BinaryMapDataObject o = objects.get(i);
int sh = i << 8; 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++) { for (int j = 0; j < o.getTypes().length; j++) {
// put(orderMap, BinaryMapDataObject.getOrder(o.getTypes()[j]), sh + j, init); // put(orderMap, BinaryMapDataObject.getOrder(o.getTypes()[j]), sh + j, init);
int wholeType = o.getTypes()[j]; int wholeType = o.getTypes()[j];
@ -429,7 +414,6 @@ public class OsmandRenderer {
render.clearValue(render.ALL.R_SHADOW_LEVEL); render.clearValue(render.ALL.R_SHADOW_LEVEL);
} }
} }
}
} }
} }
@ -452,12 +436,6 @@ public class OsmandRenderer {
protected void drawObj(BinaryMapDataObject obj, RenderingRuleSearchRequest render, Canvas canvas, RenderingContext rc, int l, protected void drawObj(BinaryMapDataObject obj, RenderingRuleSearchRequest render, Canvas canvas, RenderingContext rc, int l,
boolean renderText, boolean drawOnlyShadow) { boolean renderText, boolean drawOnlyShadow) {
rc.allObjects++; rc.allObjects++;
if (obj instanceof MultyPolygon) {
// TODO
if(!drawOnlyShadow){
drawMultiPolygon(obj, render, canvas, rc);
}
} else {
int type = obj.getObjectType(); int type = obj.getObjectType();
TagValuePair pair = obj.getMapIndex().decodeType(obj.getTypes()[l]); TagValuePair pair = obj.getMapIndex().decodeType(obj.getTypes()[l]);
if (type == MapRenderingTypes.POINT_TYPE && !drawOnlyShadow) { if (type == MapRenderingTypes.POINT_TYPE && !drawOnlyShadow) {
@ -468,7 +446,6 @@ public class OsmandRenderer {
drawPolygon(obj, render, canvas, rc, pair); drawPolygon(obj, render, canvas, rc, pair);
} }
} }
}
private PointF calcPoint(int xt, int yt, RenderingContext rc){ private PointF calcPoint(int xt, int yt, RenderingContext rc){
@ -491,70 +468,11 @@ public class OsmandRenderer {
return calcPoint(o.getPoint31XTile(ind), o.getPoint31YTile(ind), rc); 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(){ public void clearCachedResources(){
shaders.clear(); 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) { private void drawPolygon(BinaryMapDataObject obj, RenderingRuleSearchRequest render, Canvas canvas, RenderingContext rc, TagValuePair pair) {
if(render == null || pair == null){ if(render == null || pair == null){
return; return;