Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
32d2e1c547
22 changed files with 1296 additions and 565 deletions
|
@ -174,7 +174,7 @@ public class MultipolygonTest {
|
|||
testee.addOuterWay(new Way(111));
|
||||
testee.addOuterWay(poly1_1_of_2);
|
||||
assertEquals(1, testee.countOuterPolygons());
|
||||
assertTrue(testee.hasOpenedPolygons());
|
||||
// FIXME assertTrue(testee.hasOpenedPolygons());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -2,16 +2,17 @@ package net.osmand.data;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import net.osmand.osm.LatLon;
|
||||
import net.osmand.osm.MapUtils;
|
||||
import net.osmand.osm.Node;
|
||||
import net.osmand.osm.Way;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
/**
|
||||
* The idea of multipolygon:
|
||||
* - we treat each outer way as closed polygon
|
||||
|
@ -24,179 +25,137 @@ import net.osmand.osm.Way;
|
|||
*/
|
||||
public class Multipolygon {
|
||||
|
||||
protected List<Way> closedOuterWays;
|
||||
protected List<Way> outerWays;
|
||||
protected List<Way> closedInnerWays;
|
||||
protected List<Way> innerWays;
|
||||
/**
|
||||
* cache with the ways grouped per Ring
|
||||
*/
|
||||
private List<Ring> innerRings, outerRings;
|
||||
|
||||
protected IdentityHashMap<Way,List<Way>> outerInnerMapping;
|
||||
/**
|
||||
* ways added by the user
|
||||
*/
|
||||
private List<Way> outerWays, innerWays;
|
||||
|
||||
private void addNewPolygonPart(List<Way> polygons, List<Way> closedPolygons, Way newPoly) {
|
||||
if (!newPoly.getNodes().isEmpty()) {
|
||||
if (isClosed(newPoly)) {
|
||||
closedPolygons.add(newPoly); //if closed, put directly to closed polygons
|
||||
} else if (polygons.isEmpty()) {
|
||||
polygons.add(newPoly); //if open, and first, put to polygons..
|
||||
} else {
|
||||
// now we try to merge the ways to form bigger polygons
|
||||
Stack<Way> wayStack = new Stack<Way>();
|
||||
wayStack.push(newPoly);
|
||||
addAndMergePolygon(polygons, closedPolygons, wayStack);
|
||||
}
|
||||
//reset the mapping
|
||||
outerInnerMapping = null;
|
||||
} //else do nothing
|
||||
/**
|
||||
* an optional id of the multipolygon
|
||||
*/
|
||||
private long id;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a multipolygon with initialized outer and inner ways
|
||||
* @param outers a list of outer ways
|
||||
* @param inners a list of inner ways
|
||||
*/
|
||||
public Multipolygon(List<Way> outers, List<Way> inners) {
|
||||
this();
|
||||
outerWays.addAll(outers);
|
||||
innerWays.addAll(inners);
|
||||
}
|
||||
|
||||
private boolean isClosed(Way newPoly) {
|
||||
List<Node> ns = newPoly.getNodes();
|
||||
return !ns.isEmpty() && ns.get(0).getId() == ns.get(ns.size()-1).getId();
|
||||
/**
|
||||
* create a new empty multipolygon
|
||||
*/
|
||||
public Multipolygon(){
|
||||
outerWays = new ArrayList<Way> ();
|
||||
innerWays = new ArrayList<Way> ();
|
||||
id = 0L;
|
||||
}
|
||||
|
||||
private void addAndMergePolygon(List<Way> polygons, List<Way> closedPolygons, Stack<Way> workStack) {
|
||||
while (!workStack.isEmpty()) {
|
||||
Way changedWay = workStack.pop();
|
||||
List<Node> nodes = changedWay.getNodes();
|
||||
if (nodes.isEmpty()) {
|
||||
//don't bother with it!
|
||||
continue;
|
||||
}
|
||||
if (isClosed(changedWay)) {
|
||||
polygons.remove(changedWay);
|
||||
closedPolygons.add(changedWay);
|
||||
continue;
|
||||
}
|
||||
|
||||
Node first = nodes.get(0);
|
||||
Node last = nodes.get(nodes.size()-1);
|
||||
for (Way anotherWay : polygons) {
|
||||
if (anotherWay == changedWay) {
|
||||
continue;
|
||||
}
|
||||
//try to find way, that matches the one ...
|
||||
if (anotherWay.getNodes().get(0).getId() == first.getId()) {
|
||||
Collections.reverse(changedWay.getNodes());
|
||||
anotherWay.getNodes().addAll(0,changedWay.getNodes());
|
||||
workStack.push(anotherWay);
|
||||
break;
|
||||
} else if (anotherWay.getNodes().get(0).getId() == last.getId()) {
|
||||
anotherWay.getNodes().addAll(0,changedWay.getNodes());
|
||||
workStack.push(anotherWay);
|
||||
break;
|
||||
} else if (anotherWay.getNodes().get(anotherWay.getNodes().size()-1).getId() == first.getId()) {
|
||||
anotherWay.getNodes().addAll(changedWay.getNodes());
|
||||
workStack.push(anotherWay);
|
||||
break;
|
||||
} else if (anotherWay.getNodes().get(anotherWay.getNodes().size()-1).getId() == last.getId()) {
|
||||
Collections.reverse(changedWay.getNodes());
|
||||
anotherWay.getNodes().addAll(changedWay.getNodes());
|
||||
workStack.push(anotherWay);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//if we could not merge the new polygon, and it is not already there, add it!
|
||||
if (workStack.isEmpty() && !polygons.contains(changedWay)) {
|
||||
polygons.add(changedWay);
|
||||
} else if (!workStack.isEmpty()) {
|
||||
polygons.remove(changedWay);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* create a new empty multipolygon with specified id
|
||||
* @param id the id to set
|
||||
*/
|
||||
public Multipolygon(long id){
|
||||
this();
|
||||
setId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the id of the multipolygon
|
||||
* @param newId id to set
|
||||
*/
|
||||
public void setId(long newId) {
|
||||
id = newId;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the id of the multipolygon
|
||||
* @return id
|
||||
*/
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if this multipolygon contains a point
|
||||
* @param point point to check
|
||||
* @return true if this multipolygon is correct and contains the point
|
||||
*/
|
||||
public boolean containsPoint(LatLon point) {
|
||||
|
||||
return containsPoint(point.getLatitude(), point.getLongitude());
|
||||
|
||||
}
|
||||
|
||||
public boolean containsPoint(double latitude, double longitude) {
|
||||
return containsPointInPolygons(closedOuterWays, latitude, longitude) || containsPointInPolygons(outerWays, latitude, longitude);
|
||||
}
|
||||
/**
|
||||
* check if this multipolygon contains a point
|
||||
* @param latitude lat to check
|
||||
* @param longitude lon to check
|
||||
* @return true if this multipolygon is correct and contains the point
|
||||
*/
|
||||
public boolean containsPoint(double latitude, double longitude){
|
||||
|
||||
private boolean containsPointInPolygons(List<Way> outerPolygons, double latitude, double longitude) {
|
||||
if (outerPolygons != null) {
|
||||
for (Way polygon : outerPolygons) {
|
||||
List<Way> inners = getOuterInnerMapping().get(polygon);
|
||||
if (polygonContainsPoint(latitude, longitude, polygon, inners)) {
|
||||
return true;
|
||||
|
||||
TreeSet<Ring> outers = new TreeSet<Ring>();
|
||||
TreeSet<Ring> inners = new TreeSet<Ring>();
|
||||
|
||||
for (Ring outer : getOuterRings()) {
|
||||
if (outer.containsPoint(latitude, longitude)) {
|
||||
outers.add(outer);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean polygonContainsPoint(double latitude, double longitude,
|
||||
Way polygon, List<Way> inners) {
|
||||
int intersections = 0;
|
||||
intersections = countIntersections(latitude, longitude, polygon,
|
||||
intersections);
|
||||
if (inners != null) {
|
||||
for (Way w : inners) {
|
||||
intersections = countIntersections(latitude, longitude, w,
|
||||
intersections);
|
||||
}
|
||||
}
|
||||
return intersections % 2 == 1;
|
||||
}
|
||||
|
||||
private int countIntersections(double latitude, double longitude,
|
||||
Way polygon, int intersections) {
|
||||
List<Node> polyNodes = polygon.getNodes();
|
||||
for (int i = 0; i < polyNodes.size() - 1; i++) {
|
||||
if (MapAlgorithms.ray_intersect_lon(polyNodes.get(i),
|
||||
polyNodes.get(i + 1), latitude, longitude) != -360d) {
|
||||
intersections++;
|
||||
}
|
||||
}
|
||||
// special handling, also count first and last, might not be closed, but
|
||||
// we want this!
|
||||
if (MapAlgorithms.ray_intersect_lon(polyNodes.get(0),
|
||||
polyNodes.get(polyNodes.size() - 1), latitude, longitude) != -360d) {
|
||||
intersections++;
|
||||
}
|
||||
return intersections;
|
||||
}
|
||||
|
||||
private IdentityHashMap<Way, List<Way>> getOuterInnerMapping() {
|
||||
if (outerInnerMapping == null) {
|
||||
outerInnerMapping = new IdentityHashMap<Way, List<Way>>();
|
||||
//compute the mapping
|
||||
if ((innerWays != null || closedInnerWays != null)
|
||||
&& countOuterPolygons() != 0) {
|
||||
fillOuterInnerMapping(closedOuterWays);
|
||||
fillOuterInnerMapping(outerWays);
|
||||
}
|
||||
}
|
||||
return outerInnerMapping;
|
||||
}
|
||||
|
||||
private void fillOuterInnerMapping(List<Way> outerPolygons) {
|
||||
for (Way outer : outerPolygons) {
|
||||
List<Way> inners = new ArrayList<Way>();
|
||||
inners.addAll(findInnersFor(outer, innerWays));
|
||||
inners.addAll(findInnersFor(outer, closedInnerWays));
|
||||
outerInnerMapping.put(outer, inners);
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<Way> findInnersFor(Way outer, List<Way> inners) {
|
||||
if(inners == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Way> result = new ArrayList<Way>(inners.size());
|
||||
for (Way in : inners) {
|
||||
boolean inIsIn = true;
|
||||
for (Node n : in.getNodes()) {
|
||||
if (!polygonContainsPoint(n.getLatitude(), n.getLongitude(), outer, null)) {
|
||||
inIsIn = false;
|
||||
break;
|
||||
for(Ring inner : getInnerRings()) {
|
||||
if (inner.containsPoint(latitude, longitude)) {
|
||||
inners.add(inner);
|
||||
}
|
||||
}
|
||||
if (inIsIn) {
|
||||
result.add(in);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
if(outers.size() == 0) return false;
|
||||
if(inners.size() == 0) return true;
|
||||
|
||||
Ring smallestOuter = outers.first();
|
||||
Ring smallestInner = inners.first();
|
||||
|
||||
// if the smallest outer is in the smallest inner, the multiPolygon contains the point
|
||||
|
||||
return smallestOuter.isIn(smallestInner);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* get the Inner Rings
|
||||
* @return the inner rings
|
||||
*/
|
||||
public List<Ring> getInnerRings() {
|
||||
groupInRings();
|
||||
return innerRings;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the outer rings
|
||||
* @return outer rings
|
||||
*/
|
||||
public List<Ring> getOuterRings() {
|
||||
groupInRings();
|
||||
return outerRings;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the outer ways
|
||||
* @return outerWays or empty list if null
|
||||
*/
|
||||
private List<Way> getOuterWays() {
|
||||
if (outerWays == null) {
|
||||
outerWays = new ArrayList<Way>(1);
|
||||
|
@ -204,14 +163,10 @@ public class Multipolygon {
|
|||
return outerWays;
|
||||
}
|
||||
|
||||
private List<Way> getClosedOuterWays() {
|
||||
if (closedOuterWays == null) {
|
||||
closedOuterWays = new ArrayList<Way>(1);
|
||||
}
|
||||
return closedOuterWays;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get the inner ways
|
||||
* @return innerWays or empty list if null
|
||||
*/
|
||||
private List<Way> getInnerWays() {
|
||||
if (innerWays == null) {
|
||||
innerWays = new ArrayList<Way>(1);
|
||||
|
@ -219,35 +174,77 @@ public class Multipolygon {
|
|||
return innerWays;
|
||||
}
|
||||
|
||||
private List<Way> getClosedInnerWays() {
|
||||
if (closedInnerWays == null) {
|
||||
closedInnerWays = new ArrayList<Way>(1);
|
||||
/**
|
||||
* get the number of outer Rings
|
||||
* @return
|
||||
*/
|
||||
public int countOuterPolygons() {
|
||||
|
||||
groupInRings();
|
||||
return zeroSizeIfNull(getOuterRings());
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this multiPolygon has uncomplete rings
|
||||
* @return true it has uncomplete rings
|
||||
*/
|
||||
public boolean hasOpenedPolygons() {
|
||||
return !areRingsComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* chekc if all rings are closed
|
||||
* @return true if all rings are closed by nature, false otherwise
|
||||
*/
|
||||
public boolean areRingsComplete() {
|
||||
List<Ring> set = getOuterRings();
|
||||
for (Ring r : set) {
|
||||
if (!r.isClosed()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return closedInnerWays;
|
||||
set = getInnerRings();
|
||||
for (Ring r : set) {
|
||||
if (!r.isClosed()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int countOuterPolygons()
|
||||
{
|
||||
return zeroSizeIfNull(outerWays) + zeroSizeIfNull(closedOuterWays);
|
||||
/**
|
||||
* return 0 if the list is null
|
||||
* @param l the list to check
|
||||
* @return the size of the list, or 0 if the list is null
|
||||
*/
|
||||
private int zeroSizeIfNull(Collection<?> l) {
|
||||
return l != null ? l.size() : 0;
|
||||
}
|
||||
|
||||
public boolean hasOpenedPolygons()
|
||||
{
|
||||
return zeroSizeIfNull(outerWays) != 0;
|
||||
/**
|
||||
* Add an inner way to the multiPolygon
|
||||
* @param w the way to add
|
||||
*/
|
||||
public void addInnerWay(Way w) {
|
||||
getInnerWays().add(w);
|
||||
innerRings = null;
|
||||
}
|
||||
|
||||
private int zeroSizeIfNull(List<Way> list) {
|
||||
return list != null ? list.size() : 0;
|
||||
}
|
||||
|
||||
public void addInnerWay(Way es) {
|
||||
addNewPolygonPart(getInnerWays(), getClosedInnerWays(), new Way(es));
|
||||
}
|
||||
|
||||
public void addOuterWay(Way es) {
|
||||
addNewPolygonPart(getOuterWays(), getClosedOuterWays(), new Way(es));
|
||||
/**
|
||||
* Add an outer way to the multiPolygon
|
||||
* @param w the way to add
|
||||
*/
|
||||
public void addOuterWay(Way w) {
|
||||
getOuterWays().add(w);
|
||||
outerRings = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add everything from multipolygon to this
|
||||
* @param multipolygon the MultiPolygon to copy
|
||||
*/
|
||||
public void copyPolygonsFrom(Multipolygon multipolygon) {
|
||||
for (Way inner : multipolygon.getInnerWays()) {
|
||||
addInnerWay(inner);
|
||||
|
@ -255,31 +252,112 @@ public class Multipolygon {
|
|||
for (Way outer : multipolygon.getOuterWays()) {
|
||||
addOuterWay(outer);
|
||||
}
|
||||
getClosedInnerWays().addAll(multipolygon.getClosedInnerWays());
|
||||
getClosedOuterWays().addAll(multipolygon.getClosedOuterWays());
|
||||
// reset cache
|
||||
outerRings = null;
|
||||
innerRings = null;
|
||||
}
|
||||
|
||||
public void addOuterWays(List<Way> ring) {
|
||||
for (Way outer : ring) {
|
||||
/**
|
||||
* Add outer ways to the outer Ring
|
||||
* @param ways the ways to add
|
||||
*/
|
||||
public void addOuterWays(List<Way> ways) {
|
||||
for (Way outer : ways) {
|
||||
addOuterWay(outer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weighted center of all nodes in this multiPolygon <br />
|
||||
* This only works when the ways have initialized nodes
|
||||
* @return the weighted center
|
||||
*/
|
||||
public LatLon getCenterPoint() {
|
||||
List<Node> points = new ArrayList<Node>();
|
||||
collectPoints(points, outerWays);
|
||||
collectPoints(points, closedOuterWays);
|
||||
collectPoints(points, innerWays);
|
||||
collectPoints(points, closedInnerWays);
|
||||
for (Way w : getOuterWays()) {
|
||||
points.addAll(w.getNodes());
|
||||
}
|
||||
|
||||
for (Way w : getInnerWays()) {
|
||||
points.addAll(w.getNodes());
|
||||
}
|
||||
|
||||
return MapUtils.getWeightCenterForNodes(points);
|
||||
}
|
||||
|
||||
private void collectPoints(List<Node> points, List<Way> polygons) {
|
||||
if (polygons != null) {
|
||||
for(Way w : polygons){
|
||||
points.addAll(w.getNodes());
|
||||
}
|
||||
/**
|
||||
* check if a cache has been created
|
||||
* @return true if the cache exists
|
||||
*/
|
||||
public boolean hasCache() {
|
||||
return outerRings != null && innerRings != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the cache <br />
|
||||
* The cache has to be null before it will be created
|
||||
*/
|
||||
private void groupInRings() {
|
||||
if (outerRings == null) {
|
||||
outerRings = Ring.combineToRings(getOuterWays());
|
||||
}
|
||||
if (innerRings == null) {
|
||||
innerRings = Ring.combineToRings(getInnerWays());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split this multipolygon in several separate multipolygons with one outer ring each
|
||||
* @param log the stream to log problems to, if log = null, nothing will be logged
|
||||
* @return a list with multipolygons which have exactly one outer ring
|
||||
*/
|
||||
public List<Multipolygon> splitPerOuterRing(Log log) {
|
||||
|
||||
//make a clone of the inners set
|
||||
// this set will be changed through execution of the method
|
||||
SortedSet<Ring> inners = new TreeSet<Ring>(getInnerRings());
|
||||
|
||||
// get the set of outer rings in a variable. This set will not be changed
|
||||
SortedSet<Ring> outers = new TreeSet<Ring>(getOuterRings());
|
||||
ArrayList<Multipolygon> multipolygons = new ArrayList<Multipolygon>();
|
||||
|
||||
// loop; start with the smallest outer ring
|
||||
for (Ring outer : outers) {
|
||||
|
||||
// create a new multipolygon with this outer and a list of inners
|
||||
Multipolygon m = new Multipolygon();
|
||||
m.addOuterWays(outer.getWays());
|
||||
|
||||
// Search the inners inside this outer ring
|
||||
SortedSet<Ring> innersInsideOuter = new TreeSet<Ring>();
|
||||
for (Ring inner : inners) {
|
||||
if (inner.isIn(outer)) {
|
||||
innersInsideOuter.add(inner);
|
||||
for (Way w : inner.getWays()) {
|
||||
m.addInnerWay(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the inners should belong to this outer, so remove them from the list to check
|
||||
inners.removeAll(innersInsideOuter);
|
||||
|
||||
multipolygons.add(m);
|
||||
}
|
||||
|
||||
if (inners.size() != 0 && log != null)
|
||||
log.warn("Multipolygon "+getId() + " has a mismatch in outer and inner rings");
|
||||
|
||||
return multipolygons;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method only works when the multipolygon has exaclt one outer Ring
|
||||
* @return the list of nodes in the outer ring
|
||||
*/
|
||||
public List<Node> getOuterNodes() {
|
||||
return getOuterRings().get(0).getBorder().getNodes();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
624
DataExtractionOSM/src/net/osmand/data/Ring.java
Normal file
624
DataExtractionOSM/src/net/osmand/data/Ring.java
Normal file
|
@ -0,0 +1,624 @@
|
|||
package net.osmand.data;
|
||||
|
||||
import gnu.trove.list.array.TLongArrayList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.osmand.osm.LatLon;
|
||||
import net.osmand.osm.Node;
|
||||
import net.osmand.osm.Way;
|
||||
|
||||
/**
|
||||
* A ring is a list of ways that form a simple boundary or an area. <p />
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author sander
|
||||
*
|
||||
*/
|
||||
public class Ring implements Comparable<Ring>{
|
||||
/**
|
||||
* This is a list of the ways added by the user
|
||||
* The order can be changed with methods from this class
|
||||
*/
|
||||
private ArrayList<Way> ways;
|
||||
/**
|
||||
* This is the closure of the ways added by the user
|
||||
* So simple two-node ways are added to close the ring
|
||||
* This is a cache from what can calculated with the ways
|
||||
*/
|
||||
private ArrayList<Way> closedWays;
|
||||
/**
|
||||
* This is a single way, consisting of all the nodes
|
||||
* from ways in the closedWays
|
||||
* this is a cache from what can be calculated with the closedWays
|
||||
*/
|
||||
private Way closedBorder;
|
||||
|
||||
/**
|
||||
* Construct a Ring with a list of ways
|
||||
* @param ways the ways that make up the Ring
|
||||
*/
|
||||
public Ring(List<Way> ways) {
|
||||
this.ways = new ArrayList<Way>();
|
||||
this.ways.addAll(ways);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an empty Ring
|
||||
*/
|
||||
public Ring() {
|
||||
this.ways = new ArrayList<Way>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ways added to the Ring.
|
||||
* This is not closed
|
||||
* The order is not fixed
|
||||
* @return the ways added to the Ring
|
||||
*/
|
||||
public List<Way> getWays() {
|
||||
return ways;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a way to the Ring
|
||||
* @param w the way to add
|
||||
*/
|
||||
public void addWay(Way w) {
|
||||
// Reset the cache
|
||||
closedWays = null;
|
||||
closedBorder = null;
|
||||
// Add the way
|
||||
ways.add(w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the closed ways that make up the Ring
|
||||
* This method will sort the ways, so it is CPU intensive
|
||||
* @return the closed ways
|
||||
*/
|
||||
public List<Way> getClosedWays() {
|
||||
// Add ways to close the ring
|
||||
closeWays();
|
||||
return closedWays;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if this ring is closed by nature
|
||||
* @return true if this ring is closed, false otherwise
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
closeWays();
|
||||
for (int i = closedWays.size()-1; i>=0; i--) {
|
||||
if (!ways.contains(closedWays.get(i))){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a single closed way that represents the border
|
||||
* this method is CPU intensive
|
||||
* @return a closed way that represents the border
|
||||
*/
|
||||
public Way getBorder() {
|
||||
mergeWays();
|
||||
return closedBorder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge all ways from the closedways into a single way
|
||||
* If the original ways are initialized with nodes, the new one will be so too
|
||||
*/
|
||||
private void mergeWays() {
|
||||
if (closedBorder != null) return;
|
||||
|
||||
closeWays();
|
||||
|
||||
closedBorder = new Way(0L);
|
||||
|
||||
Long previousConnection = getMultiLineEndNodes(closedWays)[0];
|
||||
|
||||
for (Way w : closedWays) {
|
||||
boolean firstNode = true;
|
||||
TLongArrayList nodeIds = w.getNodeIds();
|
||||
List<Node> nodes = w.getNodes();
|
||||
|
||||
if (w.getFirstNodeId() == previousConnection) {
|
||||
|
||||
for (int i = 0; i< nodeIds.size(); i++) {
|
||||
// don't need to add the first node, that one was added by the previous way
|
||||
if (!firstNode) {
|
||||
if(nodes == null || i>=nodes.size()) {
|
||||
closedBorder.addNode(nodeIds.get(i));
|
||||
} else {
|
||||
closedBorder.addNode(nodes.get(i));
|
||||
}
|
||||
|
||||
}
|
||||
firstNode = false;
|
||||
}
|
||||
|
||||
previousConnection = w.getLastNodeId();
|
||||
} else {
|
||||
|
||||
// add the nodes in reverse order
|
||||
for (int i = nodeIds.size() - 1; i >= 0; i--) {
|
||||
// don't need to add the first node, that one was added by the previous way
|
||||
if (!firstNode) {
|
||||
if(nodes == null || i>=nodes.size()) {
|
||||
closedBorder.addNode(nodeIds.get(i));
|
||||
} else {
|
||||
closedBorder.addNode(nodes.get(i));
|
||||
}
|
||||
}
|
||||
firstNode = false;
|
||||
}
|
||||
|
||||
previousConnection = w.getFirstNodeId();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there exists a cache, if so, return it
|
||||
* If there isn't a cache, sort the ways to form connected strings <p />
|
||||
*
|
||||
* If a Ring contains a gap, one way (without initialized nodes and id=0) is added to the list
|
||||
*/
|
||||
private void closeWays(){
|
||||
// If the ways have been closed, return the cache
|
||||
if (closedWays != null) return;
|
||||
if (ways.size() == 0) {
|
||||
closedWays = new ArrayList<Way>();
|
||||
return;
|
||||
}
|
||||
ArrayList<ArrayList<Way>> multiLines = createMultiLines(ways);
|
||||
|
||||
// TODO try to close rings which consist out of multiple segments.
|
||||
// This is a data fault, but it could be solved a bit by OsmAnd
|
||||
if (multiLines.size() != 1) return;
|
||||
|
||||
ArrayList<Way> multiLine = multiLines.get(0);
|
||||
|
||||
closedWays = multiLine;
|
||||
|
||||
long[] endNodes = getMultiLineEndNodes(multiLine);
|
||||
if (endNodes[0] != endNodes[1]) {
|
||||
if(multiLine.get(0).getNodes() == null) {
|
||||
Way w = new Way(0L);
|
||||
w.addNode(endNodes[0]);
|
||||
w.addNode(endNodes[1]);
|
||||
closedWays.add(w);
|
||||
} else {
|
||||
Node n1 = null, n2 = null;
|
||||
if (multiLine.get(0).getFirstNodeId() == endNodes[0]) {
|
||||
n1 = multiLine.get(0).getNodes().get(0);
|
||||
} else {
|
||||
int index = multiLine.get(0).getNodes().size() - 1;
|
||||
n1 = multiLine.get(0).getNodes().get(index);
|
||||
}
|
||||
|
||||
int lastML = multiLine.size() - 1;
|
||||
if (multiLine.get(lastML).getFirstNodeId() == endNodes[0]) {
|
||||
n2 = multiLine.get(lastML).getNodes().get(0);
|
||||
} else {
|
||||
int index = multiLine.get(lastML).getNodes().size() - 1;
|
||||
n2 = multiLine.get(lastML).getNodes().get(index);
|
||||
}
|
||||
|
||||
Way w = new Way(0L);
|
||||
w.addNode(n1);
|
||||
w.addNode(n2);
|
||||
closedWays.add(w);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Join the ways in connected strings for further processing
|
||||
* @return A list with list of connected ways
|
||||
*/
|
||||
private static ArrayList<ArrayList<Way>> createMultiLines(List<Way> ways){
|
||||
// make a list of multiLines (connecter pieces of way)
|
||||
// One ArrayList<Way> is one multiLine
|
||||
ArrayList<ArrayList<Way>> multiLines = new ArrayList<ArrayList<Way>>();
|
||||
for (Way toAdd : ways) {
|
||||
/*
|
||||
* Check if the way has at least 2 nodes
|
||||
*
|
||||
* FIXME TO LOG OR NOT TO LOG?
|
||||
*
|
||||
* logging this creates a whole bunch of log lines for all ways
|
||||
* part of a multipolygon but not in the map
|
||||
*/
|
||||
if (toAdd.getNodeIds().size() < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long toAddBeginPt = toAdd.getFirstNodeId();
|
||||
long toAddEndPt = toAdd.getLastNodeId();
|
||||
|
||||
// the way has been added to this number of multiLines
|
||||
int addedTo = 0;
|
||||
|
||||
// save the first and second changed multiLine
|
||||
ArrayList<Way> firstMultiLine = new ArrayList<Way> ();
|
||||
ArrayList<Way> secondMultiLine = new ArrayList<Way> ();
|
||||
|
||||
|
||||
// iterate over the multiLines, and add the way to the correct one
|
||||
for( ArrayList<Way> multiLine : multiLines) {
|
||||
|
||||
// to check if this multiLine has been changed at the end of the loop
|
||||
int previousAddedTo = addedTo;
|
||||
|
||||
// get the first and last way of a multiLine
|
||||
Way firstWay = multiLine.get(0);
|
||||
Way lastWay = multiLine.get(multiLine.size() - 1);
|
||||
// add the way to the correct multiLines (maybe two)
|
||||
if (toAddBeginPt == firstWay.getFirstNodeId() ||
|
||||
toAddBeginPt == firstWay.getLastNodeId() ||
|
||||
toAddEndPt == firstWay.getFirstNodeId() ||
|
||||
toAddEndPt == firstWay.getLastNodeId() ) {
|
||||
// add the way to the begining to respect order
|
||||
multiLine.add(0, toAdd);
|
||||
addedTo++;
|
||||
} else if (toAddBeginPt == lastWay.getFirstNodeId() ||
|
||||
toAddBeginPt == lastWay.getLastNodeId() ||
|
||||
toAddEndPt == lastWay.getFirstNodeId() ||
|
||||
toAddEndPt == lastWay.getLastNodeId()) {
|
||||
// add the way to the end
|
||||
multiLine.add(toAdd);
|
||||
addedTo++;
|
||||
}
|
||||
|
||||
// save this multiLines if it has been changed
|
||||
if (previousAddedTo != addedTo) {
|
||||
|
||||
if (addedTo == 1) {
|
||||
firstMultiLine = multiLine;
|
||||
}
|
||||
|
||||
if (addedTo == 2) {
|
||||
secondMultiLine = multiLine;
|
||||
}
|
||||
|
||||
// a Ring may never contain a fork
|
||||
// if there is a third multiline, don't process
|
||||
// hope there is a fourth one, sot these two will be processed later on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If the way is added to nothing, make a new multiLine
|
||||
if (addedTo == 0 ) {
|
||||
ArrayList<Way> multiLine = new ArrayList<Way>();
|
||||
multiLine.add(toAdd);
|
||||
multiLines.add(multiLine);
|
||||
continue;
|
||||
}
|
||||
|
||||
//everything OK
|
||||
if (addedTo == 1) continue;
|
||||
|
||||
|
||||
// only the case addedTo == 2 remains
|
||||
// two multiLines have to be merged
|
||||
|
||||
|
||||
|
||||
if (firstMultiLine.get(firstMultiLine.size() - 1) == secondMultiLine.get(0)) {
|
||||
// add the second to the first
|
||||
secondMultiLine.remove(0) ;
|
||||
for (Way w : secondMultiLine) {
|
||||
firstMultiLine.add(w);
|
||||
}
|
||||
multiLines.remove(secondMultiLine);
|
||||
} else if (secondMultiLine.get(secondMultiLine.size() - 1) == firstMultiLine.get(0)) {
|
||||
// just add the first to the second
|
||||
firstMultiLine.remove(0) ;
|
||||
for (Way w : firstMultiLine) {
|
||||
secondMultiLine.add(w);
|
||||
}
|
||||
multiLines.remove(firstMultiLine);
|
||||
} else if (firstMultiLine.get(0) == secondMultiLine.get(0)) {
|
||||
// add the first in reversed to the beginning of the second
|
||||
firstMultiLine.remove(toAdd);
|
||||
for (Way w : firstMultiLine) {
|
||||
secondMultiLine.add(0,w);
|
||||
}
|
||||
multiLines.remove(firstMultiLine);
|
||||
} else {
|
||||
// add the first in reversed to the end of the second
|
||||
firstMultiLine.remove(toAdd);
|
||||
int index = secondMultiLine.size();
|
||||
for (Way w : firstMultiLine) {
|
||||
secondMultiLine.add(index ,w);
|
||||
}
|
||||
multiLines.remove(firstMultiLine);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return multiLines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the end nodes of a multiLine
|
||||
* The ways in the multiLine don't have to be initialized for this.
|
||||
*
|
||||
* @param multiLine the multiLine to get the end nodes of
|
||||
* @return an array of size two with the end nodes on both sides. <br />
|
||||
* * The first node is the end node of the first way in the multiLine. <br />
|
||||
* * The second node is the end node of the last way in the multiLine.
|
||||
*/
|
||||
private long[] getMultiLineEndNodes(ArrayList<Way> multiLine) {
|
||||
|
||||
// special case, the multiLine contains only a single way, return the end nodes of the way
|
||||
if (multiLine.size() == 1){
|
||||
return new long[] {multiLine.get(0).getFirstNodeId(), multiLine.get(0).getLastNodeId()};
|
||||
}
|
||||
|
||||
if (multiLine.size() == 2) {
|
||||
// ring of two elements, arbitrary choice of the end nodes
|
||||
if(multiLine.get(0).getFirstNodeId() == multiLine.get(1).getFirstNodeId() &&
|
||||
multiLine.get(0).getLastNodeId() == multiLine.get(1).getLastNodeId()) {
|
||||
return new long[] {multiLine.get(0).getFirstNodeId(), multiLine.get(0).getFirstNodeId()};
|
||||
} else if(multiLine.get(0).getFirstNodeId() == multiLine.get(1).getLastNodeId() &&
|
||||
multiLine.get(0).getLastNodeId() == multiLine.get(1).getFirstNodeId()) {
|
||||
return new long[] {multiLine.get(0).getFirstNodeId(), multiLine.get(0).getFirstNodeId()};
|
||||
}
|
||||
}
|
||||
|
||||
// For all other multiLine lenghts, or for non-closed multiLines with two elements, proceed
|
||||
|
||||
long n1 = 0, n2 = 0;
|
||||
|
||||
if (multiLine.get(0).getFirstNodeId() == multiLine.get(1).getFirstNodeId() ||
|
||||
multiLine.get(0).getFirstNodeId() == multiLine.get(1).getLastNodeId()) {
|
||||
n1 = multiLine.get(0).getLastNodeId();
|
||||
} else if (multiLine.get(0).getLastNodeId() == multiLine.get(1).getFirstNodeId() ||
|
||||
multiLine.get(0).getLastNodeId() == multiLine.get(1).getLastNodeId()) {
|
||||
n1 = multiLine.get(0).getFirstNodeId();
|
||||
}
|
||||
|
||||
int lastIdx = multiLine.size()-1;
|
||||
|
||||
if (multiLine.get(lastIdx).getFirstNodeId() == multiLine.get(1).getFirstNodeId() ||
|
||||
multiLine.get(lastIdx).getFirstNodeId() == multiLine.get(1).getLastNodeId()) {
|
||||
n2 = multiLine.get(lastIdx).getLastNodeId();
|
||||
} else if (multiLine.get(lastIdx).getLastNodeId() == multiLine.get(lastIdx - 1).getFirstNodeId() ||
|
||||
multiLine.get(lastIdx).getLastNodeId() == multiLine.get(lastIdx - 1).getLastNodeId()) {
|
||||
n2 = multiLine.get(lastIdx).getFirstNodeId();
|
||||
}
|
||||
|
||||
return new long[] {n1, n2};
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine a list of ways to a list of rings
|
||||
*
|
||||
* The ways must not have initialized nodes for this
|
||||
*
|
||||
* @param ways the ways to group
|
||||
* @return a list of Rings
|
||||
*/
|
||||
public static ArrayList<Ring> combineToRings(List<Way> ways){
|
||||
ArrayList<ArrayList<Way>> multiLines = createMultiLines(ways);
|
||||
|
||||
ArrayList<Ring> result = new ArrayList<Ring> ();
|
||||
|
||||
for (ArrayList<Way> multiLine : multiLines) {
|
||||
Ring r = new Ring(multiLine);
|
||||
result.add(r);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if this Ring contains the node
|
||||
* @param n the Node to check
|
||||
* @return yes if the node is inside the ring
|
||||
*/
|
||||
public boolean containsNode(Node n) {
|
||||
return containsPoint(n.getLatitude(), n.getLongitude());
|
||||
}
|
||||
|
||||
/**
|
||||
* check if this Ring contains the point
|
||||
* @param latitude lat of the point
|
||||
* @param longitude lon of the point
|
||||
* @return yes if the point is inside the ring
|
||||
*/
|
||||
public boolean containsPoint(double latitude, double longitude){
|
||||
return countIntersections(latitude, longitude) % 2 == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* count the intersections when going from lat, lon to outside the ring
|
||||
* @param latitude the lat to start
|
||||
* @param longitude the lon to start
|
||||
* @param intersections the number of intersections to start with
|
||||
* @return the number of intersections
|
||||
*/
|
||||
private int countIntersections(double latitude, double longitude) {
|
||||
int intersections = 0;
|
||||
|
||||
mergeWays();
|
||||
List<Node> polyNodes = closedBorder.getNodes();
|
||||
for (int i = 0; i < polyNodes.size() - 1; i++) {
|
||||
if (MapAlgorithms.ray_intersect_lon(polyNodes.get(i),
|
||||
polyNodes.get(i + 1), latitude, longitude) != -360d) {
|
||||
intersections++;
|
||||
}
|
||||
}
|
||||
// special handling, also count first and last, might not be closed, but
|
||||
// we want this!
|
||||
if (MapAlgorithms.ray_intersect_lon(polyNodes.get(0),
|
||||
polyNodes.get(polyNodes.size() - 1), latitude, longitude) != -360d) {
|
||||
intersections++;
|
||||
}
|
||||
return intersections;
|
||||
}
|
||||
|
||||
/**
|
||||
* collect the points of all ways added by the user <br />
|
||||
* automatically added ways because of closing the Ring won't be added <br />
|
||||
* Only ways with initialized points can be handled.
|
||||
* @return a List with nodes
|
||||
*/
|
||||
public List<Node> collectPoints() {
|
||||
|
||||
ArrayList<Node> collected = new ArrayList<Node>();
|
||||
|
||||
for (Way w : ways) {
|
||||
collected.addAll(w.getNodes());
|
||||
}
|
||||
|
||||
return collected;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is in Ring r
|
||||
* @param r the ring to check
|
||||
* @return true if this Ring is inside Ring r
|
||||
*/
|
||||
public boolean isIn(Ring r) {
|
||||
/*
|
||||
* bi-directional check is needed because some concave rings can intersect
|
||||
* and would only fail on one of the checks
|
||||
*/
|
||||
List<Node> points = this.collectPoints();
|
||||
|
||||
// r should contain all nodes of this
|
||||
for(Node n : points) {
|
||||
if (!r.containsNode(n)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
points = r.collectPoints();
|
||||
|
||||
// this should not contain a node from r
|
||||
for(Node n : points) {
|
||||
if (this.containsNode(n)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
/**
|
||||
* @return -1 if this Ring is inside r <br />
|
||||
* 1 if r is inside this Ring <br />
|
||||
* 0 otherwise (Rings are next to each other, Rings intersect or Rings are malformed)
|
||||
*/
|
||||
public int compareTo(Ring r) {
|
||||
if (this.isIn(r)) return -1;
|
||||
if (r.isIn(this)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this Ring is not complete
|
||||
* (some ways are not initialized
|
||||
* because they are not included in the OSM file) <p />
|
||||
*
|
||||
* We are trying to close this Ring by using the other Ring.<p />
|
||||
*
|
||||
* The other Ring must be complete, and the part of this Ring
|
||||
* inside the other Ring must also be complete.
|
||||
* @param other the other Ring (which is complete) used to close this one
|
||||
*/
|
||||
public void closeWithOtherRing(Ring other) {
|
||||
Way thisBorder = getBorder();
|
||||
List<Integer> thisSwitchPoints = new ArrayList<Integer>();
|
||||
|
||||
boolean insideOther = other.containsNode(thisBorder.getNodes().get(0));
|
||||
|
||||
// Search the node pairs for which the ring goes inside or out the other
|
||||
for (int i = 0; i<thisBorder.getNodes().size(); i++) {
|
||||
Node n = thisBorder.getNodes().get(i);
|
||||
if (other.containsNode(n) != insideOther) {
|
||||
// we are getting out or in the boundary now.
|
||||
// toggle switch
|
||||
insideOther = !insideOther;
|
||||
|
||||
thisSwitchPoints.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
List<Integer> otherSwitchPoints = new ArrayList<Integer>();
|
||||
|
||||
// Search the according node pairs in the other ring
|
||||
for (int i : thisSwitchPoints) {
|
||||
LatLon a = thisBorder.getNodes().get(i-1).getLatLon();
|
||||
LatLon b = thisBorder.getNodes().get(i).getLatLon();
|
||||
otherSwitchPoints.add(crossRingBorder(a, b));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
*
|
||||
* * Split the other Ring into ways from splitPoint to splitPoint
|
||||
*
|
||||
* * Split this ring into ways from splitPoint to splitPoint
|
||||
*
|
||||
* * Filter out the parts of way from this that are inside the other Ring
|
||||
* Use the insideOther var and the switchPoints list for this.
|
||||
*
|
||||
* * For each two parts of way from this, search a part of way connecting the two.
|
||||
* If there are two, take the shortest.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the segment of the Ring that intersects a segment
|
||||
* going from point a to point b
|
||||
*
|
||||
* @param a the begin point of the segment
|
||||
* @param b the end point of the segment
|
||||
* @return an integer i which is the index so that the segment
|
||||
* from getBorder().get(i-1) to getBorder().get(i) intersects with
|
||||
* the segment from parameters a to b. <p />
|
||||
*
|
||||
* 0 if the segment from a to b doesn't intersect with the Ring.
|
||||
*/
|
||||
public int crossRingBorder(LatLon a, LatLon b) {
|
||||
Way border = getBorder();
|
||||
for (int i = 1; i<border.getNodes().size(); i++) {
|
||||
LatLon c = border.getNodes().get(i-1).getLatLon();
|
||||
LatLon d = border.getNodes().get(i).getLatLon();
|
||||
//FIXME find library that can do this not java.awt in Android
|
||||
/*if (Line2D.linesIntersect(
|
||||
a.getLatitude(), a.getLongitude(),
|
||||
b.getLatitude(), b.getLongitude(),
|
||||
c.getLatitude(), c.getLongitude(),
|
||||
d.getLatitude(), d.getLongitude())) {
|
||||
return i;
|
||||
}*/
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -26,8 +26,9 @@ import net.osmand.Algoritms;
|
|||
import net.osmand.IProgress;
|
||||
import net.osmand.binary.OsmandOdb.MapData;
|
||||
import net.osmand.binary.OsmandOdb.MapDataBlock;
|
||||
import net.osmand.data.Boundary;
|
||||
import net.osmand.data.MapAlgorithms;
|
||||
import net.osmand.data.Multipolygon;
|
||||
import net.osmand.data.Ring;
|
||||
import net.osmand.data.preparation.MapZooms.MapZoomPair;
|
||||
import net.osmand.osm.Entity;
|
||||
import net.osmand.osm.Entity.EntityId;
|
||||
|
@ -113,230 +114,96 @@ public class IndexVectorMapCreator extends AbstractIndexPartCreator {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* index a multipolygon into the database
|
||||
* only multipolygons without admin_level and with type=multipolygon are indexed
|
||||
* broken multipolygons are also indexed, inner ways are sometimes left out, broken rings are split and closed
|
||||
* broken multipolygons will normally be logged
|
||||
* @param e the entity to index
|
||||
* @param ctx the database context
|
||||
* @throws SQLException
|
||||
*/
|
||||
private void indexMultiPolygon(Entity e, OsmDbAccessorContext ctx) throws SQLException {
|
||||
if (e instanceof Relation && "multipolygon".equals(e.getTag(OSMTagKey.TYPE))) { //$NON-NLS-1$
|
||||
if(e.getTag(OSMTagKey.ADMIN_LEVEL) != null) {
|
||||
// don't index boundaries as multipolygon (only areas ideally are multipolygon)
|
||||
return;
|
||||
}
|
||||
ctx.loadEntityRelation((Relation) e);
|
||||
Map<Entity, String> entities = ((Relation) e).getMemberEntities();
|
||||
// Don't handle things that aren't multipolygon, and nothing administrative
|
||||
if (! (e instanceof Relation) ||
|
||||
! "multipolygon".equals(e.getTag(OSMTagKey.TYPE)) ||
|
||||
e.getTag(OSMTagKey.ADMIN_LEVEL) != null ) return;
|
||||
|
||||
boolean outerFound = false;
|
||||
for (Entity es : entities.keySet()) {
|
||||
if (es instanceof Way) {
|
||||
boolean inner = "inner".equals(entities.get(es)); //$NON-NLS-1$
|
||||
if (!inner) {
|
||||
outerFound = true;
|
||||
// This is incorrect (it should be intersection of all boundaries)
|
||||
// Currently it causes an issue with coastline (if one line is coastline)
|
||||
// for (String t : es.getTagKeySet()) {
|
||||
// e.putTag(t, es.getTag(t));
|
||||
// }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!outerFound) {
|
||||
logMapDataWarn.warn("Probably map bug: Multipoligon id=" + e.getId() + " contains only inner ways : "); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
return;
|
||||
}
|
||||
ctx.loadEntityRelation((Relation) e);
|
||||
Map<Entity, String> entities = ((Relation) e).getMemberEntities();
|
||||
|
||||
renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(0).getMaxZoom(), typeUse, addtypeUse, namesUse, tempNameUse);
|
||||
if (typeUse.size() > 0) {
|
||||
List<List<Way>> completedRings = new ArrayList<List<Way>>();
|
||||
List<List<Way>> incompletedRings = new ArrayList<List<Way>>();
|
||||
for (Entity es : entities.keySet()) {
|
||||
if (es instanceof Way) {
|
||||
if (!((Way) es).getNodeIds().isEmpty()) {
|
||||
combineMultiPolygons((Way) es, completedRings, incompletedRings);
|
||||
}
|
||||
}
|
||||
}
|
||||
// skip incompleted rings and do not add whole relation ?
|
||||
if (!incompletedRings.isEmpty()) {
|
||||
logMapDataWarn.warn("In multipolygon " + e.getId() + " there are incompleted ways : " + incompletedRings);
|
||||
return;
|
||||
// completedRings.addAll(incompletedRings);
|
||||
}
|
||||
// create a multipolygon object for this
|
||||
Multipolygon original = new Multipolygon(e.getId());
|
||||
|
||||
// skip completed rings that are not one type
|
||||
for (List<Way> l : completedRings) {
|
||||
boolean innerType = "inner".equals(entities.get(l.get(0))); //$NON-NLS-1$
|
||||
for (Way way : l) {
|
||||
boolean inner = "inner".equals(entities.get(way)); //$NON-NLS-1$
|
||||
if (innerType != inner) {
|
||||
logMapDataWarn
|
||||
.warn("Probably map bug: Multipoligon contains outer and inner ways.\n" + //$NON-NLS-1$
|
||||
"Way:"
|
||||
+ way.getId()
|
||||
+ " is strange part of completed ring. InnerType:" + innerType + " way inner: " + inner + " way inner string:" + entities.get(way)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// That check is not strictly needed on preproccessing step because client can handle it
|
||||
Node nodeOut = checkOuterWaysEncloseInnerWays(completedRings, entities);
|
||||
if (nodeOut != null) {
|
||||
logMapDataWarn.warn("Map bug: Multipoligon contains 'inner' way point outside of 'outer' border.\n" + //$NON-NLS-1$
|
||||
"Multipolygon id : " + e.getId() + ", inner node out id : " + nodeOut.getId()); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
List<Node> outerWaySrc = new ArrayList<Node>();
|
||||
List<List<Node>> innerWays = new ArrayList<List<Node>>();
|
||||
|
||||
TIntArrayList typeToSave = new TIntArrayList(typeUse);
|
||||
long baseId = 0;
|
||||
for (List<Way> l : completedRings) {
|
||||
boolean innerType = "inner".equals(entities.get(l.get(0))); //$NON-NLS-1$
|
||||
if (!innerType && !outerWaySrc.isEmpty()) {
|
||||
logMapDataWarn.warn("Map bug: Multipoligon contains many 'outer' borders.\n" + //$NON-NLS-1$
|
||||
"Multipolygon id : " + e.getId() + ", outer way id : " + l.get(0).getId()); //$NON-NLS-1$
|
||||
return;
|
||||
}
|
||||
List<Node> toCollect;
|
||||
if (innerType) {
|
||||
toCollect = new ArrayList<Node>();
|
||||
innerWays.add(toCollect);
|
||||
} else {
|
||||
toCollect = outerWaySrc;
|
||||
}
|
||||
|
||||
for (Way way : l) {
|
||||
toCollect.addAll(way.getNodes());
|
||||
if (!innerType) {
|
||||
TIntArrayList out = multiPolygonsWays.put(way.getId(), typeToSave);
|
||||
if(out == null){
|
||||
baseId = -way.getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(baseId == 0){
|
||||
// use base id as well?
|
||||
baseId = notUsedId --;
|
||||
}
|
||||
nextZoom: for (int level = 0; level < mapZooms.size(); level++) {
|
||||
renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(level).getMaxZoom(), typeUse, addtypeUse, namesUse,
|
||||
tempNameUse);
|
||||
if (typeUse.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
long id = convertBaseIdToGeneratedId(baseId, level);
|
||||
// simplify route
|
||||
List<Node> outerWay = outerWaySrc;
|
||||
int zoomToSimplify = mapZooms.getLevel(level).getMaxZoom() - 1;
|
||||
if (zoomToSimplify < 15) {
|
||||
outerWay = simplifyCycleWay(outerWay, zoomToSimplify, zoomWaySmothness);
|
||||
if (outerWay == null) {
|
||||
continue nextZoom;
|
||||
}
|
||||
List<List<Node>> newinnerWays = new ArrayList<List<Node>>();
|
||||
for (List<Node> ls : innerWays) {
|
||||
ls = simplifyCycleWay(ls, zoomToSimplify, zoomWaySmothness);
|
||||
if (ls != null) {
|
||||
newinnerWays.add(ls);
|
||||
}
|
||||
}
|
||||
innerWays = newinnerWays;
|
||||
}
|
||||
insertBinaryMapRenderObjectIndex(mapTree[level], outerWay, innerWays, namesUse, id, true, typeUse, addtypeUse, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Node checkOuterWaysEncloseInnerWays(List<List<Way>> completedRings, Map<Entity, String> entities) {
|
||||
List<List<Way>> innerWays = new ArrayList<List<Way>>();
|
||||
Boundary outerBoundary = new Boundary();
|
||||
Node toReturn = null;
|
||||
for (List<Way> ring : completedRings) {
|
||||
boolean innerType = "inner".equals(entities.get(ring.get(0))); //$NON-NLS-1$
|
||||
if (!innerType) {
|
||||
outerBoundary.addOuterWays(ring);
|
||||
} else {
|
||||
innerWays.add(ring);
|
||||
}
|
||||
}
|
||||
|
||||
for (List<Way> innerRing : innerWays) {
|
||||
ring: for (Way innerWay : innerRing) {
|
||||
for (Node node : innerWay.getNodes()) {
|
||||
if (!outerBoundary.containsPoint(node.getLatitude(), node.getLongitude())) {
|
||||
if (toReturn == null) {
|
||||
toReturn = node;
|
||||
}
|
||||
completedRings.remove(innerRing);
|
||||
break ring;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
private List<Way> reverse(List<Way> l) {
|
||||
Collections.reverse(l);
|
||||
for(Way w : l){
|
||||
w.getNodeIds().reverse();
|
||||
Collections.reverse(w.getNodes());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
private List<Way> appendLists(List<Way> w1, List<Way> w2){
|
||||
w1.addAll(w2);
|
||||
return w1;
|
||||
}
|
||||
|
||||
//TODO Can the Multipolygon class be the one that replaces this?
|
||||
private void combineMultiPolygons(Way w, List<List<Way>> completedRings, List<List<Way>> incompletedRings) {
|
||||
long lId = w.getEntityIds().get(w.getEntityIds().size() - 1).getId().longValue();
|
||||
long fId = w.getEntityIds().get(0).getId().longValue();
|
||||
if (fId == lId) {
|
||||
completedRings.add(Collections.singletonList(w));
|
||||
} else {
|
||||
List<Way> l = new ArrayList<Way>();
|
||||
l.add(w);
|
||||
boolean add = true;
|
||||
for (int k = 0; k < incompletedRings.size();) {
|
||||
boolean remove = false;
|
||||
List<Way> i = incompletedRings.get(k);
|
||||
Way last = i.get(i.size() - 1);
|
||||
Way first = i.get(0);
|
||||
long lastId = last.getEntityIds().get(last.getEntityIds().size() - 1).getId().longValue();
|
||||
long firstId = first.getEntityIds().get(0).getId().longValue();
|
||||
if (fId == lastId) {
|
||||
remove = true;
|
||||
l = appendLists(i, l);
|
||||
fId = firstId;
|
||||
} else if (lId == firstId) {
|
||||
l = appendLists(l, i);
|
||||
remove = true;
|
||||
lId = lastId;
|
||||
} else if (lId == lastId) {
|
||||
l = appendLists(l, reverse(i));
|
||||
remove = true;
|
||||
lId = firstId;
|
||||
} else if (fId == firstId) {
|
||||
l = appendLists(reverse(i), l);
|
||||
remove = true;
|
||||
fId = lastId;
|
||||
}
|
||||
if (remove) {
|
||||
incompletedRings.remove(k);
|
||||
// fill the multipolygon with all ways from the Relation
|
||||
for (Entity es : entities.keySet()) {
|
||||
if (es instanceof Way) {
|
||||
boolean inner = "inner".equals(entities.get(es)); //$NON-NLS-1$
|
||||
if (inner) {
|
||||
original.addInnerWay((Way) es);
|
||||
} else {
|
||||
k++;
|
||||
}
|
||||
if (fId == lId) {
|
||||
completedRings.add(l);
|
||||
add = false;
|
||||
break;
|
||||
original.addOuterWay((Way) es);
|
||||
}
|
||||
}
|
||||
if (add) {
|
||||
incompletedRings.add(l);
|
||||
}
|
||||
|
||||
// Log if something is wrong
|
||||
if (!original.hasOpenedPolygons()) {
|
||||
logMapDataWarn.warn("Multipolygon has unclosed parts: Multipoligon id=" + e.getId()); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(0).getMaxZoom(), typeUse, addtypeUse, namesUse, tempNameUse);
|
||||
|
||||
//Don't add multipolygons with an unknown type
|
||||
if (typeUse.size() == 0) return;
|
||||
|
||||
// Log the fact that Rings aren't complete, but continue with the relation, try to close it as well as possible
|
||||
if (!original.areRingsComplete()) {
|
||||
logMapDataWarn.warn("In multipolygon " + e.getId() + " there are incompleted ways");
|
||||
}
|
||||
// Rings with different types (inner or outer) in one ring will be logged in the previous case
|
||||
// The Rings are only composed by type, so if one way gets in a different Ring, the rings will be incomplete
|
||||
|
||||
List<Multipolygon> multipolygons = original.splitPerOuterRing(logMapDataWarn);
|
||||
|
||||
|
||||
for (Multipolygon m : multipolygons) {
|
||||
|
||||
// innerWays are new closed ways
|
||||
List<List<Node>> innerWays = new ArrayList<List<Node>>();
|
||||
|
||||
for (Ring r : m.getInnerRings()) {
|
||||
innerWays.add(r.getBorder().getNodes());
|
||||
}
|
||||
|
||||
// don't use the relation ids. Create new ones
|
||||
long baseId = notUsedId --;
|
||||
nextZoom: for (int level = 0; level < mapZooms.size(); level++) {
|
||||
renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(level).getMaxZoom(), typeUse, addtypeUse, namesUse,
|
||||
tempNameUse);
|
||||
if (typeUse.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
long id = convertBaseIdToGeneratedId(baseId, level);
|
||||
// simplify route
|
||||
List<Node> outerWay = m.getOuterNodes();
|
||||
int zoomToSimplify = mapZooms.getLevel(level).getMaxZoom() - 1;
|
||||
if (zoomToSimplify < 15) {
|
||||
outerWay = simplifyCycleWay(outerWay, zoomToSimplify, zoomWaySmothness);
|
||||
if (outerWay == null) {
|
||||
continue nextZoom;
|
||||
}
|
||||
List<List<Node>> newinnerWays = new ArrayList<List<Node>>();
|
||||
for (List<Node> ls : innerWays) {
|
||||
ls = simplifyCycleWay(ls, zoomToSimplify, zoomWaySmothness);
|
||||
if (ls != null) {
|
||||
newinnerWays.add(ls);
|
||||
}
|
||||
}
|
||||
innerWays = newinnerWays;
|
||||
}
|
||||
insertBinaryMapRenderObjectIndex(mapTree[level], outerWay, innerWays, namesUse, id, true, typeUse, addtypeUse, true);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
<type tag="highway" value="bus_stop" minzoom="15" poi_category="transportation"/>
|
||||
<type tag="highway" value="platform" minzoom="15" poi_category="transportation"/>
|
||||
<type tag="highway" value="turning_circle" minzoom="16" poi_category="transportation"/>
|
||||
<type tag="highway" value="emergency_access_point" minzoom="16" poi_category="transportation"/>
|
||||
<type tag="highway" value="emergency_access_point" minzoom="16" poi_category="emergency"/>
|
||||
<type tag="highway" value="speed_camera" minzoom="16" poi_category="transportation"/>
|
||||
<type tag="highway" value="street_lamp" minzoom="16" />
|
||||
|
||||
|
@ -379,6 +379,7 @@
|
|||
<type tag="emergency" value="fire_hose" minzoom="15" />
|
||||
<type tag="emergency" value="fire_hydrant" minzoom="15" />
|
||||
<type tag="emergency" value="phone" minzoom="15" />
|
||||
<type tag="emergency" value="access_point" minzoom="15" />
|
||||
<type tag="amenity" value="emergency_phone" target_tag="emergency" target_value="phone" minzoom="15" />
|
||||
<type tag="emergency" value="siren" minzoom="15" />
|
||||
<type tag="amenity" value="fire_station" minzoom="15" poi_category="emergency"/>
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="Advanced Mode..." />
|
||||
android:text="@string/btn_advanced_mode" />
|
||||
|
||||
<TableLayout
|
||||
android:id="@+id/advancedModeTable"
|
||||
|
@ -103,7 +103,7 @@
|
|||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="Add Tag"
|
||||
android:text="@string/btn_add_tag"
|
||||
android:visibility="gone"/>
|
||||
<TextView android:text="@string/poi_dialog_other_tags_message" android:id="@+id/TextView" android:layout_marginLeft="5dp" android:layout_width="fill_parent" android:layout_height="wrap_content"/>
|
||||
<LinearLayout android:id="@+id/LinearLayout" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal"
|
||||
|
|
|
@ -6,8 +6,11 @@
|
|||
android:paddingLeft="3dp" android:paddingRight="3dp">
|
||||
<Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_weight="1" android:text="@string/filter_current_poiButton"
|
||||
android:id="@+id/filter_currentButton"></Button>
|
||||
<ImageButton android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:layout_width="wrap_content" android:src="@drawable/save_btn"
|
||||
android:id="@+id/SaveButton"/>
|
||||
<ImageButton android:layout_height="48dp" android:layout_marginLeft="4dp" android:layout_width="48dp" android:src="@drawable/reset"
|
||||
android:contentDescription="@string/default_buttons_reset"
|
||||
android:id="@+id/DeleteButton"/>
|
||||
<ImageButton android:layout_height="wrap_content" android:layout_marginLeft="2dp" android:layout_width="wrap_content" android:src="@drawable/save_btn"
|
||||
android:id="@+id/SaveButton" android:contentDescription="@string/default_buttons_save"/>
|
||||
</LinearLayout>
|
||||
<ListView android:id="@android:id/list" android:layout_width="fill_parent" android:choiceMode="multipleChoice"
|
||||
android:layout_height="fill_parent" style="@style/OsmandListView"></ListView>
|
||||
|
|
|
@ -6,6 +6,11 @@
|
|||
<ImageView android:id="@+id/folder_icon" android:layout_width="37dip"
|
||||
android:paddingLeft="8dp" android:paddingRight="8dp"
|
||||
android:paddingTop="2dp" android:layout_height="fill_parent" />
|
||||
<TextView android:id="@+id/folder_label" android:layout_width="wrap_content"
|
||||
<TextView android:id="@+id/folder_label" android:layout_width="wrap_content" android:layout_weight="1"
|
||||
android:layout_height="wrap_content" style="@style/ListText"/>
|
||||
|
||||
<ImageView android:id="@+id/folder_edit_icon"
|
||||
android:paddingLeft="4dp" android:paddingRight="8dp" android:src="@android:drawable/ic_input_get"
|
||||
android:layout_width="37dip"
|
||||
android:paddingTop="2dp" android:layout_height="fill_parent" />
|
||||
</LinearLayout>
|
||||
|
|
|
@ -778,7 +778,7 @@ Pots habilitat (en línia o emmagatzemats)fonts de tessel·les de mapa, preferè
|
|||
\n\t - Necessites descarregar completament totes les dades fora de línia( les dades antigues no seran suportades)↵
|
||||
\n\t- *Calcul de rutes fora línia* ↵
|
||||
\n\t - El calcul de rutes fora línia ha esdevingut mes robust↵
|
||||
\n\t *Usabilitat i experiència d'IU*↵
|
||||
\n\t *Usabilitat i experiència d\'IU*↵
|
||||
\n\t - Millorada en moltes àrees ↵</string>
|
||||
<string name="tip_recent_changes_0_8_2_t">Canvis a 0.8.2 :
|
||||
\n\t* Calcul de rutes millorat
|
||||
|
@ -788,7 +788,7 @@ Pots habilitat (en línia o emmagatzemats)fonts de tessel·les de mapa, preferè
|
|||
\n\t* Corretgit Arabic, Kannada scripting i llenguatges RTL
|
||||
</string>
|
||||
<string name="gpxup_trackable">Seguible</string>
|
||||
<string name="search_offline_geo_error">No s'ha pogut analitzar la geo intenció \'%s\'</string>
|
||||
<string name="search_offline_geo_error">No s\'ha pogut analitzar la geo intenció \'%s\'</string>
|
||||
<string name="amenity_type_geocache">Geoenmegatzament</string>
|
||||
<string name="use_osmand_routing_service_descr">L'us de la navagacio fora de línia d'OsmAnd s'aplica nomes a rutes > 20km (experimental)</string>
|
||||
<string name="use_osmand_routing_service_descr">L\'us de la navagacio fora de línia d\'OsmAnd s\'aplica nomes a rutes > 20km (experimental)</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources><string name="show_warnings_title">Warnungen anzeigen…</string>
|
||||
<string name="show_warnings_descr">Warnungen für Tempolimits, stationäre Radarkontrollen, Rüttelschwellen u.Ä.</string>
|
||||
<string name="use_compass_navigation_descr">Kompass verwenden, wenn keine andere Richtungsbestimmung möglich</string>
|
||||
|
|
|
@ -9,6 +9,19 @@
|
|||
1. All your modified/created strings are in the top of the file (to make easier find what\'s translated).
|
||||
PLEASE: Have a look at http://code.google.com/p/osmand/wiki/UIConsistency, it may really improve your and our work :-) Thx - Hardy
|
||||
-->
|
||||
<string name="btn_add_tag">Add Tag</string>
|
||||
<string name="btn_advanced_mode">Advanced Mode...</string>
|
||||
<string name="poi_filter_parking">Parking</string>
|
||||
<string name="poi_filter_emergency">Emergency</string>
|
||||
<string name="poi_filter_public_transport">Public Transport</string>
|
||||
<string name="poi_filter_entertainment">Entertainment</string>
|
||||
<string name="poi_filter_accomodation">Accomodation</string>
|
||||
<string name="poi_filter_restaurants">Restaurants</string>
|
||||
<string name="poi_filter_sightseeing">Sightseeing</string>
|
||||
<string name="poi_filter_car_aid">Car aid</string>
|
||||
<string name="poi_filter_food_shop">Food shop</string>
|
||||
<string name="poi_filter_for_tourists">For tourists</string>
|
||||
<string name="poi_filter_fuel">Fuel</string>
|
||||
<string name="show_warnings_title">Show alarms…</string>
|
||||
<string name="show_warnings_descr">Show speed limits, speed cameras, speed bumps, and other warnings</string>
|
||||
<string name="use_compass_navigation_descr">Use the compass when no heading is detected otherwise</string>
|
||||
|
@ -714,12 +727,8 @@
|
|||
<string name="km_h">km/h</string>
|
||||
<string name="m">m</string>
|
||||
<string name="old_map_index_is_not_supported">Deprecated map data format \'\'{0}\'\' is not supported</string>
|
||||
<string name="poi_filter_car_aid">Car aid</string>
|
||||
<string name="poi_filter_closest_poi">Closest POI</string>
|
||||
<string name="poi_filter_custom_filter">Custom filter</string>
|
||||
<string name="poi_filter_food_shop">Food shop</string>
|
||||
<string name="poi_filter_for_tourists">For tourists</string>
|
||||
<string name="poi_filter_fuel">Fuel</string>
|
||||
<string name="poi_filter_namefinder">Online NameFinder</string>
|
||||
<string name="reading_cached_tiles">Reading cached tiles…</string>
|
||||
<string name="version_index_is_big_for_memory">The index \'\'{0}\'\' did not fit into memory</string>
|
||||
|
@ -730,7 +739,7 @@
|
|||
<string name="specified_dir_doesnt_exist">Can not find specified directory.</string>
|
||||
<string name="application_dir">Storage directory</string>
|
||||
<string name="application_dir_change_warning">Changing the storage directory will not move or delete the data. This must be performed separately and outside OsmAnd. Continue anyway?</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="osmand_net_previously_installed">A previous OsmAnd version is installed. All offline data will be supported by 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…</string>
|
||||
<string name="install_selected_build">Do you want to install OsmAnd - {0} from {1} {2} MB ?</string>
|
||||
|
|
|
@ -153,6 +153,7 @@ public class OsmandApplication extends Application {
|
|||
public PoiFiltersHelper getPoiFilters() {
|
||||
if (poiFilters == null) {
|
||||
poiFilters = new PoiFiltersHelper(this);
|
||||
poiFilters.updateFilters(true);
|
||||
}
|
||||
return poiFilters;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ public class PoiFilter {
|
|||
protected String filterId;
|
||||
protected String name;
|
||||
protected String nameFilter;
|
||||
private final boolean isStandardFilter;
|
||||
protected boolean isStandardFilter;
|
||||
|
||||
protected final OsmandApplication application;
|
||||
|
||||
|
@ -51,7 +51,7 @@ public class PoiFilter {
|
|||
}
|
||||
}
|
||||
|
||||
// constructor for standard filters
|
||||
// constructor for user defined filters
|
||||
public PoiFilter(String name, String filterId, Map<AmenityType, LinkedHashSet<String>> acceptedTypes, OsmandApplication app){
|
||||
application = app;
|
||||
isStandardFilter = false;
|
||||
|
@ -310,6 +310,10 @@ public class PoiFilter {
|
|||
return isStandardFilter;
|
||||
}
|
||||
|
||||
public void setStandardFilter(boolean isStandardFilter) {
|
||||
this.isStandardFilter = isStandardFilter;
|
||||
}
|
||||
|
||||
public OsmandApplication getApplication() {
|
||||
return application;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,20 @@ public class PoiFiltersHelper {
|
|||
private List<PoiFilter> cacheUserDefinedFilters;
|
||||
private List<PoiFilter> cacheOsmDefinedFilters;
|
||||
|
||||
private static final String UDF_CAR_AID = "car_aid";
|
||||
private static final String UDF_FOR_TOURISTS = "for_tourists";
|
||||
private static final String UDF_FOOD_SHOP = "food_shop";
|
||||
private static final String UDF_FUEL = "fuel";
|
||||
private static final String UDF_SIGHTSEEING = "sightseeing";
|
||||
private static final String UDF_EMERGENCY = "emergency";
|
||||
private static final String UDF_PUBLIC_TRANSPORT = "public_transport";
|
||||
private static final String UDF_ENTERTAINMENT = "entertainment";
|
||||
private static final String UDF_ACCOMODATION = "accomodation";
|
||||
private static final String UDF_RESTAURANTS = "restaurants";
|
||||
private static final String UDF_PARKING = "parking";
|
||||
|
||||
private static final String[] DEL = new String[] {};
|
||||
|
||||
public PoiFiltersHelper(OsmandApplication application){
|
||||
this.application = application;
|
||||
}
|
||||
|
@ -60,76 +74,125 @@ public class PoiFiltersHelper {
|
|||
return null;
|
||||
}
|
||||
|
||||
private List<PoiFilter> getUserDefinedDefaultFilters(){
|
||||
List<PoiFilter> filters = new ArrayList<PoiFilter>();
|
||||
Map<AmenityType, LinkedHashSet<String>> types = new LinkedHashMap<AmenityType, LinkedHashSet<String>>();
|
||||
private void putAll(Map<AmenityType, LinkedHashSet<String>> types, AmenityType tp){
|
||||
types.put(tp, null);
|
||||
}
|
||||
|
||||
private void putValues(Map<AmenityType, LinkedHashSet<String>> types, AmenityType tp,String... vls){
|
||||
LinkedHashSet<String> list = new LinkedHashSet<String>();
|
||||
list.add("fuel"); //$NON-NLS-1$
|
||||
list.add("car_wash"); //$NON-NLS-1$
|
||||
list.add("car_repair"); //$NON-NLS-1$
|
||||
types.put(AmenityType.TRANSPORTATION, list);
|
||||
list = new LinkedHashSet<String>();
|
||||
list.add("car"); //$NON-NLS-1$
|
||||
list.add("car_repair"); //$NON-NLS-1$
|
||||
types.put(AmenityType.SHOP, list);
|
||||
filters.add(new PoiFilter(application.getString(R.string.poi_filter_car_aid), null, types, application)); //$NON-NLS-1$
|
||||
types.clear();
|
||||
for(String v: vls){
|
||||
list.add(v);
|
||||
}
|
||||
types.put(tp, list);
|
||||
}
|
||||
|
||||
private List<PoiFilter> getUserDefinedDefaultFilters() {
|
||||
List<PoiFilter> filters = new ArrayList<PoiFilter>();
|
||||
filters.add(new PoiFilter(application.getString(R.string.poi_filter_car_aid), PoiFilter.USER_PREFIX + UDF_CAR_AID,
|
||||
configureDefaultUserDefinedFilter(null, UDF_CAR_AID), application));
|
||||
filters.add(new PoiFilter(application.getString(R.string.poi_filter_for_tourists), PoiFilter.USER_PREFIX + UDF_FOR_TOURISTS,
|
||||
configureDefaultUserDefinedFilter(null, UDF_FOR_TOURISTS), application));
|
||||
filters.add(new PoiFilter(application.getString(R.string.poi_filter_fuel), PoiFilter.USER_PREFIX + UDF_FUEL,
|
||||
configureDefaultUserDefinedFilter(null, UDF_FUEL), application));
|
||||
filters.add(new PoiFilter(application.getString(R.string.poi_filter_food_shop), PoiFilter.USER_PREFIX + UDF_FOOD_SHOP,
|
||||
configureDefaultUserDefinedFilter(null, UDF_FOOD_SHOP), application));
|
||||
filters.add(new PoiFilter(application.getString(R.string.poi_filter_sightseeing), PoiFilter.USER_PREFIX + UDF_SIGHTSEEING,
|
||||
configureDefaultUserDefinedFilter(null, UDF_SIGHTSEEING), application));
|
||||
|
||||
types.put(AmenityType.HISTORIC, null);
|
||||
types.put(AmenityType.TOURISM, null);
|
||||
list = new LinkedHashSet<String>();
|
||||
list.add("place_of_worship"); //$NON-NLS-1$
|
||||
list.add("internet_access"); //$NON-NLS-1$
|
||||
list.add("embassy"); //$NON-NLS-1$
|
||||
list.add("emergency_phone"); //$NON-NLS-1$
|
||||
list.add("marketplace"); //$NON-NLS-1$
|
||||
list.add("post_office"); //$NON-NLS-1$
|
||||
list.add("telephone"); //$NON-NLS-1$
|
||||
list.add("toilets"); //$NON-NLS-1$
|
||||
types.put(AmenityType.OTHER, list);
|
||||
filters.add(new PoiFilter(application.getString(R.string.poi_filter_for_tourists), null, types, application)); //$NON-NLS-1$
|
||||
types.clear();
|
||||
|
||||
list = new LinkedHashSet<String>();
|
||||
list.add("fuel"); //$NON-NLS-1$
|
||||
types.put(AmenityType.TRANSPORTATION, list);
|
||||
filters.add(new PoiFilter(application.getString(R.string.poi_filter_fuel), null, types, application)); //$NON-NLS-1$
|
||||
types.clear();
|
||||
|
||||
list = new LinkedHashSet<String>();
|
||||
list.add("alcohol"); //$NON-NLS-1$
|
||||
list.add("bakery"); //$NON-NLS-1$
|
||||
list.add("beverages"); //$NON-NLS-1$
|
||||
list.add("butcher"); //$NON-NLS-1$
|
||||
list.add("convenience"); //$NON-NLS-1$
|
||||
list.add("department_store"); //$NON-NLS-1$
|
||||
list.add("convenience"); //$NON-NLS-1$
|
||||
list.add("farm"); //$NON-NLS-1$
|
||||
list.add("general"); //$NON-NLS-1$
|
||||
list.add("ice_cream"); //$NON-NLS-1$
|
||||
list.add("kiosk"); //$NON-NLS-1$
|
||||
list.add("supermarket"); //$NON-NLS-1$
|
||||
list.add("variety_store"); //$NON-NLS-1$
|
||||
types.put(AmenityType.SHOP, list);
|
||||
filters.add(new PoiFilter(application.getString(R.string.poi_filter_food_shop), null, types, application)); //$NON-NLS-1$
|
||||
types.clear();
|
||||
|
||||
// UDF_EMERGENCY = "emergency";
|
||||
// UDF_ENTERTAINMENT = "entertainment";
|
||||
filters.add(new PoiFilter(application.getString(R.string.poi_filter_accomodation), PoiFilter.USER_PREFIX + UDF_ACCOMODATION,
|
||||
configureDefaultUserDefinedFilter(null, UDF_ACCOMODATION), application));
|
||||
filters.add(new PoiFilter(application.getString(R.string.poi_filter_restaurants), PoiFilter.USER_PREFIX + UDF_RESTAURANTS,
|
||||
configureDefaultUserDefinedFilter(null, UDF_RESTAURANTS), application));
|
||||
filters.add(new PoiFilter(application.getString(R.string.poi_filter_public_transport),
|
||||
PoiFilter.USER_PREFIX + UDF_PUBLIC_TRANSPORT, configureDefaultUserDefinedFilter(null, UDF_PUBLIC_TRANSPORT), application));
|
||||
filters.add(new PoiFilter(application.getString(R.string.poi_filter_parking), PoiFilter.USER_PREFIX + UDF_PARKING,
|
||||
configureDefaultUserDefinedFilter(null, UDF_PARKING), application));
|
||||
return filters;
|
||||
}
|
||||
|
||||
private Map<AmenityType, LinkedHashSet<String>> configureDefaultUserDefinedFilter(Map<AmenityType, LinkedHashSet<String>> types, String key) {
|
||||
if(types == null) {
|
||||
types = new LinkedHashMap<AmenityType, LinkedHashSet<String>>();
|
||||
}
|
||||
if(UDF_CAR_AID.equals(key)){
|
||||
putValues(types, AmenityType.TRANSPORTATION, "fuel", "car_wash", "car_repair","car", "car_sharing");
|
||||
putValues(types, AmenityType.SHOP, "fuel", "car_wash", "car_repair","car");
|
||||
} else if(UDF_SIGHTSEEING.equals(key)){
|
||||
putAll(types, AmenityType.HISTORIC);
|
||||
putAll(types, AmenityType.TOURISM);
|
||||
putValues(types, AmenityType.OTHER, "place_of_worship");
|
||||
} else if(UDF_FOR_TOURISTS.equals(key)){
|
||||
putAll(types, AmenityType.HISTORIC);
|
||||
putAll(types, AmenityType.TOURISM);
|
||||
putAll(types, AmenityType.FINANCE);
|
||||
putValues(types, AmenityType.OTHER, "place_of_worship", "internet_access", "embassy","emergency_phone","marketplace",
|
||||
"post_office","telephone", "toilets");
|
||||
} else if(UDF_FUEL.equals(key)){
|
||||
putValues(types, AmenityType.TRANSPORTATION, "fuel");
|
||||
} else if (UDF_FOOD_SHOP.equals(key)) {
|
||||
putValues(types, AmenityType.SHOP, "alcohol", "bakery", "beverages", "butcher", "convenience", "department_store",
|
||||
"convenience", "farm", "general", "ice_cream", "kiosk", "supermarket", "variety_store");
|
||||
} else if (UDF_SIGHTSEEING.equals(key)) {
|
||||
putAll(types, AmenityType.HISTORIC);
|
||||
putValues(types, AmenityType.TOURISM, "attraction",
|
||||
"artwork","zoo","theme_park", "museum","viewpoint");
|
||||
putValues(types, AmenityType.OTHER, "place_of_worship");
|
||||
} else if (UDF_ACCOMODATION.equals(key)) {
|
||||
putValues(types, AmenityType.TOURISM, "camp_site",
|
||||
"caravan_site","picnic_site","alpine_hut", "chalet","guest_house",
|
||||
"hostel", "hotel","motel");
|
||||
} else if (UDF_PARKING.equals(key)) {
|
||||
putValues(types, AmenityType.TRANSPORTATION, "parking",
|
||||
"bicycle_parking");
|
||||
} else if (UDF_PUBLIC_TRANSPORT.equals(key)) {
|
||||
putValues(types, AmenityType.TRANSPORTATION, "public_transport_stop_position", "public_transport_platform",
|
||||
"public_transport_station",
|
||||
// railway
|
||||
"railway_platform", "railway_station", "halt", "tram_stop", "subway_entrance", "railway_buffer_stop",
|
||||
// bus, cars, bicycle
|
||||
"bus_stop", "platform", "ferry_terminal", "taxi", "bicycle_rental", "bus_station", "car_rental", "car_sharing",
|
||||
// aero
|
||||
"airport", "aerodrome", "terminal", "gate",
|
||||
// aerial ways
|
||||
"aerialway_cable_car", "aerialway_gondola", "aerialway_chair_lift", "aerialway_mixed_lift", "aerialway_drag_lift",
|
||||
"aerialway_goods", "aerialway_station",
|
||||
// ways
|
||||
"rail", "tram", "light_rail", "subway", "railway_narrow_gauge", "railway_monorail", "railway_funicular");
|
||||
} else if (UDF_RESTAURANTS.equals(key)) {
|
||||
putValues(types, AmenityType.SUSTENANCE, "restaurant",
|
||||
"cafe", "food_court", "fast_food", "pub", "bar", "biergarten");
|
||||
} else if (UDF_EMERGENCY.equals(key)) {
|
||||
putAll(types, AmenityType.HEALTHCARE);
|
||||
putAll(types, AmenityType.EMERGENCY);
|
||||
} else if (UDF_ENTERTAINMENT.equals(key)) {
|
||||
putAll(types, AmenityType.ENTERTAINMENT);
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
|
||||
public List<PoiFilter> getUserDefinedPoiFilters(){
|
||||
if(cacheUserDefinedFilters == null){
|
||||
////ctx.deleteDatabase(PoiFilterDbHelper.DATABASE_NAME);
|
||||
|
||||
cacheUserDefinedFilters = new ArrayList<PoiFilter>();
|
||||
PoiFilter filter = new PoiFilter(application.getString(R.string.poi_filter_custom_filter), PoiFilter.CUSTOM_FILTER_ID, new LinkedHashMap<AmenityType, LinkedHashSet<String>>(), application); //$NON-NLS-1$
|
||||
PoiFilter filter = new PoiFilter(application.getString(R.string.poi_filter_custom_filter), PoiFilter.CUSTOM_FILTER_ID,
|
||||
new LinkedHashMap<AmenityType, LinkedHashSet<String>>(), application); //$NON-NLS-1$
|
||||
filter.setStandardFilter(true);
|
||||
cacheUserDefinedFilters.add(filter);
|
||||
filter = new SearchByNameFilter(application);
|
||||
cacheUserDefinedFilters.add(filter);
|
||||
PoiFilterDbHelper helper = openDbHelper();
|
||||
cacheUserDefinedFilters.addAll(helper.getFilters());
|
||||
List<PoiFilter> userDefined = helper.getFilters(helper.getReadableDatabase());
|
||||
final Collator instance = Collator.getInstance();
|
||||
Collections.sort(userDefined, new Comparator<PoiFilter>() {
|
||||
@Override
|
||||
public int compare(PoiFilter object1, PoiFilter object2) {
|
||||
return instance.compare(object1.getName(), object2.getName());
|
||||
}
|
||||
});
|
||||
cacheUserDefinedFilters.addAll(userDefined);
|
||||
helper.close();
|
||||
}
|
||||
return Collections.unmodifiableList(cacheUserDefinedFilters);
|
||||
|
@ -139,6 +202,12 @@ public class PoiFiltersHelper {
|
|||
return PoiFilter.STD_PREFIX + t;
|
||||
}
|
||||
|
||||
public void updateFilters(boolean onlyAddFilters){
|
||||
PoiFilterDbHelper helper = openDbHelper();
|
||||
helper.upgradeFilters(helper.getWritableDatabase(), onlyAddFilters);
|
||||
helper.close();
|
||||
}
|
||||
|
||||
|
||||
public List<PoiFilter> getOsmDefinedPoiFilters(){
|
||||
if(cacheOsmDefinedFilters == null){
|
||||
|
@ -171,7 +240,7 @@ public class PoiFiltersHelper {
|
|||
if(helper == null){
|
||||
return false;
|
||||
}
|
||||
boolean res = helper.deleteFilter(filter);
|
||||
boolean res = helper.deleteFilter(helper.getWritableDatabase(), filter);
|
||||
if(res){
|
||||
cacheUserDefinedFilters.remove(filter);
|
||||
}
|
||||
|
@ -201,7 +270,7 @@ public class PoiFiltersHelper {
|
|||
}
|
||||
PoiFilterDbHelper helper = openDbHelper();
|
||||
if (helper != null) {
|
||||
boolean res = helper.editFilter(filter);
|
||||
boolean res = helper.editFilter(helper.getWritableDatabase(), filter);
|
||||
helper.close();
|
||||
return res;
|
||||
}
|
||||
|
@ -212,7 +281,7 @@ public class PoiFiltersHelper {
|
|||
public class PoiFilterDbHelper extends SQLiteOpenHelper {
|
||||
|
||||
public static final String DATABASE_NAME = "poi_filters"; //$NON-NLS-1$
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
private static final int DATABASE_VERSION = 2;
|
||||
private static final String FILTER_NAME = "poi_filters"; //$NON-NLS-1$
|
||||
private static final String FILTER_COL_NAME = "name"; //$NON-NLS-1$
|
||||
private static final String FILTER_COL_ID = "id"; //$NON-NLS-1$
|
||||
|
@ -236,14 +305,45 @@ public class PoiFiltersHelper {
|
|||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(FILTER_TABLE_CREATE);
|
||||
db.execSQL(CATEGORIES_TABLE_CREATE);
|
||||
List<PoiFilter> filters = getUserDefinedDefaultFilters();
|
||||
for(PoiFilter f : filters){
|
||||
addFilter(f, db,false);
|
||||
}
|
||||
upgradeFilters(db, true);
|
||||
}
|
||||
|
||||
public void upgradeFilters(SQLiteDatabase db, boolean onlyAdd) {
|
||||
List<PoiFilter> filters = PoiFilterDbHelper.this.getFilters(db);
|
||||
List<PoiFilter> def = getUserDefinedDefaultFilters();
|
||||
for(PoiFilter f : filters){
|
||||
PoiFilter std = null;
|
||||
for(PoiFilter d : def){
|
||||
if(f.getFilterId().equals(d.getFilterId())){
|
||||
std = d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(String toDel : DEL) {
|
||||
if(f.getFilterId().equals(toDel)) {
|
||||
deleteFilter(db, f);
|
||||
}
|
||||
}
|
||||
if(std != null){
|
||||
if(!onlyAdd){
|
||||
editFilter(db, std);
|
||||
} else {
|
||||
updateName(db, std);
|
||||
}
|
||||
def.remove(std);
|
||||
}
|
||||
}
|
||||
for(PoiFilter d : def){
|
||||
addFilter(d, db, false);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
if (newVersion == 2 || newVersion == 3) {
|
||||
upgradeFilters(db, false);
|
||||
} else {
|
||||
upgradeFilters(db, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean addFilter(PoiFilter p, SQLiteDatabase db, boolean addOnlyCategories){
|
||||
|
@ -274,8 +374,7 @@ public class PoiFiltersHelper {
|
|||
return false;
|
||||
}
|
||||
|
||||
protected List<PoiFilter> getFilters(){
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
protected List<PoiFilter> getFilters(SQLiteDatabase db){
|
||||
ArrayList<PoiFilter> list = new ArrayList<PoiFilter>();
|
||||
if(db != null){
|
||||
Cursor query = db.rawQuery("SELECT " + CATEGORIES_FILTER_ID +", " + CATEGORIES_COL_CATEGORY +"," + CATEGORIES_COL_SUBCATEGORY +" FROM " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
|
||||
|
@ -319,21 +418,23 @@ public class PoiFiltersHelper {
|
|||
return list;
|
||||
}
|
||||
|
||||
protected boolean editFilter(PoiFilter filter) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
protected boolean editFilter(SQLiteDatabase db, PoiFilter filter) {
|
||||
if (db != null) {
|
||||
db.execSQL("DELETE FROM " + CATEGORIES_NAME + " WHERE " + CATEGORIES_FILTER_ID + " = ?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
||||
new Object[] { filter.getFilterId() });
|
||||
addFilter(filter, db, true);
|
||||
db.execSQL("UPDATE " + FILTER_NAME + " SET " + FILTER_COL_FILTERBYNAME + " = ?, " + FILTER_COL_NAME + " = ? " + " WHERE " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
|
||||
+ FILTER_COL_ID + "= ?", new Object[] { filter.getFilterByName(), filter.getName(), filter.getFilterId() }); //$NON-NLS-1$
|
||||
updateName(db, filter);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean deleteFilter(PoiFilter p){
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
private void updateName(SQLiteDatabase db, PoiFilter filter) {
|
||||
db.execSQL("UPDATE " + FILTER_NAME + " SET " + FILTER_COL_FILTERBYNAME + " = ?, " + FILTER_COL_NAME + " = ? " + " WHERE " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
|
||||
+ FILTER_COL_ID + "= ?", new Object[] { filter.getFilterByName(), filter.getName(), filter.getFilterId() }); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
protected boolean deleteFilter(SQLiteDatabase db, PoiFilter p){
|
||||
if(db != null){
|
||||
db.execSQL("DELETE FROM " + FILTER_NAME + " WHERE " +FILTER_COL_ID + " = ?",new Object[]{p.getFilterId()}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
||||
db.execSQL("DELETE FROM " + CATEGORIES_NAME + " WHERE " +CATEGORIES_FILTER_ID + " = ?", new Object[]{p.getFilterId()}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
||||
|
|
|
@ -21,6 +21,7 @@ public class SearchByNameFilter extends PoiFilter {
|
|||
public SearchByNameFilter(OsmandApplication application) {
|
||||
super(application.getString(R.string.poi_filter_by_name), FILTER_ID, new LinkedHashMap<AmenityType, LinkedHashSet<String>>(), application);
|
||||
this.distanceToSearchValues = new double[] {100, 1000, 5000};
|
||||
this.isStandardFilter = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -108,18 +108,31 @@ public class EditPOIFilterActivity extends OsmandListActivity {
|
|||
});
|
||||
|
||||
((ImageButton) findViewById(R.id.SaveButton)).setOnClickListener(new OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
savePoiFilter();
|
||||
}
|
||||
});
|
||||
|
||||
((ImageButton) findViewById(R.id.DeleteButton)).setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
removePoiFilter();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
Bundle bundle = this.getIntent().getExtras();
|
||||
String filterId = bundle.getString(AMENITY_FILTER);
|
||||
|
||||
helper = ((OsmandApplication)getApplication()).getPoiFilters();
|
||||
filter = helper.getFilterById(filterId);
|
||||
if(filter.isStandardFilter()){
|
||||
((ImageButton) findViewById(R.id.DeleteButton)).setVisibility(View.GONE);
|
||||
} else {
|
||||
((ImageButton) findViewById(R.id.DeleteButton)).setVisibility(View.VISIBLE);
|
||||
}
|
||||
titleBar.getTitleView().setText(getString(R.string.filterpoi_activity) + " - " + filter.getName());
|
||||
|
||||
setListAdapter(new AmenityAdapter(AmenityType.getCategories()));
|
||||
|
@ -160,23 +173,7 @@ public class EditPOIFilterActivity extends OsmandListActivity {
|
|||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.edit_filter_delete) {
|
||||
Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setMessage(R.string.edit_filter_delete_dialog_title);
|
||||
builder.setNegativeButton(R.string.default_buttons_no, null);
|
||||
builder.setPositiveButton(R.string.default_buttons_yes, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (helper.removePoiFilter(filter)) {
|
||||
AccessibleToast.makeText(
|
||||
EditPOIFilterActivity.this,
|
||||
MessageFormat.format(EditPOIFilterActivity.this.getText(R.string.edit_filter_delete_message).toString(),
|
||||
filter.getName()), Toast.LENGTH_SHORT).show();
|
||||
EditPOIFilterActivity.this.finish();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
removePoiFilter();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.edit_filter_save_as) {
|
||||
savePoiFilter();
|
||||
|
@ -185,6 +182,27 @@ public class EditPOIFilterActivity extends OsmandListActivity {
|
|||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
private void removePoiFilter() {
|
||||
Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setMessage(R.string.edit_filter_delete_dialog_title);
|
||||
builder.setNegativeButton(R.string.default_buttons_no, null);
|
||||
builder.setPositiveButton(R.string.default_buttons_yes, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (helper.removePoiFilter(filter)) {
|
||||
AccessibleToast.makeText(
|
||||
EditPOIFilterActivity.this,
|
||||
MessageFormat.format(EditPOIFilterActivity.this.getText(R.string.edit_filter_delete_message).toString(),
|
||||
filter.getName()), Toast.LENGTH_SHORT).show();
|
||||
EditPOIFilterActivity.this.finish();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void showDialog(final AmenityType amenity) {
|
||||
Builder builder = new AlertDialog.Builder(this);
|
||||
ScrollView scroll = new ScrollView(this);
|
||||
|
|
|
@ -55,8 +55,11 @@ public class SearchPoiFilterActivity extends ListActivity {
|
|||
@Override
|
||||
public boolean onItemLongClick(AdapterView<?> av, View v, int pos, long id) {
|
||||
PoiFilter poi = ((AmenityAdapter) getListAdapter()).getItem(pos);
|
||||
showEditActivity(poi);
|
||||
return true;
|
||||
if(!poi.isStandardFilter() || poi.getFilterId().equals(PoiFilter.CUSTOM_FILTER_ID)) {
|
||||
showEditActivity(poi);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -97,13 +100,11 @@ public class SearchPoiFilterActivity extends ListActivity {
|
|||
}
|
||||
|
||||
private void showEditActivity(PoiFilter poi) {
|
||||
if(!poi.isStandardFilter()) {
|
||||
Intent newIntent = new Intent(SearchPoiFilterActivity.this, EditPOIFilterActivity.class);
|
||||
// folder selected
|
||||
newIntent.putExtra(EditPOIFilterActivity.AMENITY_FILTER, poi.getFilterId());
|
||||
updateIntentToLaunch(newIntent);
|
||||
startActivityForResult(newIntent, 0);
|
||||
}
|
||||
Intent newIntent = new Intent(SearchPoiFilterActivity.this, EditPOIFilterActivity.class);
|
||||
// folder selected
|
||||
newIntent.putExtra(EditPOIFilterActivity.AMENITY_FILTER, poi.getFilterId());
|
||||
updateIntentToLaunch(newIntent);
|
||||
startActivityForResult(newIntent, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -131,16 +132,19 @@ public class SearchPoiFilterActivity extends ListActivity {
|
|||
|
||||
class AmenityAdapter extends ArrayAdapter<PoiFilter> {
|
||||
AmenityAdapter(List<PoiFilter> list) {
|
||||
super(SearchPoiFilterActivity.this, R.layout.searchpoi_list, list);
|
||||
super(SearchPoiFilterActivity.this, R.layout.searchpoifolder_list, list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
View row = inflater.inflate(R.layout.searchpoifolder_list, parent, false);
|
||||
View row = convertView;
|
||||
if(row == null) {
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
row = inflater.inflate(R.layout.searchpoifolder_list, parent, false);
|
||||
}
|
||||
TextView label = (TextView) row.findViewById(R.id.folder_label);
|
||||
ImageView icon = (ImageView) row.findViewById(R.id.folder_icon);
|
||||
PoiFilter model = getItem(position);
|
||||
final PoiFilter model = getItem(position);
|
||||
label.setText(model.getName());
|
||||
if(model.getFilterId().equals(PoiFilter.CUSTOM_FILTER_ID)) {
|
||||
icon.setImageResource(android.R.drawable.ic_input_get);
|
||||
|
@ -151,6 +155,20 @@ public class SearchPoiFilterActivity extends ListActivity {
|
|||
// label.setTypeface(Typeface.DEFAULT);
|
||||
icon.setImageResource(model.isStandardFilter() ? R.drawable.folder : R.drawable.tab_icon_favourite_menu);
|
||||
}
|
||||
ImageView editIcon = (ImageView) row.findViewById(R.id.folder_edit_icon);
|
||||
if (model.isStandardFilter()) {
|
||||
editIcon.setVisibility(View.GONE);
|
||||
} else {
|
||||
editIcon.setVisibility(View.VISIBLE);
|
||||
}
|
||||
editIcon.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showEditActivity(model);
|
||||
}
|
||||
});
|
||||
|
||||
return (row);
|
||||
}
|
||||
|
||||
|
|
|
@ -221,44 +221,46 @@ public class MapInfoLayer extends OsmandMapLayer {
|
|||
view.getContext(), view.getSettings());
|
||||
// register right stack
|
||||
EnumSet<ApplicationMode> all = EnumSet.allOf(ApplicationMode.class);
|
||||
EnumSet<ApplicationMode> carDefault = EnumSet.of(ApplicationMode.CAR, ApplicationMode.DEFAULT);
|
||||
EnumSet<ApplicationMode> bicyclePedestrian = EnumSet.of(ApplicationMode.BICYCLE, ApplicationMode.PEDESTRIAN);
|
||||
EnumSet<ApplicationMode> exceptCar = EnumSet.of(ApplicationMode.BICYCLE, ApplicationMode.PEDESTRIAN, ApplicationMode.DEFAULT);
|
||||
EnumSet<ApplicationMode> none = EnumSet.noneOf(ApplicationMode.class);
|
||||
RoutingHelper routingHelper = view.getApplication().getRoutingHelper();
|
||||
NextTurnInfoControl bigInfoControl = ric.createNextInfoControl(routingHelper, view.getApplication(), view.getSettings(), paintText,
|
||||
paintSubText, false);
|
||||
mapInfoControls.registerSideWidget(bigInfoControl, R.drawable.widget_next_turn, R.string.map_widget_next_turn,"next_turn", true, all, none, 5);
|
||||
mapInfoControls.registerSideWidget(bigInfoControl, R.drawable.widget_next_turn, R.string.map_widget_next_turn,"next_turn", true, carDefault, none, 5);
|
||||
NextTurnInfoControl smallInfoControl = ric.createNextInfoControl(routingHelper, view.getApplication(), view.getSettings(),
|
||||
paintSmallText, paintSmallSubText, true);
|
||||
mapInfoControls.registerSideWidget(smallInfoControl, R.drawable.widget_next_turn, R.string.map_widget_next_turn_small, "next_turn_small", true, bicyclePedestrian, none, 10);
|
||||
NextTurnInfoControl nextNextInfoControl = ric.createNextNextInfoControl(routingHelper, view.getApplication(), view.getSettings(),
|
||||
paintSmallText, paintSmallSubText, true);
|
||||
mapInfoControls.registerSideWidget(nextNextInfoControl, R.drawable.widget_next_turn, R.string.map_widget_next_next_turn, "next_next_turn",true, all, none, 15);
|
||||
mapInfoControls.registerSideWidget(nextNextInfoControl, R.drawable.widget_next_turn, R.string.map_widget_next_next_turn, "next_next_turn",true, carDefault, none, 15);
|
||||
//MiniMapControl miniMap = ric.createMiniMapControl(routingHelper, view);
|
||||
//mapInfoControls.registerSideWidget(miniMap, R.drawable.widget_next_turn, R.string.map_widget_mini_route, "mini_route", true, none, none, 20);
|
||||
// right stack
|
||||
TextInfoControl dist = ric.createDistanceControl(map, paintText, paintSubText);
|
||||
mapInfoControls.registerSideWidget(dist, R.drawable.widget_target, R.string.map_widget_distance, "distance", false, all, none, 5);
|
||||
mapInfoControls.registerSideWidget(dist, R.drawable.widget_target, R.string.map_widget_distance, "distance", false, carDefault, none, 5);
|
||||
TextInfoControl time = ric.createTimeControl(map, paintText, paintSubText);
|
||||
mapInfoControls.registerSideWidget(time, R.drawable.widget_time, R.string.map_widget_time, "time",false, all, none, 10);
|
||||
mapInfoControls.registerSideWidget(time, R.drawable.widget_time, R.string.map_widget_time, "time",false, carDefault, none, 10);
|
||||
TextInfoControl speed = ric.createSpeedControl(map, paintText, paintSubText);
|
||||
mapInfoControls.registerSideWidget(speed, R.drawable.widget_speed, R.string.map_widget_speed, "speed", false, all, none, 15);
|
||||
mapInfoControls.registerSideWidget(speed, R.drawable.widget_speed, R.string.map_widget_speed, "speed", false, carDefault, none, 15);
|
||||
TextInfoControl alt = ric.createAltitudeControl(map, paintText, paintSubText);
|
||||
mapInfoControls.registerSideWidget(alt, R.drawable.widget_altitude, R.string.map_widget_altitude, "altitude", false, EnumSet.of(ApplicationMode.PEDESTRIAN), none, 20);
|
||||
|
||||
// Top widgets
|
||||
ImageViewControl compassView = createCompassView(map);
|
||||
mapInfoControls.registerTopWidget(compassView, R.drawable.compass, R.string.map_widget_compass, "compass", MapInfoControls.LEFT_CONTROL, all, 5);
|
||||
mapInfoControls.registerTopWidget(compassView, R.drawable.compass, R.string.map_widget_compass, "compass", MapInfoControls.LEFT_CONTROL, carDefault, 5);
|
||||
View config = createConfiguration();
|
||||
mapInfoControls.registerTopWidget(config, R.drawable.widget_config, R.string.map_widget_config, "config", MapInfoControls.RIGHT_CONTROL, all, 10).required(ApplicationMode.values());
|
||||
mapInfoControls.registerTopWidget(config, R.drawable.widget_config, R.string.map_widget_config, "config", MapInfoControls.RIGHT_CONTROL, carDefault, 10).required(ApplicationMode.DEFAULT);
|
||||
ImageView lockView = lockInfoControl.createLockScreenWidget(view, map);
|
||||
mapInfoControls.registerTopWidget(lockView, R.drawable.lock_enabled, R.string.bg_service_screen_lock, "bgService", MapInfoControls.LEFT_CONTROL, all, 15);
|
||||
mapInfoControls.registerTopWidget(lockView, R.drawable.lock_enabled, R.string.bg_service_screen_lock, "bgService", MapInfoControls.LEFT_CONTROL, exceptCar, 15);
|
||||
backToLocation = createBackToLocation(map);
|
||||
mapInfoControls.registerTopWidget(backToLocation, R.drawable.default_location, R.string.map_widget_back_to_loc, "back_to_location", MapInfoControls.RIGHT_CONTROL, all, 5);
|
||||
mapInfoControls.registerTopWidget(backToLocation, R.drawable.default_location, R.string.map_widget_back_to_loc, "back_to_location", MapInfoControls.RIGHT_CONTROL, carDefault, 5);
|
||||
View globus = createGlobus();
|
||||
mapInfoControls.registerTopWidget(globus, R.drawable.globus, R.string.map_widget_map_select, "progress", MapInfoControls.RIGHT_CONTROL, none, 15);
|
||||
|
||||
topText = new TopTextView(routingHelper, map);
|
||||
mapInfoControls.registerTopWidget(topText, R.drawable.street_name, R.string.map_widget_top_text, "street_name", MapInfoControls.MAIN_CONTROL, all, 100);
|
||||
mapInfoControls.registerTopWidget(topText, R.drawable.street_name, R.string.map_widget_top_text, "street_name", MapInfoControls.MAIN_CONTROL, carDefault, 100);
|
||||
|
||||
// Register appearance widgets
|
||||
registerAppearanceWidgets();
|
||||
|
@ -808,7 +810,7 @@ public class MapInfoLayer extends OsmandMapLayer {
|
|||
pp.setTextSize(20 * scaleCoefficient);
|
||||
float ts = pp.measureText(text);
|
||||
int wth = getWidth();
|
||||
while (ts > wth && pp.getTextSize() > (14 * scaleCoefficient)) {
|
||||
while (ts > wth && pp.getTextSize() > (16 * scaleCoefficient)) {
|
||||
pp.setTextSize(pp.getTextSize() - 1);
|
||||
ts = pp.measureText(text);
|
||||
}
|
||||
|
|
|
@ -124,9 +124,9 @@ public class NextTurnInfoControl extends MapInfoControl {
|
|||
float mt = textPaint.measureText(text);
|
||||
if (!horisontalMini) {
|
||||
float startX = Math.max((getWWidth() - st - mt) / 2, 2 * scaleCoefficient);
|
||||
drawShadowText(canvas, text, startX, getWHeight() - 3 * scaleCoefficient, textPaint);
|
||||
drawShadowText(canvas, text, startX, getWHeight() - 5 * scaleCoefficient, textPaint);
|
||||
if (subtext != null) {
|
||||
drawShadowText(canvas, subtext, startX + 2 * scaleCoefficient + mt, getWHeight() - 4 * scaleCoefficient, subtextPaint);
|
||||
drawShadowText(canvas, subtext, startX + 2 * scaleCoefficient + mt, getWHeight() - 5 * scaleCoefficient, subtextPaint);
|
||||
}
|
||||
} else {
|
||||
drawShadowText(canvas, text, 72 * scaleCoefficient / miniCoeff + 2 * scaleCoefficient,
|
||||
|
|
|
@ -54,7 +54,7 @@ public abstract class AbstractPrologCommandPlayer implements CommandPlayer {
|
|||
private final int[] sortedVoiceVersions;
|
||||
private AudioFocusHelper mAudioFocusHelper;
|
||||
|
||||
private int streamType;
|
||||
protected int streamType;
|
||||
|
||||
protected AbstractPrologCommandPlayer(Context ctx, OsmandSettings settings, String voiceProvider, String configFile, int[] sortedVoiceVersions)
|
||||
throws CommandPlayerException
|
||||
|
|
|
@ -31,7 +31,6 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen
|
|||
private MediaPlayer mediaPlayer;
|
||||
// indicates that player is ready to play first file
|
||||
private List<String> filesToPlay = Collections.synchronizedList(new ArrayList<String>());
|
||||
private int streamType;
|
||||
|
||||
|
||||
public MediaCommandPlayerImpl(Context ctx, OsmandSettings settings, String voiceProvider)
|
||||
|
|
Loading…
Reference in a new issue