Merge remote-tracking branch 'origin/master'

This commit is contained in:
Weblate 2012-09-13 00:00:25 +02:00
commit 32d2e1c547
22 changed files with 1296 additions and 565 deletions

View file

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

View file

@ -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
@ -23,180 +24,138 @@ import net.osmand.osm.Way;
* @author Pavol Zibrita
*/
public class Multipolygon {
/**
* cache with the ways grouped per Ring
*/
private List<Ring> innerRings, outerRings;
protected List<Way> closedOuterWays;
protected List<Way> outerWays;
protected List<Way> closedInnerWays;
protected List<Way> innerWays;
/**
* ways added by the user
*/
private List<Way> outerWays, innerWays;
protected IdentityHashMap<Way,List<Way>> outerInnerMapping;
/**
* an optional id of the multipolygon
*/
private long id;
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
/**
* 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);
}
/**
* create a new empty multipolygon
*/
public Multipolygon(){
outerWays = new ArrayList<Way> ();
innerWays = new ArrayList<Way> ();
id = 0L;
}
/**
* create a new empty multipolygon with specified id
* @param id the id to set
*/
public Multipolygon(long id){
this();
setId(id);
}
/**
* set the id of the multipolygon
* @param newId id to set
*/
public void setId(long newId) {
id = newId;
}
/**
* get the id of the multipolygon
* @return id
*/
public long getId() {
return id;
}
/**
* 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());
}
private boolean isClosed(Way newPoly) {
List<Node> ns = newPoly.getNodes();
return !ns.isEmpty() && ns.get(0).getId() == ns.get(ns.size()-1).getId();
}
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;
/**
* 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){
TreeSet<Ring> outers = new TreeSet<Ring>();
TreeSet<Ring> inners = new TreeSet<Ring>();
for (Ring outer : getOuterRings()) {
if (outer.containsPoint(latitude, longitude)) {
outers.add(outer);
}
}
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;
for(Ring inner : getInnerRings()) {
if (inner.containsPoint(latitude, longitude)) {
inners.add(inner);
}
}
//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);
}
}
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);
}
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);
}
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;
}
}
}
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;
}
}
if (inIsIn) {
result.add(in);
}
}
return result;
/**
* 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,50 +163,88 @@ 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);
}
return innerWays;
}
/**
* get the number of outer Rings
* @return
*/
public int countOuterPolygons() {
groupInRings();
return zeroSizeIfNull(getOuterRings());
}
private List<Way> getClosedInnerWays() {
if (closedInnerWays == null) {
closedInnerWays = new ArrayList<Way>(1);
/**
* 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;
}
public int countOuterPolygons()
{
return zeroSizeIfNull(outerWays) + zeroSizeIfNull(closedOuterWays);
}
public boolean hasOpenedPolygons()
{
return zeroSizeIfNull(outerWays) != 0;
set = getInnerRings();
for (Ring r : set) {
if (!r.isClosed()) {
return false;
}
}
return true;
}
private int zeroSizeIfNull(List<Way> list) {
return list != null ? list.size() : 0;
/**
* 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 void addInnerWay(Way es) {
addNewPolygonPart(getInnerWays(), getClosedInnerWays(), new Way(es));
/**
* Add an inner way to the multiPolygon
* @param w the way to add
*/
public void addInnerWay(Way w) {
getInnerWays().add(w);
innerRings = null;
}
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();
}
}

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

View file

@ -26,8 +26,9 @@ import net.osmand.Algoritms;
import net.osmand.IProgress;
import net.osmand.binary.OsmandOdb.MapData;
import net.osmand.binary.OsmandOdb.MapDataBlock;
import net.osmand.data.Boundary;
import net.osmand.data.MapAlgorithms;
import net.osmand.data.Multipolygon;
import net.osmand.data.Ring;
import net.osmand.data.preparation.MapZooms.MapZoomPair;
import net.osmand.osm.Entity;
import net.osmand.osm.Entity.EntityId;
@ -113,234 +114,100 @@ public class IndexVectorMapCreator extends AbstractIndexPartCreator {
}
}
/**
* index a multipolygon into the database
* only multipolygons without admin_level and with type=multipolygon are indexed
* broken multipolygons are also indexed, inner ways are sometimes left out, broken rings are split and closed
* broken multipolygons will normally be logged
* @param e the entity to index
* @param ctx the database context
* @throws SQLException
*/
private void indexMultiPolygon(Entity e, OsmDbAccessorContext ctx) throws SQLException {
if (e instanceof Relation && "multipolygon".equals(e.getTag(OSMTagKey.TYPE))) { //$NON-NLS-1$
if(e.getTag(OSMTagKey.ADMIN_LEVEL) != null) {
// don't index boundaries as multipolygon (only areas ideally are multipolygon)
return;
}
ctx.loadEntityRelation((Relation) e);
Map<Entity, String> entities = ((Relation) e).getMemberEntities();
// Don't handle things that aren't multipolygon, and nothing administrative
if (! (e instanceof Relation) ||
! "multipolygon".equals(e.getTag(OSMTagKey.TYPE)) ||
e.getTag(OSMTagKey.ADMIN_LEVEL) != null ) return;
boolean outerFound = false;
for (Entity es : entities.keySet()) {
if (es instanceof Way) {
boolean inner = "inner".equals(entities.get(es)); //$NON-NLS-1$
if (!inner) {
outerFound = true;
// This is incorrect (it should be intersection of all boundaries)
// Currently it causes an issue with coastline (if one line is coastline)
// for (String t : es.getTagKeySet()) {
// e.putTag(t, es.getTag(t));
// }
break;
}
}
}
if (!outerFound) {
logMapDataWarn.warn("Probably map bug: Multipoligon id=" + e.getId() + " contains only inner ways : "); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
ctx.loadEntityRelation((Relation) e);
Map<Entity, String> entities = ((Relation) e).getMemberEntities();
renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(0).getMaxZoom(), typeUse, addtypeUse, namesUse, tempNameUse);
if (typeUse.size() > 0) {
List<List<Way>> completedRings = new ArrayList<List<Way>>();
List<List<Way>> incompletedRings = new ArrayList<List<Way>>();
for (Entity es : entities.keySet()) {
if (es instanceof Way) {
if (!((Way) es).getNodeIds().isEmpty()) {
combineMultiPolygons((Way) es, completedRings, incompletedRings);
}
}
}
// skip incompleted rings and do not add whole relation ?
if (!incompletedRings.isEmpty()) {
logMapDataWarn.warn("In multipolygon " + e.getId() + " there are incompleted ways : " + incompletedRings);
return;
// completedRings.addAll(incompletedRings);
}
// create a multipolygon object for this
Multipolygon original = new Multipolygon(e.getId());
// skip completed rings that are not one type
for (List<Way> l : completedRings) {
boolean innerType = "inner".equals(entities.get(l.get(0))); //$NON-NLS-1$
for (Way way : l) {
boolean inner = "inner".equals(entities.get(way)); //$NON-NLS-1$
if (innerType != inner) {
logMapDataWarn
.warn("Probably map bug: Multipoligon contains outer and inner ways.\n" + //$NON-NLS-1$
"Way:"
+ way.getId()
+ " is strange part of completed ring. InnerType:" + innerType + " way inner: " + inner + " way inner string:" + entities.get(way)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
return;
}
}
}
// That check is not strictly needed on preproccessing step because client can handle it
Node nodeOut = checkOuterWaysEncloseInnerWays(completedRings, entities);
if (nodeOut != null) {
logMapDataWarn.warn("Map bug: Multipoligon contains 'inner' way point outside of 'outer' border.\n" + //$NON-NLS-1$
"Multipolygon id : " + e.getId() + ", inner node out id : " + nodeOut.getId()); //$NON-NLS-1$
}
List<Node> outerWaySrc = new ArrayList<Node>();
List<List<Node>> innerWays = new ArrayList<List<Node>>();
TIntArrayList typeToSave = new TIntArrayList(typeUse);
long baseId = 0;
for (List<Way> l : completedRings) {
boolean innerType = "inner".equals(entities.get(l.get(0))); //$NON-NLS-1$
if (!innerType && !outerWaySrc.isEmpty()) {
logMapDataWarn.warn("Map bug: Multipoligon contains many 'outer' borders.\n" + //$NON-NLS-1$
"Multipolygon id : " + e.getId() + ", outer way id : " + l.get(0).getId()); //$NON-NLS-1$
return;
}
List<Node> toCollect;
if (innerType) {
toCollect = new ArrayList<Node>();
innerWays.add(toCollect);
} else {
toCollect = outerWaySrc;
}
for (Way way : l) {
toCollect.addAll(way.getNodes());
if (!innerType) {
TIntArrayList out = multiPolygonsWays.put(way.getId(), typeToSave);
if(out == null){
baseId = -way.getId();
}
}
}
}
if(baseId == 0){
// use base id as well?
baseId = notUsedId --;
}
nextZoom: for (int level = 0; level < mapZooms.size(); level++) {
renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(level).getMaxZoom(), typeUse, addtypeUse, namesUse,
tempNameUse);
if (typeUse.isEmpty()) {
continue;
}
long id = convertBaseIdToGeneratedId(baseId, level);
// simplify route
List<Node> outerWay = outerWaySrc;
int zoomToSimplify = mapZooms.getLevel(level).getMaxZoom() - 1;
if (zoomToSimplify < 15) {
outerWay = simplifyCycleWay(outerWay, zoomToSimplify, zoomWaySmothness);
if (outerWay == null) {
continue nextZoom;
}
List<List<Node>> newinnerWays = new ArrayList<List<Node>>();
for (List<Node> ls : innerWays) {
ls = simplifyCycleWay(ls, zoomToSimplify, zoomWaySmothness);
if (ls != null) {
newinnerWays.add(ls);
}
}
innerWays = newinnerWays;
}
insertBinaryMapRenderObjectIndex(mapTree[level], outerWay, innerWays, namesUse, id, true, typeUse, addtypeUse, true);
}
}
}
}
private Node checkOuterWaysEncloseInnerWays(List<List<Way>> completedRings, Map<Entity, String> entities) {
List<List<Way>> innerWays = new ArrayList<List<Way>>();
Boundary outerBoundary = new Boundary();
Node toReturn = null;
for (List<Way> ring : completedRings) {
boolean innerType = "inner".equals(entities.get(ring.get(0))); //$NON-NLS-1$
if (!innerType) {
outerBoundary.addOuterWays(ring);
} else {
innerWays.add(ring);
}
}
for (List<Way> innerRing : innerWays) {
ring: for (Way innerWay : innerRing) {
for (Node node : innerWay.getNodes()) {
if (!outerBoundary.containsPoint(node.getLatitude(), node.getLongitude())) {
if (toReturn == null) {
toReturn = node;
}
completedRings.remove(innerRing);
break ring;
}
}
}
}
return toReturn;
}
private List<Way> reverse(List<Way> l) {
Collections.reverse(l);
for(Way w : l){
w.getNodeIds().reverse();
Collections.reverse(w.getNodes());
}
return l;
}
private List<Way> appendLists(List<Way> w1, List<Way> w2){
w1.addAll(w2);
return w1;
}
//TODO Can the Multipolygon class be the one that replaces this?
private void combineMultiPolygons(Way w, List<List<Way>> completedRings, List<List<Way>> incompletedRings) {
long lId = w.getEntityIds().get(w.getEntityIds().size() - 1).getId().longValue();
long fId = w.getEntityIds().get(0).getId().longValue();
if (fId == lId) {
completedRings.add(Collections.singletonList(w));
} else {
List<Way> l = new ArrayList<Way>();
l.add(w);
boolean add = true;
for (int k = 0; k < incompletedRings.size();) {
boolean remove = false;
List<Way> i = incompletedRings.get(k);
Way last = i.get(i.size() - 1);
Way first = i.get(0);
long lastId = last.getEntityIds().get(last.getEntityIds().size() - 1).getId().longValue();
long firstId = first.getEntityIds().get(0).getId().longValue();
if (fId == lastId) {
remove = true;
l = appendLists(i, l);
fId = firstId;
} else if (lId == firstId) {
l = appendLists(l, i);
remove = true;
lId = lastId;
} else if (lId == lastId) {
l = appendLists(l, reverse(i));
remove = true;
lId = firstId;
} else if (fId == firstId) {
l = appendLists(reverse(i), l);
remove = true;
fId = lastId;
}
if (remove) {
incompletedRings.remove(k);
// fill the multipolygon with all ways from the Relation
for (Entity es : entities.keySet()) {
if (es instanceof Way) {
boolean inner = "inner".equals(entities.get(es)); //$NON-NLS-1$
if (inner) {
original.addInnerWay((Way) es);
} else {
k++;
}
if (fId == lId) {
completedRings.add(l);
add = false;
break;
original.addOuterWay((Way) es);
}
}
if (add) {
incompletedRings.add(l);
}
// Log if something is wrong
if (!original.hasOpenedPolygons()) {
logMapDataWarn.warn("Multipolygon has unclosed parts: Multipoligon id=" + e.getId()); //$NON-NLS-1$ //$NON-NLS-2$
}
renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(0).getMaxZoom(), typeUse, addtypeUse, namesUse, tempNameUse);
//Don't add multipolygons with an unknown type
if (typeUse.size() == 0) return;
// Log the fact that Rings aren't complete, but continue with the relation, try to close it as well as possible
if (!original.areRingsComplete()) {
logMapDataWarn.warn("In multipolygon " + e.getId() + " there are incompleted ways");
}
// Rings with different types (inner or outer) in one ring will be logged in the previous case
// The Rings are only composed by type, so if one way gets in a different Ring, the rings will be incomplete
List<Multipolygon> multipolygons = original.splitPerOuterRing(logMapDataWarn);
for (Multipolygon m : multipolygons) {
// innerWays are new closed ways
List<List<Node>> innerWays = new ArrayList<List<Node>>();
for (Ring r : m.getInnerRings()) {
innerWays.add(r.getBorder().getNodes());
}
// don't use the relation ids. Create new ones
long baseId = notUsedId --;
nextZoom: for (int level = 0; level < mapZooms.size(); level++) {
renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(level).getMaxZoom(), typeUse, addtypeUse, namesUse,
tempNameUse);
if (typeUse.isEmpty()) {
continue;
}
long id = convertBaseIdToGeneratedId(baseId, level);
// simplify route
List<Node> outerWay = m.getOuterNodes();
int zoomToSimplify = mapZooms.getLevel(level).getMaxZoom() - 1;
if (zoomToSimplify < 15) {
outerWay = simplifyCycleWay(outerWay, zoomToSimplify, zoomWaySmothness);
if (outerWay == null) {
continue nextZoom;
}
List<List<Node>> newinnerWays = new ArrayList<List<Node>>();
for (List<Node> ls : innerWays) {
ls = simplifyCycleWay(ls, zoomToSimplify, zoomWaySmothness);
if (ls != null) {
newinnerWays.add(ls);
}
}
innerWays = newinnerWays;
}
insertBinaryMapRenderObjectIndex(mapTree[level], outerWay, innerWays, namesUse, id, true, typeUse, addtypeUse, true);
}
}
}
public static List<Node> simplifyCycleWay(List<Node> ns, int zoom, int zoomWaySmothness) throws SQLException {
if (checkForSmallAreas(ns, zoom + Math.min(zoomWaySmothness / 2, 3), 2, 4)) {
return null;

View file

@ -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"/>

View file

@ -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"

View file

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

View file

@ -9,5 +9,5 @@
<TextView android:id="@+id/poi_label" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:textSize="22sp" />
</LinearLayout>

View file

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

View file

@ -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 &gt; 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 &gt; 20km (experimental)</string>
</resources>

View file

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

View file

@ -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&#8230;</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&#8230;</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&#8230;</string>
<string name="install_selected_build">Do you want to install OsmAnd - {0} from {1} {2} MB ?</string>

View file

@ -153,6 +153,7 @@ public class OsmandApplication extends Application {
public PoiFiltersHelper getPoiFilters() {
if (poiFilters == null) {
poiFilters = new PoiFiltersHelper(this);
poiFilters.updateFilters(true);
}
return poiFilters;
}

View file

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

View file

@ -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();
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();
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));
// 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;
}
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(PoiFilter p){
SQLiteDatabase db = getWritableDatabase();
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$

View file

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

View file

@ -108,20 +108,33 @@ 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();
@ -184,6 +181,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);

View file

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

View file

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

View file

@ -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,

View file

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

View file

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