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

View file

@ -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)){

View file

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

View file

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

View file

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

View file

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

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

@ -198,7 +198,8 @@
<filter layer="1" tag="highway" value="bridleway" order="67"/>
<filter appMode="pedestrian" tag="highway" value="bridleway" order="59"/>
<filter tag="highway" value="bridleway" order="35"/>
<filter tag="natural" value="coastline_broken" order="4"/>
</group>
<group objectType="3" point="false">
@ -232,6 +233,7 @@
<filter tag="natural" value="water" 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="" order="1"/>
</group>
@ -822,7 +824,7 @@
<!-- Water, night color 330099 -->
<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="lake"/>
<filter minzoom="4" tag="natural" value="bay"/>
@ -847,9 +849,15 @@
</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 -->
<group>
<filter minzoom="10" color="#f2efe9" tag="natural" value="land"/>
<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="farmland"/>
@ -944,12 +952,13 @@
<filter minzoom="9" shader="glacier" color="#100000" color_2="#99ccff" strokeWidth_2="2" tag="natural" value="glacier"/>
<filter minzoom="10" shader="danger" color="#100000" tag="military" value="danger_area"/>
<filter minzoom="9" shader="nr2" color="#abdf96" color_2="#66cc33" strokeWidth_2="1" tag="leisure" value="nature_reserve"/>
<groupFilter noPolygons="true" color="#00ffffff" color_2="#00ffffff" shader=""/>
<groupFilter noPolygons="true" color="#00ffffff" color_2="#00ffffff" shader=""/>
</group>
</polygon>
<line>
<filter minzoom="1" tag="natural" value="coastline_broken" color="#000000" strokeWidth_2="1.5"/>
<group>
<filter hmRendered="true" tag="highway" value="motorway" maxzoom="13" color="#809bff"/>
<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="build_installed">Build {0} successfully installed ({1}).</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">Loading OsmAnd builds&#8230;</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_tu">Make U-turn and go</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_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>

View file

@ -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<BinaryMapDataObject> tempList = new ArrayList<BinaryMapDataObject>();
System.gc(); // to clear previous objects
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 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<BinaryMapDataObject>());
}
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<MultyPolygon> pMulti = proccessMultiPolygons(multiPolygons, leftX, rightX, bottomY, topY, zoom);
tempList.addAll(pMulti);
if(!coastLines.isEmpty()) {
List<BinaryMapDataObject> 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<String, BinaryMapIndexReader> getMetaInfoFiles() {
return files;
}
// / Manipulating with multipolygons
public List<MultyPolygon> proccessMultiPolygons(Map<TagValuePair, List<BinaryMapDataObject>> multyPolygons, int leftX, int rightX,
int bottomY, int topY, int zoom) {
List<MultyPolygon> listPolygons = new ArrayList<MultyPolygon>(multyPolygons.size());
/// MULTI POLYGONS (coastline)
private List<BinaryMapDataObject> processCoastlines(List<BinaryMapDataObject> coastLines, int leftX, int rightX, int bottomY, int topY,
int zoom) {
List<TLongList> completedRings = new ArrayList<TLongList>();
List<TLongList> incompletedRings = new ArrayList<TLongList>();
List<String> completedRingNames = new ArrayList<String>();
List<String> incompletedRingNames = new ArrayList<String>();
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);
List<BinaryMapDataObject> result = new ArrayList<BinaryMapDataObject>(coastLines.size());
MapIndex mapIndex = null;
long dbId = 0;
for (int km = 0; km < 2; km++) {
List<BinaryMapDataObject> 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<TLongList> completedRings, List<TLongList> incompletedRings,
List<String> completedRingsNames, List<String> incompletedRingsNames, TLongList coordinates, String name) {
private void combineMultipolygonLine(List<TLongList> completedRings, List<TLongList> 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<TLongList> incompletedRings, List<TLongList> completedRings, List<String> completedRingNames,
List<String> incompletedRingNames, int leftX, int rightX, int bottomY, int topY, long dbId, int zoom) {
private void unifyIncompletedRings(List<TLongList> incompletedRings, List<TLongList> completedRings, int leftX, int rightX, int bottomY, int topY, long dbId, int zoom) {
int mask = 0xffffffff;
Set<Integer> nonvisitedRings = new LinkedHashSet<Integer>();
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<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.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<TIntArrayList> orderMap = new TIntObjectHashMap<TIntArrayList>();
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;