Merge branch 'master' into speed_cameras

This commit is contained in:
Dmitriy Ruban 2020-06-19 13:43:35 +03:00 committed by GitHub
commit e75841b312
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 798 additions and 99482 deletions

View file

@ -43,6 +43,7 @@ task collectTestResources(type: Copy) {
into "src/test/resources/" into "src/test/resources/"
from("../../resources/test-resources") { from("../../resources/test-resources") {
include "*" include "*"
include "/search/*"
} }
from("../../resources/poi") { from("../../resources/poi") {
include "poi_types.xml" include "poi_types.xml"

View file

@ -957,6 +957,24 @@ public class GPXUtilities {
return ls; return ls;
} }
public static QuadRect calculateBounds(List<WptPt> pts) {
QuadRect trackBounds = new QuadRect(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY,
Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
updateBounds(trackBounds, pts, 0);
return trackBounds;
}
public static void updateBounds(QuadRect trackBounds, List<WptPt> pts, int startIndex) {
for (int i = startIndex; i < pts.size(); i++) {
WptPt pt = pts.get(i);
trackBounds.right = Math.max(trackBounds.right, pt.lon);
trackBounds.left = Math.min(trackBounds.left, pt.lon);
trackBounds.top = Math.max(trackBounds.top, pt.lat);
trackBounds.bottom = Math.min(trackBounds.bottom, pt.lat);
}
}
public static class GPXFile extends GPXExtensions { public static class GPXFile extends GPXExtensions {
public String author; public String author;
public Metadata metadata; public Metadata metadata;

View file

@ -221,7 +221,7 @@ public class BinaryMapAddressReaderAdapter {
int fp = codedIS.getTotalBytesRead(); int fp = codedIS.getTotalBytesRead();
int length = codedIS.readRawVarint32(); int length = codedIS.readRawVarint32();
int oldLimit = codedIS.pushLimit(length); int oldLimit = codedIS.pushLimit(length);
City c = readCityHeader(new DefaultCityMatcher(matcher), fp, additionalTagsTable); City c = readCityHeader(resultMatcher, new DefaultCityMatcher(matcher), fp, additionalTagsTable);
if (c != null) { if (c != null) {
if (resultMatcher == null || resultMatcher.publish(c)) { if (resultMatcher == null || resultMatcher.publish(c)) {
cities.add(c); cities.add(c);
@ -256,6 +256,7 @@ public class BinaryMapAddressReaderAdapter {
int oldLimit = codedIS.pushLimit(length); int oldLimit = codedIS.pushLimit(length);
readStreet(s, null, false, x >> 7, y >> 7, city.isPostcode() ? city.getName() : null, readStreet(s, null, false, x >> 7, y >> 7, city.isPostcode() ? city.getName() : null,
attributeTagsTable); attributeTagsTable);
publishRawData(resultMatcher, s);
if (resultMatcher == null || resultMatcher.publish(s)) { if (resultMatcher == null || resultMatcher.publish(s)) {
city.registerStreet(s); city.registerStreet(s);
} }
@ -303,7 +304,7 @@ public class BinaryMapAddressReaderAdapter {
} }
} }
protected City readCityHeader(CityMatcher matcher, int filePointer, List<String> additionalTagsTable) throws IOException { protected City readCityHeader(SearchRequest<? super City> resultMatcher, CityMatcher matcher, int filePointer, List<String> additionalTagsTable) throws IOException {
int x = 0; int x = 0;
int y = 0; int y = 0;
City c = null; City c = null;
@ -313,6 +314,7 @@ public class BinaryMapAddressReaderAdapter {
int tag = WireFormat.getTagFieldNumber(t); int tag = WireFormat.getTagFieldNumber(t);
switch (tag) { switch (tag) {
case 0: case 0:
publishRawData(resultMatcher, c);
return (matcher == null || matcher.matches(c)) ? c : null; return (matcher == null || matcher.matches(c)) ? c : null;
case OsmandOdb.CityIndex.CITY_TYPE_FIELD_NUMBER: case OsmandOdb.CityIndex.CITY_TYPE_FIELD_NUMBER:
int type = codedIS.readUInt32(); int type = codedIS.readUInt32();
@ -445,6 +447,7 @@ public class BinaryMapAddressReaderAdapter {
if (loadBuildingsAndIntersected) { if (loadBuildingsAndIntersected) {
int oldLimit = codedIS.pushLimit(length); int oldLimit = codedIS.pushLimit(length);
Building b = readBuilding(offset, x, y, additionalTagsTable); Building b = readBuilding(offset, x, y, additionalTagsTable);
publishRawData(buildingsMatcher, b);
if (postcodeFilter == null || postcodeFilter.equalsIgnoreCase(b.getPostcode())) { if (postcodeFilter == null || postcodeFilter.equalsIgnoreCase(b.getPostcode())) {
if (buildingsMatcher == null || buildingsMatcher.publish(b)) { if (buildingsMatcher == null || buildingsMatcher.publish(b)) {
s.addBuilding(b); s.addBuilding(b);
@ -688,7 +691,7 @@ public class BinaryMapAddressReaderAdapter {
codedIS.seek(contOffset); codedIS.seek(contOffset);
int len = codedIS.readRawVarint32(); int len = codedIS.readRawVarint32();
int old = codedIS.pushLimit(len); int old = codedIS.pushLimit(len);
obj = readCityHeader(null, contOffset, reg.attributeTagsTable); obj = readCityHeader(req, null, contOffset, reg.attributeTagsTable);
codedIS.popLimit(old); codedIS.popLimit(old);
} }
if (obj != null) { if (obj != null) {
@ -701,6 +704,7 @@ public class BinaryMapAddressReaderAdapter {
readStreet(s, null, false, MapUtils.get31TileNumberX(l.getLongitude()) >> 7, readStreet(s, null, false, MapUtils.get31TileNumberX(l.getLongitude()) >> 7,
MapUtils.get31TileNumberY(l.getLatitude()) >> 7, obj.isPostcode() ? obj.getName() : null, MapUtils.get31TileNumberY(l.getLatitude()) >> 7, obj.isPostcode() ? obj.getName() : null,
reg.attributeTagsTable); reg.attributeTagsTable);
publishRawData(req, s);
boolean matches = stringMatcher.matches(s.getName()); boolean matches = stringMatcher.matches(s.getName());
if (!matches) { if (!matches) {
for (String n : s.getAllNames()) { for (String n : s.getAllNames()) {
@ -727,7 +731,8 @@ public class BinaryMapAddressReaderAdapter {
codedIS.seek(offset); codedIS.seek(offset);
int len = codedIS.readRawVarint32(); int len = codedIS.readRawVarint32();
int old = codedIS.pushLimit(len); int old = codedIS.pushLimit(len);
City obj = readCityHeader(cityPostcodeMatcher, list.get(j), reg.attributeTagsTable); City obj = readCityHeader(req, cityPostcodeMatcher, list.get(j), reg.attributeTagsTable);
publishRawData(req, obj);
if (obj != null && !published.contains(offset)) { if (obj != null && !published.contains(offset)) {
req.publish(obj); req.publish(obj);
published.add(offset); published.add(offset);
@ -805,4 +810,9 @@ public class BinaryMapAddressReaderAdapter {
} }
} }
private <T> void publishRawData(SearchRequest<T> resultMatcher, T obj) {
if (resultMatcher != null && obj != null) {
resultMatcher.collectRawData(obj);
}
}
} }

View file

@ -496,7 +496,7 @@ public class BinaryMapIndexReader {
} }
} }
Iterator<Entry<TransportIndex, TIntArrayList>> it = groupPoints.entrySet().iterator(); Iterator<Entry<TransportIndex, TIntArrayList>> it = groupPoints.entrySet().iterator();
if (it.hasNext()) { while (it.hasNext()) {
Entry<TransportIndex, TIntArrayList> e = it.next(); Entry<TransportIndex, TIntArrayList> e = it.next();
TransportIndex ind = e.getKey(); TransportIndex ind = e.getKey();
TIntArrayList pointers = e.getValue(); TIntArrayList pointers = e.getValue();
@ -1457,8 +1457,14 @@ public class BinaryMapIndexReader {
public static <T> SearchRequest<T> buildAddressByNameRequest(ResultMatcher<T> resultMatcher, String nameRequest, public static <T> SearchRequest<T> buildAddressByNameRequest(ResultMatcher<T> resultMatcher, String nameRequest,
StringMatcherMode matcherMode) { StringMatcherMode matcherMode) {
return buildAddressByNameRequest(resultMatcher, null, nameRequest, matcherMode);
}
public static <T> SearchRequest<T> buildAddressByNameRequest(ResultMatcher<T> resultMatcher, ResultMatcher<T> rawDataCollector, String nameRequest,
StringMatcherMode matcherMode) {
SearchRequest<T> request = new SearchRequest<T>(); SearchRequest<T> request = new SearchRequest<T>();
request.resultMatcher = resultMatcher; request.resultMatcher = resultMatcher;
request.rawDataCollector = rawDataCollector;
request.nameQuery = nameRequest.trim(); request.nameQuery = nameRequest.trim();
request.matcherMode = matcherMode; request.matcherMode = matcherMode;
return request; return request;
@ -1542,6 +1548,10 @@ public class BinaryMapIndexReader {
public static SearchRequest<Amenity> buildSearchPoiRequest(int x, int y, String nameFilter, int sleft, int sright, int stop, int sbottom, ResultMatcher<Amenity> resultMatcher) { public static SearchRequest<Amenity> buildSearchPoiRequest(int x, int y, String nameFilter, int sleft, int sright, int stop, int sbottom, ResultMatcher<Amenity> resultMatcher) {
return buildSearchPoiRequest(x, y, nameFilter, sleft, sright, stop, sbottom, resultMatcher, null);
}
public static SearchRequest<Amenity> buildSearchPoiRequest(int x, int y, String nameFilter, int sleft, int sright, int stop, int sbottom, ResultMatcher<Amenity> resultMatcher, ResultMatcher<Amenity> rawDataCollector) {
SearchRequest<Amenity> request = new SearchRequest<Amenity>(); SearchRequest<Amenity> request = new SearchRequest<Amenity>();
request.x = x; request.x = x;
request.y = y; request.y = y;
@ -1550,6 +1560,7 @@ public class BinaryMapIndexReader {
request.top = stop; request.top = stop;
request.bottom = sbottom; request.bottom = sbottom;
request.resultMatcher = resultMatcher; request.resultMatcher = resultMatcher;
request.rawDataCollector = rawDataCollector;
request.nameQuery = nameFilter.trim(); request.nameQuery = nameFilter.trim();
return request; return request;
} }
@ -1634,6 +1645,7 @@ public class BinaryMapIndexReader {
private boolean ocean = false; private boolean ocean = false;
private ResultMatcher<T> resultMatcher; private ResultMatcher<T> resultMatcher;
private ResultMatcher<T> rawDataCollector;
// 31 zoom tiles // 31 zoom tiles
// common variables // common variables
@ -1711,6 +1723,12 @@ public class BinaryMapIndexReader {
return false; return false;
} }
public void collectRawData(T obj) {
if (rawDataCollector != null) {
rawDataCollector.publish(obj);
}
}
protected void publishOceanTile(boolean ocean) { protected void publishOceanTile(boolean ocean) {
if (ocean) { if (ocean) {
this.ocean = true; this.ocean = true;

View file

@ -26,7 +26,6 @@ import net.osmand.data.Amenity.AmenityRoutePoint;
import net.osmand.data.LatLon; import net.osmand.data.LatLon;
import net.osmand.osm.MapPoiTypes; import net.osmand.osm.MapPoiTypes;
import net.osmand.osm.PoiCategory; import net.osmand.osm.PoiCategory;
import net.osmand.osm.PoiType;
import net.osmand.util.MapUtils; import net.osmand.util.MapUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@ -591,6 +590,7 @@ public class BinaryMapPoiReaderAdapter {
} }
} }
if (matches) { if (matches) {
req.collectRawData(am);
req.publish(am); req.publish(am);
} }
} }
@ -637,6 +637,7 @@ public class BinaryMapPoiReaderAdapter {
int yp = (int) MapUtils.getTileNumberY(zSkip, am.getLocation().getLatitude()); int yp = (int) MapUtils.getTileNumberY(zSkip, am.getLocation().getLatitude());
long valSkip = (((long) xp) << zSkip) | yp; long valSkip = (((long) xp) << zSkip) | yp;
if (!toSkip.contains(valSkip)) { if (!toSkip.contains(valSkip)) {
req.collectRawData(am);
boolean publish = req.publish(am); boolean publish = req.publish(am);
if (publish) { if (publish) {
read = true; read = true;
@ -647,6 +648,7 @@ public class BinaryMapPoiReaderAdapter {
return read; return read;
} }
} else { } else {
req.collectRawData(am);
if (req.publish(am)) { if (req.publish(am)) {
read = true; read = true;
} }

View file

@ -23,7 +23,7 @@ import net.osmand.osm.edit.Way;
import net.osmand.util.MapUtils; import net.osmand.util.MapUtils;
public class TransportStopsRouteReader { public class TransportStopsRouteReader {
public static final int MISSING_STOP_SEARCH_RADIUS = 30000; public static final int MISSING_STOP_SEARCH_RADIUS = 50000;
TLongObjectHashMap<TransportRoute> combinedRoutesCache = new TLongObjectHashMap<TransportRoute>(); TLongObjectHashMap<TransportRoute> combinedRoutesCache = new TLongObjectHashMap<TransportRoute>();
Map<BinaryMapIndexReader, TIntObjectHashMap<TransportRoute>> routesFilesCache = new LinkedHashMap<BinaryMapIndexReader, Map<BinaryMapIndexReader, TIntObjectHashMap<TransportRoute>> routesFilesCache = new LinkedHashMap<BinaryMapIndexReader,
TIntObjectHashMap<TransportRoute>>(); TIntObjectHashMap<TransportRoute>>();
@ -128,6 +128,7 @@ public class TransportStopsRouteReader {
} }
} }
} }
return routesToLoad; return routesToLoad;
} }

View file

@ -516,7 +516,7 @@ public class SearchUICore {
} }
currentSearchResult = collection; currentSearchResult = collection;
if (phrase.getSettings().isExportObjects()) { if (phrase.getSettings().isExportObjects()) {
//rm.createTestJSON(collection); rm.createTestJSON(collection);
} }
rm.searchFinished(phrase); rm.searchFinished(phrase);
if (onResultsComplete != null) { if (onResultsComplete != null) {

View file

@ -35,6 +35,7 @@ import net.osmand.util.MapUtils;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
@ -203,7 +204,9 @@ public class SearchCoreFactory {
} }
} }
if (!leftUnknownSearchWords.isEmpty() && api != null && api.isSearchAvailable(phrase)) { if (!leftUnknownSearchWords.isEmpty() && api != null && api.isSearchAvailable(phrase)) {
SearchPhrase nphrase = phrase.selectWord(res, leftUnknownSearchWords, phrase.isLastUnknownSearchWordComplete()); SearchPhrase nphrase = phrase.selectWord(res, leftUnknownSearchWords,
phrase.isLastUnknownSearchWordComplete() ||
!leftUnknownSearchWords.contains(phrase.getLastUnknownSearchWord()));
SearchResult prev = resultMatcher.setParentSearchResult(publish ? res : SearchResult prev = resultMatcher.setParentSearchResult(publish ? res :
resultMatcher.getParentSearchResult()); resultMatcher.getParentSearchResult());
api.search(nphrase, resultMatcher); api.search(nphrase, resultMatcher);
@ -397,9 +400,6 @@ public class SearchCoreFactory {
int limit = 0; int limit = 0;
@Override @Override
public boolean publish(MapObject object) { public boolean publish(MapObject object) {
if (phrase.getSettings().isExportObjects()) {
resultMatcher.exportObject(phrase, object);
}
if (isCancelled()) { if (isCancelled()) {
return false; return false;
} }
@ -492,6 +492,23 @@ public class SearchCoreFactory {
resultMatcher.isCancelled(); resultMatcher.isCancelled();
} }
}; };
ResultMatcher<MapObject> rawDataCollector = null;
if (phrase.getSettings().isExportObjects()) {
rawDataCollector = new ResultMatcher<MapObject>() {
@Override
public boolean publish(MapObject object) {
resultMatcher.exportObject(phrase, object);
return true;
}
@Override
public boolean isCancelled() {
return false;
}
};
}
Iterator<BinaryMapIndexReader> offlineIterator = phrase.getRadiusOfflineIndexes(DEFAULT_ADDRESS_BBOX_RADIUS * 5, Iterator<BinaryMapIndexReader> offlineIterator = phrase.getRadiusOfflineIndexes(DEFAULT_ADDRESS_BBOX_RADIUS * 5,
SearchPhraseDataType.ADDRESS); SearchPhraseDataType.ADDRESS);
String wordToSearch = phrase.getUnknownWordToSearch(); String wordToSearch = phrase.getUnknownWordToSearch();
@ -499,7 +516,7 @@ public class SearchCoreFactory {
BinaryMapIndexReader r = offlineIterator.next(); BinaryMapIndexReader r = offlineIterator.next();
currentFile[0] = r; currentFile[0] = r;
immediateResults.clear(); immediateResults.clear();
SearchRequest<MapObject> req = BinaryMapIndexReader.buildAddressByNameRequest(rm, wordToSearch.toLowerCase(), SearchRequest<MapObject> req = BinaryMapIndexReader.buildAddressByNameRequest(rm, rawDataCollector, wordToSearch.toLowerCase(),
phrase.isMainUnknownSearchWordComplete() ? StringMatcherMode.CHECK_EQUALS_FROM_SPACE phrase.isMainUnknownSearchWordComplete() ? StringMatcherMode.CHECK_EQUALS_FROM_SPACE
: StringMatcherMode.CHECK_STARTS_FROM_SPACE); : StringMatcherMode.CHECK_STARTS_FROM_SPACE);
if (locSpecified) { if (locSpecified) {
@ -549,6 +566,22 @@ public class SearchCoreFactory {
final NameStringMatcher nm = phrase.getMainUnknownNameStringMatcher(); final NameStringMatcher nm = phrase.getMainUnknownNameStringMatcher();
QuadRect bbox = phrase.getRadiusBBoxToSearch(BBOX_RADIUS_INSIDE); QuadRect bbox = phrase.getRadiusBBoxToSearch(BBOX_RADIUS_INSIDE);
final Set<String> ids = new HashSet<String>(); final Set<String> ids = new HashSet<String>();
ResultMatcher<Amenity> rawDataCollector = null;
if (phrase.getSettings().isExportObjects()) {
rawDataCollector = new ResultMatcher<Amenity>() {
@Override
public boolean publish(Amenity object) {
resultMatcher.exportObject(phrase, object);
return true;
}
@Override
public boolean isCancelled() {
return false;
}
};
}
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest((int) bbox.centerX(), SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest((int) bbox.centerX(),
(int) bbox.centerY(), searchWord, (int) bbox.left, (int) bbox.right, (int) bbox.top, (int) bbox.centerY(), searchWord, (int) bbox.left, (int) bbox.right, (int) bbox.top,
(int) bbox.bottom, new ResultMatcher<Amenity>() { (int) bbox.bottom, new ResultMatcher<Amenity>() {
@ -556,9 +589,6 @@ public class SearchCoreFactory {
@Override @Override
public boolean publish(Amenity object) { public boolean publish(Amenity object) {
if (phrase.getSettings().isExportObjects()) {
resultMatcher.exportObject(phrase, object);
}
if (limit++ > LIMIT) { if (limit++ > LIMIT) {
return false; return false;
} }
@ -598,7 +628,7 @@ public class SearchCoreFactory {
public boolean isCancelled() { public boolean isCancelled() {
return resultMatcher.isCancelled() && (limit < LIMIT); return resultMatcher.isCancelled() && (limit < LIMIT);
} }
}); }, rawDataCollector);
while (offlineIterator.hasNext()) { while (offlineIterator.hasNext()) {
BinaryMapIndexReader r = offlineIterator.next(); BinaryMapIndexReader r = offlineIterator.next();
@ -641,6 +671,11 @@ public class SearchCoreFactory {
} }
} }
protected static class PoiTypeResult {
public AbstractPoiType pt;
public Set<String> foundWords = new LinkedHashSet<String>();
}
public static class SearchAmenityTypesAPI extends SearchBaseAPI { public static class SearchAmenityTypesAPI extends SearchBaseAPI {
public final static String STD_POI_FILTER_PREFIX = "std_"; public final static String STD_POI_FILTER_PREFIX = "std_";
@ -675,68 +710,102 @@ public class SearchCoreFactory {
} }
} }
public Map<AbstractPoiType, List<String>> getPoiTypeResults(NameStringMatcher nm, boolean includeAdditionals) { public Map<String, PoiTypeResult> getPoiTypeResults(NameStringMatcher nm, NameStringMatcher nmAdditional) {
Map<AbstractPoiType, List<String>> results = new LinkedHashMap<>(); Map<String, PoiTypeResult> results = new LinkedHashMap<>();
for (AbstractPoiType pf : topVisibleFilters) { for (AbstractPoiType pf : topVisibleFilters) {
checkPoiType(nm, pf, results); PoiTypeResult res = checkPoiType(nm, pf);
if(res != null) {
results.put(res.pt.getKeyName(), res);
}
}
if (nmAdditional != null) {
addAditonals(nmAdditional, results, types.getOtherMapCategory());
} }
for (PoiCategory c : categories) { for (PoiCategory c : categories) {
checkPoiType(nm, c, results); PoiTypeResult res = checkPoiType(nm, c);
if(res != null) {
results.put(res.pt.getKeyName(), res);
}
if (nmAdditional != null) {
addAditonals(nmAdditional, results, c);
}
} }
Iterator<Entry<String, PoiType>> it = translatedNames.entrySet().iterator(); Iterator<Entry<String, PoiType>> it = translatedNames.entrySet().iterator();
while (it.hasNext()) { while (it.hasNext()) {
Entry<String, PoiType> e = it.next(); Entry<String, PoiType> e = it.next();
PoiType pt = e.getValue(); PoiType pt = e.getValue();
if (pt.getCategory() != types.getOtherMapCategory()) { if (pt.getCategory() != types.getOtherMapCategory() && !pt.isReference()) {
checkPoiType(nm, pt, results); PoiTypeResult res = checkPoiType(nm, pt);
List<PoiType> additionals = pt.getPoiAdditionals(); if(res != null) {
if (additionals != null && includeAdditionals) { results.put(res.pt.getKeyName(), res);
for (PoiType a : additionals) {
if (!results.containsKey(a)) {
String enTranslation = a.getEnTranslation().toLowerCase();
if (!"yes".equals(enTranslation) && !"no".equals(enTranslation)) {
checkPoiType(nm, a, results);
}
}
} }
if (nmAdditional != null) {
addAditonals(nmAdditional, results, pt);
} }
} }
} }
return results; return results;
} }
private void checkPoiType(NameStringMatcher nm, AbstractPoiType pf, Map<AbstractPoiType, List<String>> results) { private void addAditonals(NameStringMatcher nm, Map<String, PoiTypeResult> results, AbstractPoiType pt) {
List<String> lst = results.get(pf); List<PoiType> additionals = pt.getPoiAdditionals();
boolean nl = lst == null; if (additionals != null) {
for (PoiType a : additionals) {
PoiTypeResult existingResult = results.get(a.getKeyName());
if (existingResult != null) {
PoiAdditionalCustomFilter f ;
if (existingResult.pt instanceof PoiAdditionalCustomFilter) {
f = (PoiAdditionalCustomFilter) existingResult.pt;
} else {
f = new PoiAdditionalCustomFilter(types, (PoiType) existingResult.pt);
}
f.additionalPoiTypes.add(a);
existingResult.pt = f;
} else {
String enTranslation = a.getEnTranslation().toLowerCase();
if (!"no".equals(enTranslation) // && !"yes".equals(enTranslation)
) {
PoiTypeResult ptr = checkPoiType(nm, a);
if (ptr != null) {
results.put(a.getKeyName(), ptr);
}
}
}
}
}
}
private PoiTypeResult checkPoiType(NameStringMatcher nm, AbstractPoiType pf) {
PoiTypeResult res = null;
if (nm.matches(pf.getTranslation())) { if (nm.matches(pf.getTranslation())) {
lst = addToList(pf.getTranslation(), lst); res = addIfMatch(nm, pf.getTranslation(), pf, res);
} }
if (nm.matches(pf.getEnTranslation())) { if (nm.matches(pf.getEnTranslation())) {
lst = addToList(pf.getEnTranslation(), lst); res = addIfMatch(nm, pf.getEnTranslation(), pf, res);
} }
if (nm.matches(pf.getKeyName())) { if (nm.matches(pf.getKeyName())) {
lst = addToList(pf.getKeyName().replace('_', ' '), lst); res = addIfMatch(nm, pf.getKeyName().replace('_', ' '), pf, res);
} }
if (nm.matches(pf.getSynonyms())) { if (nm.matches(pf.getSynonyms())) {
String[] synonyms = pf.getSynonyms().split(";"); String[] synonyms = pf.getSynonyms().split(";");
for (String synonym : synonyms) { for (String synonym : synonyms) {
if (nm.matches(synonym)) { res = addIfMatch(nm, synonym, pf, res);
lst = addToList(synonym, lst);
} }
} }
} return res;
if(lst != null && nl) {
results.put(pf, lst);
}
} }
private List<String> addToList(String s, List<String> lst) { private PoiTypeResult addIfMatch(NameStringMatcher nm, String s, AbstractPoiType pf, PoiTypeResult res) {
if(lst == null) { if (nm.matches(s)) {
lst = new ArrayList<>(); if (res == null) {
res = new PoiTypeResult();
res.pt = pf;
} }
lst.add(s); res.foundWords.add(s);
return lst;
}
return res;
} }
private void initPoiTypes() { private void initPoiTypes() {
@ -752,6 +821,7 @@ public class SearchCoreFactory {
public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) throws IOException { public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) throws IOException {
boolean showTopFiltersOnly = !phrase.isUnknownSearchWordPresent(); boolean showTopFiltersOnly = !phrase.isUnknownSearchWordPresent();
NameStringMatcher nm = phrase.getFirstUnknownNameStringMatcher(); NameStringMatcher nm = phrase.getFirstUnknownNameStringMatcher();
initPoiTypes(); initPoiTypes();
if (showTopFiltersOnly) { if (showTopFiltersOnly) {
for (AbstractPoiType pt : topVisibleFilters) { for (AbstractPoiType pt : topVisibleFilters) {
@ -763,11 +833,13 @@ public class SearchCoreFactory {
} else { } else {
boolean includeAdditional = !phrase.hasMoreThanOneUnknownSearchWord(); boolean includeAdditional = !phrase.hasMoreThanOneUnknownSearchWord();
Map<AbstractPoiType, List<String>> poiTypes = getPoiTypeResults(nm, includeAdditional); NameStringMatcher nmAdditional = includeAdditional ?
for (Entry<AbstractPoiType, List<String>> pt : poiTypes.entrySet()) { new NameStringMatcher(phrase.getFirstUnknownSearchWord(), StringMatcherMode.CHECK_EQUALS_FROM_SPACE) : null;
Map<String, PoiTypeResult> poiTypes = getPoiTypeResults(nm, nmAdditional);
for (PoiTypeResult ptr : poiTypes.values()) {
boolean match = !phrase.isFirstUnknownSearchWordComplete(); boolean match = !phrase.isFirstUnknownSearchWordComplete();
if (!match) { if (!match) {
for (String foundName : pt.getValue()) { for (String foundName : ptr.foundWords) {
CollatorStringMatcher csm = new CollatorStringMatcher(foundName, StringMatcherMode.CHECK_ONLY_STARTS_WITH); CollatorStringMatcher csm = new CollatorStringMatcher(foundName, StringMatcherMode.CHECK_ONLY_STARTS_WITH);
match = csm.matches(phrase.getUnknownSearchPhrase()); match = csm.matches(phrase.getUnknownSearchPhrase());
if (match) { if (match) {
@ -777,9 +849,9 @@ public class SearchCoreFactory {
} }
if (match) { if (match) {
SearchResult res = new SearchResult(phrase); SearchResult res = new SearchResult(phrase);
res.localeName = pt.getKey().getTranslation(); res.localeName = ptr.pt.getTranslation();
res.object = pt.getKey(); res.object = ptr.pt;
addPoiTypeResult(phrase, resultMatcher, showTopFiltersOnly, getStandardFilterId(pt.getKey()), addPoiTypeResult(phrase, resultMatcher, showTopFiltersOnly, getStandardFilterId(ptr.pt),
res); res);
} }
} }
@ -797,7 +869,7 @@ public class SearchCoreFactory {
} }
private void addPoiTypeResult(SearchPhrase phrase, SearchResultMatcher resultMatcher, boolean showTopFiltersOnly, private void addPoiTypeResult(SearchPhrase phrase, SearchResultMatcher resultMatcher, boolean showTopFiltersOnly,
String stdFilterId , SearchResult res) { String stdFilterId, SearchResult res) {
res.priorityDistance = 0; res.priorityDistance = 0;
res.objectType = ObjectType.POI_TYPE; res.objectType = ObjectType.POI_TYPE;
res.firstUnknownWordMatches = true; res.firstUnknownWordMatches = true;
@ -890,7 +962,7 @@ public class SearchCoreFactory {
SearchPoiTypeFilter poiTypeFilter = null; SearchPoiTypeFilter poiTypeFilter = null;
String nameFilter = null; String nameFilter = null;
int countExtraWords = 0; int countExtraWords = 0;
Map<String, PoiType> poiAdditionals = new LinkedHashMap<String, PoiType>(); Set<String> poiAdditionals = new LinkedHashSet<>();
if (phrase.isLastWord(ObjectType.POI_TYPE)) { if (phrase.isLastWord(ObjectType.POI_TYPE)) {
Object obj = phrase.getLastSelectedWord().getResult().object; Object obj = phrase.getLastSelectedWord().getResult().object;
if (obj instanceof AbstractPoiType) { if (obj instanceof AbstractPoiType) {
@ -903,11 +975,13 @@ public class SearchCoreFactory {
nameFilter = phrase.getUnknownSearchPhrase(); nameFilter = phrase.getUnknownSearchPhrase();
} else if (searchAmenityTypesAPI != null && phrase.isFirstUnknownSearchWordComplete()) { } else if (searchAmenityTypesAPI != null && phrase.isFirstUnknownSearchWordComplete()) {
NameStringMatcher nm = phrase.getFirstUnknownNameStringMatcher(); NameStringMatcher nm = phrase.getFirstUnknownNameStringMatcher();
NameStringMatcher nmAdditional = new NameStringMatcher(phrase.getFirstUnknownSearchWord(),
StringMatcherMode.CHECK_EQUALS_FROM_SPACE) ;
searchAmenityTypesAPI.initPoiTypes(); searchAmenityTypesAPI.initPoiTypes();
Map<AbstractPoiType, List<String>> poiTypeResults = searchAmenityTypesAPI.getPoiTypeResults(nm, true); Map<String, PoiTypeResult> poiTypeResults = searchAmenityTypesAPI.getPoiTypeResults(nm, nmAdditional);
// find first full match only // find first full match only
for (Entry<AbstractPoiType, List<String>> poiType : poiTypeResults.entrySet()) { for (PoiTypeResult poiTypeResult : poiTypeResults.values()) {
for (String foundName : poiType.getValue()) { for (String foundName : poiTypeResult.foundWords) {
CollatorStringMatcher csm = new CollatorStringMatcher(foundName, StringMatcherMode.CHECK_ONLY_STARTS_WITH); CollatorStringMatcher csm = new CollatorStringMatcher(foundName, StringMatcherMode.CHECK_ONLY_STARTS_WITH);
// matches only completely // matches only completely
int mwords = phrase.countWords(foundName) ; int mwords = phrase.countWords(foundName) ;
@ -924,8 +998,8 @@ public class SearchCoreFactory {
nameFilter += otherSearchWords.get(k); nameFilter += otherSearchWords.get(k);
} }
} }
poiTypeFilter = getPoiTypeFilter(poiType.getKey(), poiAdditionals); poiTypeFilter = getPoiTypeFilter(poiTypeResult.pt, poiAdditionals);
unselectedPoiType = poiType.getKey(); unselectedPoiType = poiTypeResult.pt;
} }
} }
} }
@ -954,7 +1028,7 @@ public class SearchCoreFactory {
private ResultMatcher<Amenity> getResultMatcher(final SearchPhrase phrase, final SearchPoiTypeFilter poiTypeFilter, private ResultMatcher<Amenity> getResultMatcher(final SearchPhrase phrase, final SearchPoiTypeFilter poiTypeFilter,
final SearchResultMatcher resultMatcher, final String nameFilter, final SearchResultMatcher resultMatcher, final String nameFilter,
final BinaryMapIndexReader selected, final Set<String> searchedPois, final BinaryMapIndexReader selected, final Set<String> searchedPois,
final Map<String, PoiType> poiAdditionals, final int countExtraWords) { final Collection<String> poiAdditionals, final int countExtraWords) {
final NameStringMatcher ns = nameFilter == null ? null : new NameStringMatcher(nameFilter, StringMatcherMode.CHECK_STARTS_FROM_SPACE); final NameStringMatcher ns = nameFilter == null ? null : new NameStringMatcher(nameFilter, StringMatcherMode.CHECK_STARTS_FROM_SPACE);
@ -975,7 +1049,7 @@ public class SearchCoreFactory {
} }
if (!poiAdditionals.isEmpty()) { if (!poiAdditionals.isEmpty()) {
boolean found = false; boolean found = false;
for (String add : poiAdditionals.keySet()) { for (String add : poiAdditionals) {
if(object.getAdditionalInfo().containsKey(add)) { if(object.getAdditionalInfo().containsKey(add)) {
found = true; found = true;
break; break;
@ -1007,6 +1081,8 @@ public class SearchCoreFactory {
res.localeName += " " + ref; res.localeName += " " + ref;
} }
} }
} else {
phrase.countUnknownWordsMatch(res, "", null, countExtraWords);
} }
res.object = object; res.object = object;
@ -1027,13 +1103,13 @@ public class SearchCoreFactory {
}; };
} }
private SearchPoiTypeFilter getPoiTypeFilter(AbstractPoiType pt, Map<String, PoiType> poiAdditionals ) { private SearchPoiTypeFilter getPoiTypeFilter(AbstractPoiType pt, Set<String> poiAdditionals ) {
final Map<PoiCategory, LinkedHashSet<String>> acceptedTypes = new LinkedHashMap<PoiCategory, final Map<PoiCategory, LinkedHashSet<String>> acceptedTypes = new LinkedHashMap<PoiCategory,
LinkedHashSet<String>>(); LinkedHashSet<String>>();
pt.putTypes(acceptedTypes); pt.putTypes(acceptedTypes);
poiAdditionals.clear(); poiAdditionals.clear();
if (pt instanceof PoiType && ((PoiType) pt).isAdditional() && ((PoiType) pt).getParentType() != null) { if (pt.isAdditional()) {
poiAdditionals.put(pt.getKeyName(), (PoiType) pt); poiAdditionals.add(pt.getKeyName());
} }
return new SearchPoiTypeFilter() { return new SearchPoiTypeFilter() {
@ -1294,6 +1370,36 @@ public class SearchCoreFactory {
} }
} }
protected static class PoiAdditionalCustomFilter extends AbstractPoiType {
protected List<PoiType> additionalPoiTypes = new ArrayList<PoiType>();
public PoiAdditionalCustomFilter(MapPoiTypes registry, PoiType pt) {
super(pt.getKeyName(), registry);
additionalPoiTypes.add(pt);
}
@Override
public boolean isAdditional() {
return true;
}
public Map<PoiCategory, LinkedHashSet<String>> putTypes(Map<PoiCategory, LinkedHashSet<String>> acceptedTypes) {
for (PoiType p : additionalPoiTypes) {
if (p.getParentType() == registry.getOtherMapCategory()) {
for (PoiCategory c : registry.getCategories(false)) {
c.putTypes(acceptedTypes);
}
} else {
p.getParentType().putTypes(acceptedTypes);
}
}
return acceptedTypes;
}
}
public static class SearchLocationAndUrlAPI extends SearchBaseAPI { public static class SearchLocationAndUrlAPI extends SearchBaseAPI {
private static final int OLC_RECALC_DISTANCE_THRESHOLD = 100000; // 100 km private static final int OLC_RECALC_DISTANCE_THRESHOLD = 100000; // 100 km

View file

@ -526,7 +526,7 @@ public class SearchPhrase {
public NameStringMatcher getFirstUnknownNameStringMatcher() { public NameStringMatcher getFirstUnknownNameStringMatcher() {
if (firstUnknownNameStringMatcher == null) { if (firstUnknownNameStringMatcher == null) {
firstUnknownNameStringMatcher = getNameStringMatcher(firstUnknownSearchWord, lastUnknownSearchWordComplete); firstUnknownNameStringMatcher = getNameStringMatcher(firstUnknownSearchWord, isFirstUnknownSearchWordComplete());
} }
return firstUnknownNameStringMatcher; return firstUnknownNameStringMatcher;
} }
@ -793,6 +793,13 @@ public class SearchPhrase {
return r; return r;
} }
public String getLastUnknownSearchWord() {
if(otherUnknownWords.size() > 0) {
return otherUnknownWords.get(otherUnknownWords.size() - 1);
}
return firstUnknownSearchWord;
}
public int getRadiusSearch(int meters, int radiusLevel) { public int getRadiusSearch(int meters, int radiusLevel) {
int res = meters; int res = meters;
@ -877,4 +884,6 @@ public class SearchPhrase {
} }

View file

@ -23,8 +23,7 @@ public class SearchSettings {
private ObjectType[] searchTypes; private ObjectType[] searchTypes;
private boolean emptyQueryAllowed; private boolean emptyQueryAllowed;
private boolean sortByName; private boolean sortByName;
private SearchExportSettings exportSettings; private SearchExportSettings exportSettings; // = new SearchExportSettings(true, true, -1);
//private SearchExportSettings exportSettings = new SearchExportSettings(false, false, -1);
public SearchSettings(SearchSettings s) { public SearchSettings(SearchSettings s) {
if(s != null) { if(s != null) {

View file

@ -1,13 +1,16 @@
package net.osmand.search; package net.osmand.search;
import java.io.File; import net.osmand.ResultMatcher;
import java.io.IOException; import net.osmand.binary.BinaryMapIndexReader;
import java.util.ArrayList; import net.osmand.osm.AbstractPoiType;
import java.util.Collections; import net.osmand.osm.MapPoiTypes;
import java.util.HashMap; import net.osmand.search.SearchUICore.SearchResultCollection;
import java.util.List; import net.osmand.search.SearchUICore.SearchResultMatcher;
import java.util.Map; import net.osmand.search.core.SearchPhrase;
import java.util.concurrent.atomic.AtomicInteger; import net.osmand.search.core.SearchResult;
import net.osmand.search.core.SearchSettings;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
@ -19,23 +22,18 @@ import org.junit.runner.RunWith;
import org.junit.runners.Parameterized; import org.junit.runners.Parameterized;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import net.osmand.OsmAndCollator; import java.io.File;
import net.osmand.ResultMatcher; import java.io.FileInputStream;
import net.osmand.binary.BinaryMapIndexReader; import java.io.FileOutputStream;
import net.osmand.data.Amenity; import java.io.IOException;
import net.osmand.data.Building; import java.io.RandomAccessFile;
import net.osmand.data.City; import java.util.ArrayList;
import net.osmand.data.MapObject; import java.util.Collections;
import net.osmand.data.Street; import java.util.HashMap;
import net.osmand.osm.AbstractPoiType; import java.util.List;
import net.osmand.osm.MapPoiTypes; import java.util.Map;
import net.osmand.search.SearchUICore.SearchResultCollection; import java.util.concurrent.atomic.AtomicInteger;
import net.osmand.search.SearchUICore.SearchResultMatcher; import java.util.zip.GZIPInputStream;
import net.osmand.search.core.SearchPhrase;
import net.osmand.search.core.SearchResult;
import net.osmand.search.core.SearchSettings;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
@RunWith(Parameterized.class) @RunWith(Parameterized.class)
public class SearchUICoreTest { public class SearchUICoreTest {
@ -94,11 +92,12 @@ public class SearchUICoreTest {
@Test @Test
public void testSearch() throws IOException, JSONException { public void testSearch() throws IOException, JSONException {
File jsonFile = testFile; File jsonFile = testFile;
File obfFile = new File(testFile.getParentFile(), testFile.getName().replace(".json", ".obf"));
File obfZipFile = new File(testFile.getParentFile(), testFile.getName().replace(".json", ".obf.gz"));
String sourceJsonText = Algorithms.getFileAsString(jsonFile); String sourceJsonText = Algorithms.getFileAsString(jsonFile);
Assert.assertNotNull(sourceJsonText); Assert.assertNotNull(sourceJsonText);
Assert.assertTrue(sourceJsonText.length() > 0); Assert.assertTrue(sourceJsonText.length() > 0);
BinaryMapIndexReaderTest reader = new BinaryMapIndexReaderTest();
JSONObject sourceJson = new JSONObject(sourceJsonText); JSONObject sourceJson = new JSONObject(sourceJsonText);
JSONArray phrasesJson = sourceJson.optJSONArray("phrases"); JSONArray phrasesJson = sourceJson.optJSONArray("phrases");
String singlePhrase = sourceJson.optString("phrase", null); String singlePhrase = sourceJson.optString("phrase", null);
@ -115,39 +114,23 @@ public class SearchUICoreTest {
} }
} }
JSONObject settingsJson = sourceJson.getJSONObject("settings"); JSONObject settingsJson = sourceJson.getJSONObject("settings");
if (sourceJson.has("amenities")) { BinaryMapIndexReader reader = null;
JSONArray amenitiesArr = sourceJson.getJSONArray("amenities"); boolean useData = settingsJson.optBoolean("useData", true);
List<Amenity> amenities = new ArrayList<>(); if (useData) {
for (int i = 0; i < amenitiesArr.length(); i++) { boolean obfZipFileExists = obfZipFile.exists();
JSONObject amenityObj = amenitiesArr.getJSONObject(i); if (!obfZipFileExists) {
amenities.add(Amenity.parseJSON(amenityObj)); System.out.println(String.format("Could not find obf file: %s", obfZipFile.getPath()));
return;
} }
reader.amenities = amenities; //Assert.assertTrue(obfZipFileExists);
}
if (sourceJson.has("cities")) { GZIPInputStream gzin = new GZIPInputStream(new FileInputStream(obfZipFile));
JSONArray citiesArr = sourceJson.getJSONArray("cities"); FileOutputStream fous = new FileOutputStream(obfFile);
List<City> cities = new ArrayList<>(); Algorithms.streamCopy(gzin, fous);
List<City> initCities = new ArrayList<>(); fous.close();
List<City> matchedCities = new ArrayList<>(); gzin.close();
List<City> streetCities = new ArrayList<>();
for (int i = 0; i < citiesArr.length(); i++) { reader = new BinaryMapIndexReader(new RandomAccessFile(obfFile.getPath(), "r"), obfFile);
JSONObject cityObj = citiesArr.getJSONObject(i);
final City city = City.parseJSON(cityObj);
cities.add(city);
if (cityObj.has("init")) {
initCities.add(city);
}
if (cityObj.has("matchCity")) {
matchedCities.add(city);
}
if (cityObj.has("matchStreet")) {
streetCities.add(city);
}
}
reader.cities = cities;
reader.initCities = initCities;
reader.matchedCities = matchedCities;
reader.streetCities = streetCities;
} }
List<List<String>> results = new ArrayList<>(); List<List<String>> results = new ArrayList<>();
for (int i = 0; i < phrases.size(); i++) { for (int i = 0; i < phrases.size(); i++) {
@ -166,7 +149,9 @@ public class SearchUICoreTest {
} }
SearchSettings s = SearchSettings.parseJSON(settingsJson); SearchSettings s = SearchSettings.parseJSON(settingsJson);
if (reader != null) {
s.setOfflineIndexes(Collections.singletonList(reader)); s.setOfflineIndexes(Collections.singletonList(reader));
}
final SearchUICore core = new SearchUICore(MapPoiTypes.getDefault(), "en", false); final SearchUICore core = new SearchUICore(MapPoiTypes.getDefault(), "en", false);
core.init(); core.init();
@ -216,6 +201,8 @@ public class SearchUICoreTest {
} }
} }
} }
obfFile.delete();
} }
private void parseResults(JSONObject sourceJson, String tag, List<List<String>> results) { private void parseResults(JSONObject sourceJson, String tag, List<List<String>> results) {
@ -329,116 +316,4 @@ public class SearchUICoreTest {
return val; return val;
} }
}; };
private static class BinaryMapIndexReaderTest extends BinaryMapIndexReader {
List<Amenity> amenities = Collections.emptyList();
List<City> cities = Collections.emptyList();
List<City> initCities = Collections.emptyList();
List<City> matchedCities = Collections.emptyList();
List<City> streetCities = Collections.emptyList();
BinaryMapIndexReaderTest() throws IOException {
super(null, null, false);
}
@Override
public List<Amenity> searchPoiByName(SearchRequest<Amenity> req) throws IOException {
for (Amenity amenity : amenities) {
req.publish(amenity);
}
return req.getSearchResults();
}
@Override
public List<Amenity> searchPoi(SearchRequest<Amenity> req) throws IOException {
for (Amenity amenity : amenities) {
req.publish(amenity);
}
return req.getSearchResults();
}
@Override
public List<City> getCities(SearchRequest<City> resultMatcher, int cityType) throws IOException {
for (City city : initCities) {
if (resultMatcher != null) {
resultMatcher.publish(city);
}
}
return initCities;
}
@Override
public int preloadStreets(City c, SearchRequest<Street> resultMatcher) throws IOException {
return 0;
}
@Override
public void preloadBuildings(Street s, SearchRequest<Building> resultMatcher) throws IOException {
// cities must be filled with streets and buildings
}
@Override
public List<MapObject> searchAddressDataByName(SearchRequest<MapObject> req) throws IOException {
for (City city : streetCities) {
for (Street street : city.getStreets()) {
req.publish(street);
}
}
for (City city : matchedCities) {
req.publish(city);
}
return req.getSearchResults();
}
@Override
public String getRegionName() {
return "Test region";
}
@Override
public boolean containsPoiData(int left31x, int top31y, int right31x, int bottom31y) {
return true;
}
@Override
public boolean containsMapData() {
return true;
}
@Override
public boolean containsPoiData() {
return true;
}
@Override
public boolean containsRouteData() {
return true;
}
@Override
public boolean containsRouteData(int left31x, int top31y, int right31x, int bottom31y, int zoom) {
return true;
}
@Override
public boolean containsAddressData(int left31x, int top31y, int right31x, int bottom31y) {
return true;
}
@Override
public boolean containsMapData(int tile31x, int tile31y, int zoom) {
return true;
}
@Override
public boolean containsMapData(int left31x, int top31y, int right31x, int bottom31y, int zoom) {
return true;
}
@Override
public boolean containsAddressData() {
return true;
}
}
} }

View file

@ -1,5 +1,6 @@
/*.json /*.json
/osm_live/*.json /osm_live/*.json
/search/*
*.obf *.obf
*.osm *.osm
phrases.xml phrases.xml

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,736 +0,0 @@
{
"settings": {
"lat": "55.28666",
"lon": "52.00556",
"radiusLevel": 1,
"totalLimit": -1,
"lang": "",
"transliterateIfMissing": false,
"emptyQueryAllowed": false,
"sortByName": false
},
"phrase": "parking ",
"results": [
"Parking (Personal transport) [[1, POI_TYPE, 1.000, 0.00 km]]",
"для посетителей [[0, POI, 2.000, 0.03 km]]",
"Parking [[0, POI, 2.000, 0.05 km]]",
"Parking [[0, POI, 2.000, 0.11 km]]",
"Parking [[0, POI, 2.000, 0.16 km]]",
"Parking [[0, POI, 2.000, 0.17 km]]",
"Parking [[0, POI, 2.000, 0.23 km]]",
"Parking [[0, POI, 2.000, 0.35 km]]",
"Parking [[0, POI, 2.000, 0.40 km]]",
"Parking [[0, POI, 2.000, 0.48 km]]",
"Parking [[0, POI, 2.000, 0.49 km]]",
"Parking [[0, POI, 2.000, 0.55 km]]",
"Автостоянка №1 [[0, POI, 2.000, 0.56 km]]",
"Parking [[0, POI, 2.000, 0.58 km]]",
"Parking [[0, POI, 2.000, 0.61 km]]",
"Parking [[0, POI, 2.000, 0.72 km]]",
"Parking [[0, POI, 2.000, 0.81 km]]",
"Parking [[0, POI, 2.000, 0.81 km]]",
"Parking [[0, POI, 2.000, 0.85 km]]",
"для клиентов [[0, POI, 2.000, 0.89 km]]",
"Parking [[0, POI, 2.000, 0.93 km]]",
"Parking [[0, POI, 2.000, 0.94 km]]",
"Заинская ГРЭС [[0, POI, 2.000, 1.01 km]]",
"Parking [[0, POI, 2.000, 1.02 km]]",
"Южная [[0, POI, 2.000, 1.03 km]]",
"Parking [[0, POI, 2.000, 1.06 km]]",
"Parking [[0, POI, 2.000, 1.09 km]]",
"Parking [[0, POI, 2.000, 1.10 km]]",
"Служебная [[0, POI, 2.000, 1.14 km]]",
"Parking [[0, POI, 2.000, 1.20 km]]",
"Parking [[0, POI, 2.000, 1.21 km]]",
"Parking [[0, POI, 2.000, 1.36 km]]",
"Parking [[0, POI, 2.000, 1.44 km]]",
"Три тополя [[0, POI, 2.000, 1.47 km]]",
"Parking [[0, POI, 2.000, 1.53 km]]",
"Parking [[0, POI, 2.000, 1.56 km]]",
"Parking [[0, POI, 2.000, 1.62 km]]",
"Parking [[0, POI, 2.000, 1.85 km]]",
"Штрафстоянка (Эвакуатор) [[0, POI, 2.000, 2.03 km]]",
"Parking [[0, POI, 2.000, 2.05 km]]",
"для регистрации в ГИБДД [[0, POI, 2.000, 2.06 km]]",
"Parking [[0, POI, 2.000, 2.06 km]]",
"Parking [[0, POI, 2.000, 2.07 km]]",
"для техосмотра [[0, POI, 2.000, 2.09 km]]",
"Parking [[0, POI, 2.000, 2.18 km]]",
"Parking [[0, POI, 2.000, 2.55 km]]",
"Parking [[0, POI, 2.000, 3.40 km]]",
"Parking [[0, POI, 2.000, 3.41 km]]",
"Parking [[0, POI, 2.000, 3.41 km]]",
"Parking [[0, POI, 2.000, 3.45 km]]",
"Parking [[0, POI, 2.000, 4.29 km]]",
"Parking [[0, POI, 2.000, 6.71 km]]"
],
"amenities": [
{
"lat": "55.29801",
"lon": "51.99100",
"id": 881010149,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.29481",
"lon": "51.98964",
"id": 803545283,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"parking_surface": "surface"
}
},
{
"name": "Заинская ГРЭС",
"lat": "55.28492",
"lon": "52.02125",
"id": 481494555,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_yes": "yes",
"parking_surface": "surface"
}
},
{
"lat": "55.29105",
"lon": "51.99531",
"id": 540237721,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.28683",
"lon": "52.01479",
"id": 888343263,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_unpaved": "unpaved",
"access_permissive": "permissive",
"supervised_yes": "yes"
}
},
{
"lat": "55.30317",
"lon": "52.00192",
"id": 1161722765,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"parking_surface": "surface",
"operator": "cахарный завод"
}
},
{
"name": "для техосмотра",
"lat": "55.30439",
"lon": "52.01634",
"id": 484385195,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.28615",
"lon": "52.00702",
"id": 1040771455,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.28582",
"lon": "52.00758",
"id": 535925577,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.30950",
"lon": "51.96954",
"id": 1228158833,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"parking_surface": "surface"
}
},
{
"lat": "55.29345",
"lon": "51.99385",
"id": 535897911,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.30957",
"lon": "52.00374",
"id": 897019441,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.28602",
"lon": "52.00908",
"id": 268235957,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.28777",
"lon": "51.99608",
"id": 4410403520512,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"surface_unpaved": "unpaved",
"supervised_yes": "yes",
"parking_surface": "surface",
"fee_yes": "yes",
"capacity": "70"
}
},
{
"lat": "55.34092",
"lon": "52.05172",
"id": 2136388576,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no"
}
},
{
"lat": "55.28271",
"lon": "52.00248",
"id": 536030271,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.28391",
"lon": "52.00273",
"id": 536035007,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"name": "Служебная",
"lat": "55.28615",
"lon": "52.02350",
"id": 481491939,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"surface_asphalt": "asphalt",
"fee_no": "no",
"access_permissive": "permissive",
"supervised_yes": "yes",
"parking_surface": "surface"
}
},
{
"lat": "55.30969",
"lon": "51.96917",
"id": 1228158821,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.31022",
"lon": "51.97104",
"id": 1228158819,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"parking_surface": "surface"
}
},
{
"lat": "55.29275",
"lon": "51.99355",
"id": 892959215,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"name": "для посетителей",
"lat": "55.28688",
"lon": "52.00578",
"id": 11390213698,
"subType": "parking",
"type": "transportation"
},
{
"lat": "55.30838",
"lon": "51.96767",
"id": 1164434353,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"parking_surface": "surface"
}
},
{
"name": "Южная",
"lat": "55.27739",
"lon": "52.00544",
"id": 641567205,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"surface_unpaved": "unpaved",
"supervised_yes": "yes",
"parking_surface": "surface",
"fee_yes": "yes"
}
},
{
"lat": "55.28045",
"lon": "51.99550",
"id": 478137507,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"supervised_yes": "yes",
"parking_surface": "surface",
"fee_yes": "yes"
}
},
{
"name": "Автостоянка №1",
"lat": "55.29028",
"lon": "51.99934",
"id": 502948005,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"surface_gravel": "gravel",
"supervised_yes": "yes",
"parking_surface": "surface",
"fee_yes": "yes"
}
},
{
"lat": "55.27839",
"lon": "52.00301",
"id": 1168315587,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"parking_surface": "surface",
"operator": "Заинская ЦРБ"
}
},
{
"lat": "55.30468",
"lon": "52.01335",
"id": 897014805,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.32509",
"lon": "51.99930",
"id": 1172111487,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"name": "Штрафстоянка (Эвакуатор)",
"lat": "55.30439",
"lon": "52.01346",
"id": 5153530824,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"supervised_yes": "yes",
"parking_surface": "surface",
"fee_yes": "yes",
"phone": "+7 (85558) 7-77-01"
}
},
{
"name": "Три тополя",
"lat": "55.29666",
"lon": "51.99048",
"id": 642175341,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"surface_unpaved": "unpaved",
"supervised_yes": "yes",
"parking_surface": "surface",
"fee_yes": "yes"
}
},
{
"lat": "55.29077",
"lon": "52.00812",
"id": 1168356553,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"surface_asphalt": "asphalt",
"supervised_yes": "yes",
"parking_surface": "surface",
"fee_yes": "yes"
}
},
{
"lat": "55.29429",
"lon": "51.99190",
"id": 893048919,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.29959",
"lon": "52.00516",
"id": 1022216961,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.28355",
"lon": "51.99881",
"id": 943827057,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"parking_surface": "surface"
}
},
{
"lat": "55.28651",
"lon": "52.00479",
"id": 905757839,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.28928",
"lon": "52.00977",
"id": 190467181,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_gravel": "gravel",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.28674",
"lon": "52.00282",
"id": 1168411743,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"name": "для клиентов",
"lat": "55.29116",
"lon": "51.99394",
"id": 821840897,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"surface_asphalt": "asphalt",
"parking_surface": "surface",
"access_customers": "customers"
}
},
{
"lat": "55.29068",
"lon": "51.99490",
"id": 540238031,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.28982",
"lon": "51.98911",
"id": 190489189,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.29341",
"lon": "51.99072",
"id": 806562897,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no"
}
},
{
"lat": "55.28042",
"lon": "52.00155",
"id": 1159734951,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.30493",
"lon": "52.01007",
"id": 1172110357,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.30500",
"lon": "52.01108",
"id": 1172110359,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface",
"capacity": "13"
}
},
{
"lat": "55.29816",
"lon": "51.99222",
"id": 1160388645,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.29015",
"lon": "51.99366",
"id": 503278061,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.30624",
"lon": "52.00475",
"id": 691494051,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface",
"operator": "ЗЗМК - Тимер"
}
},
{
"name": "для регистрации в ГИБДД",
"lat": "55.30438",
"lon": "52.01483",
"id": 484385289,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
},
{
"lat": "55.29258",
"lon": "51.99188",
"id": 892959265,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no"
}
},
{
"lat": "55.28050",
"lon": "52.00200",
"id": 1159662915,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface",
"capacity": "10"
}
},
{
"lat": "55.29845",
"lon": "51.99057",
"id": 881010015,
"subType": "parking",
"type": "transportation",
"additionalInfo": {
"fee_no": "no",
"surface_asphalt": "asphalt",
"supervised_no": "no",
"parking_surface": "surface"
}
}
]
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,17 +0,0 @@
{
"settings": {
"lat": "55.75138",
"lon": "37.62940",
"radiusLevel": 1,
"totalLimit": -1,
"lang": "",
"transliterateIfMissing": false,
"emptyQueryAllowed": false,
"sortByName": false
},
"phrase": "QHW6+CQ",
"results": [
"55.796062, 37.56194 [[1, LOCATION, 1.000, 6.52 km]]"
],
"cities": []
}

View file

@ -1,710 +0,0 @@
{
"settings": {
"lat": "51.04933",
"lon": "13.73815",
"radiusLevel": 1,
"totalLimit": -1,
"lang": "",
"transliterateIfMissing": false,
"emptyQueryAllowed": false,
"sortByName": false
},
"phrase": "Biergarten ",
"results": [
"Biergarten (Food) [[1, POI_TYPE, 1.000, 0.00 km]]",
"Biergarten Italienisches Dörfchen [[1, POI, 2.000, 0.55 km]]",
"Biergarten Narrenhäus'l [[1, POI, 2.000, 0.84 km]]",
"Biergarten Elbsegler [[1, POI, 2.000, 0.89 km]]",
"Biergarten Elbsegler [[1, POI, 2.000, 0.90 km]]",
"Körnergarten [[1, POI, 2.000, 5.18 km]]",
"Biergarten Goldener Anker [[1, POI, 2.000, 9.78 km]]",
"Palais Bistro [[0, POI, 2.000, 0.29 km]]",
"Augustus Garten am Narrenhäusl [[0, POI, 2.000, 0.88 km]]",
"Zum Schießhaus [[0, POI, 2.000, 0.96 km]]",
"Biergarten [[0, POI, 2.000, 1.24 km]]",
"Wachstube [[0, POI, 2.000, 1.24 km]]",
"Torwirtschaft [[0, POI, 2.000, 1.27 km]]",
"Altes Wettbüro [[0, POI, 2.000, 1.77 km]]",
"Bottoms Up [[0, POI, 2.000, 2.16 km]]",
"Louisengarten [[0, POI, 2.000, 2.20 km]]",
"Biergarten [[0, POI, 2.000, 2.28 km]]",
"Fährgarten Johannstadt [[0, POI, 2.000, 2.42 km]]",
"Carolaschlösschen [[0, POI, 2.000, 2.53 km]]",
"Alt-Dresden [[0, POI, 2.000, 2.75 km]]",
"Biergarten [[0, POI, 2.000, 2.90 km]]",
"Café & Restaurant Saite [[0, POI, 2.000, 3.09 km]]",
"Brauhaus am Waldschlößchen [[0, POI, 2.000, 3.45 km]]",
"Brauhaus Watzke [[0, POI, 2.000, 3.51 km]]",
"el Horst [[0, POI, 2.000, 3.67 km]]",
"Biergarten [[0, POI, 2.000, 3.74 km]]",
"Spitzwegerich [[0, POI, 2.000, 3.81 km]]",
"Biergarten [[0, POI, 2.000, 3.88 km]]",
"Biergarten [[0, POI, 2.000, 4.46 km]]",
"Trobischhof [[0, POI, 2.000, 4.48 km]]",
"Biergarten [[0, POI, 2.000, 4.80 km]]",
"Biergarten [[0, POI, 2.000, 4.91 km]]",
"Schillergarten [[0, POI, 2.000, 4.97 km]]",
"Biergarten [[0, POI, 2.000, 5.11 km]]",
"Biergarten [[0, POI, 2.000, 5.13 km]]",
"Demnitz Elbegarten [[0, POI, 2.000, 5.16 km]]",
"Körnergarten [[0, POI, 2.000, 5.18 km]]",
"Biergarten [[0, POI, 2.000, 5.31 km]]",
"Trachauer Sommergarten [[0, POI, 2.000, 5.31 km]]",
"Biergarten [[0, POI, 2.000, 5.91 km]]",
"Biergarten [[0, POI, 2.000, 6.12 km]]",
"Biergarten [[0, POI, 2.000, 6.26 km]]",
"Biergarten [[0, POI, 2.000, 6.42 km]]",
"Biergarten [[0, POI, 2.000, 6.88 km]]",
"Biergarten [[0, POI, 2.000, 7.11 km]]",
"Straußenwirtschaft Weingut Pesterwitz [[0, POI, 2.000, 7.14 km]]",
"Klotzscher Sommerwirtschaft [[0, POI, 2.000, 7.16 km]]",
"Biergarten [[0, POI, 2.000, 7.17 km]]",
"Zacke [[0, POI, 2.000, 8.20 km]]",
"Weingut Seifert [[0, POI, 2.000, 8.50 km]]",
"Biergarten [[0, POI, 2.000, 8.82 km]]",
"Biergarten [[0, POI, 2.000, 9.11 km]]",
"Biergarten [[0, POI, 2.000, 9.23 km]]",
"Landgut Hofewiese [[0, POI, 2.000, 9.40 km]]",
"Biergarten Goldener Anker [[0, POI, 2.000, 9.78 km]]",
"Biergarten [[0, POI, 2.000, 10.00 km]]",
"Biergarten [[0, POI, 2.000, 10.22 km]]",
"Besenwirtschaft Steinrücken [[0, POI, 2.000, 10.85 km]]"
],
"amenities": [
{
"lat": "51.02489",
"lon": "13.69860",
"id": 244538865,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"surface_fine_gravel": "fine_gravel"
}
},
{
"lat": "51.09478",
"lon": "13.84153",
"id": 865332598,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"operator": "Einkehr"
}
},
{
"lat": "51.01123",
"lon": "13.67918",
"id": 9712288200,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"website": "http://www.zur-linde-freital.de/",
"operator": "Zur Linde",
"image": "http://commons.wikimedia.org/wiki/File:Hotel_und_Gasthaus_Zur_Linde_Freital-Birkigt.jpg"
}
},
{
"name": "Altes Wettbüro",
"lat": "51.06472",
"lon": "13.74467",
"id": 11451975032,
"subType": "biergarten",
"type": "sustenance",
"openingHours": "Tu-Sa 17:00-22:00",
"additionalInfo": {
"wheelchair_limited": "limited",
"outdoor_seating_yes": "yes",
"outdoor_seating_filter_yes": "yes",
"opening_hours": "Tu-Sa 17:00-22:00",
"website": "http://www.altes-wettbuero.de",
"phone": "+49 351 6588983",
"operator": "Falk Gruß"
}
},
{
"name": "Carolaschlösschen",
"lat": "51.03341",
"lon": "13.76392",
"id": 3704353318,
"subType": "biergarten",
"type": "sustenance",
"openingHours": "Mo-Su 11:00-24:00",
"additionalInfo": {
"wheelchair_limited": "limited",
"opening_hours": "Mo-Su 11:00-24:00",
"facebook": "https://www.facebook.com/carolaschloesschen/"
}
},
{
"lat": "51.01251",
"lon": "13.69407",
"id": 562115695,
"subType": "biergarten",
"type": "sustenance"
},
{
"name": "Torwirtschaft",
"lat": "51.04156",
"lon": "13.75139",
"id": 517007905,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"wheelchair_yes": "yes",
"website": "http://www.torwirtschaft-dresden.de",
"facebook": "https://www.facebook.com/Torwirtschaft-der-Biergarten-f%C3%BCr-alle-Dynamofans-168677023169963/"
}
},
{
"name": "Biergarten Italienisches Dörfchen",
"lat": "51.05429",
"lon": "13.73759",
"id": 486232011,
"subType": "biergarten",
"type": "sustenance"
},
{
"lat": "51.04261",
"lon": "13.75229",
"id": 98518191,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"wheelchair_yes": "yes"
}
},
{
"name": "Körnergarten",
"names": {
"prefix": "Biergarten"
},
"lat": "51.05316",
"lon": "13.81192",
"id": 3704353594,
"subType": "biergarten",
"type": "sustenance",
"openingHours": "Mo-Su 11:00-24:00",
"additionalInfo": {
"wheelchair_limited": "limited",
"opening_hours": "Mo-Su 11:00-24:00",
"website": "http://www.koernergarten.de/"
}
},
{
"lat": "51.06397",
"lon": "13.79748",
"id": 670742479,
"subType": "biergarten",
"type": "sustenance"
},
{
"name": "Weingut Seifert",
"lat": "51.11134",
"lon": "13.66701",
"id": 789740008,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"note": "not really a 'biergarten' :)"
}
},
{
"lat": "50.98710",
"lon": "13.65086",
"id": 9037536542,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"operator": "Zum Gründl"
}
},
{
"lat": "51.10538",
"lon": "13.62622",
"id": 9289210778,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"operator": "Zum Bürgergarten"
}
},
{
"lat": "51.09459",
"lon": "13.84170",
"id": 5598697742,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"operator": "Einkehr"
}
},
{
"name": "Trobischhof",
"lat": "51.08649",
"lon": "13.71338",
"id": 409847251,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"wheelchair_limited": "limited"
}
},
{
"name": "Demnitz Elbegarten",
"lat": "51.05328",
"lon": "13.81175",
"id": 771102016,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"wheelchair_no": "no",
"cuisine_german": "german",
"website": "http://www.elbegarten.de"
}
},
{
"name": "Augustus Garten am Narrenhäusl",
"lat": "51.05697",
"lon": "13.74166",
"id": 465538291,
"subType": "biergarten",
"type": "sustenance"
},
{
"lat": "51.01529",
"lon": "13.65107",
"id": 7482890514,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"operator": "Burgwartschänke"
}
},
{
"name": "el Horst",
"lat": "51.04361",
"lon": "13.78979",
"id": 559979958,
"subType": "biergarten",
"type": "sustenance",
"openingHours": "Oct-Apr: Mo-Fr 17:00-18:00+, Oct-Apr: Sa,PH 11:30-18:00+, May-Sep: Mo-Fr 15:00-18:00+, May-Sep: Sa,PH 11:30-18:00+",
"additionalInfo": {
"opening_hours": "Oct-Apr: Mo-Fr 17:00-18:00+, Oct-Apr: Sa,PH 11:30-18:00+, May-Sep: Mo-Fr 15:00-18:00+, May-Sep: Sa,PH 11:30-18:00+"
}
},
{
"lat": "51.07376",
"lon": "13.70135",
"id": 553613482,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"operator": "Wirtshaus Lindenschänke"
}
},
{
"name": "Wachstube",
"lat": "51.04254",
"lon": "13.75222",
"id": 46060263,
"subType": "biergarten",
"type": "sustenance",
"openingHours": "11:00+",
"additionalInfo": {
"wheelchair_no": "no",
"opening_hours": "11:00+",
"website": "https://www.torwirtschaft-dresden.de/wachstube/restaurant.php",
"phone": "+49 351 4466975",
"image": "https://commons.wikimedia.org/wiki/File:Torhaus_N_Grosser_Garten_Dresden-2.jpg",
"wheelchair_description:de": "Zugang ins Gebäude nur über Stufen",
"height": "5.7",
"facebook": "https://www.facebook.com/Wachstube-im-Gro%C3%9Fen-Garten-Dresden-144180698984577/",
"email": "info@wachstube-dresden.de"
}
},
{
"lat": "51.09050",
"lon": "13.70555",
"id": 6300849046,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"operator": "Bäckerei Werner"
}
},
{
"name": "Biergarten Elbsegler",
"lat": "51.05731",
"lon": "13.73973",
"id": 465318113,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"wheelchair_limited": "limited"
}
},
{
"name": "Straußenwirtschaft Weingut Pesterwitz",
"lat": "51.02883",
"lon": "13.64144",
"id": 478369183,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"website": "https://www.gut-pesterwitz.de/"
}
},
{
"name": "Schillergarten",
"lat": "51.05228",
"lon": "13.80908",
"id": 493702544,
"subType": "biergarten",
"type": "sustenance",
"openingHours": "Mo-Su 11:00-01:00",
"additionalInfo": {
"wheelchair_yes": "yes",
"opening_hours": "Mo-Su 11:00-01:00"
}
},
{
"lat": "51.04111",
"lon": "13.80554",
"id": 5837244174,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"operator": "Astloch"
}
},
{
"name": "Spitzwegerich",
"lat": "51.07767",
"lon": "13.70757",
"id": 412610355,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"wheelchair_yes": "yes",
"toilets_wheelchair_no": "no"
}
},
{
"name": "Biergarten Goldener Anker",
"lat": "51.10399",
"lon": "13.62850",
"id": 143015201,
"subType": "biergarten",
"type": "sustenance",
"openingHours": "Tu-Su,PH 11:00-21:00",
"additionalInfo": {
"wheelchair_yes": "yes",
"opening_hours": "Tu-Su,PH 11:00-21:00"
}
},
{
"name": "Bottoms Up",
"lat": "51.06516",
"lon": "13.75615",
"id": 1394136626,
"subType": "biergarten",
"type": "sustenance"
},
{
"name": "Biergarten Narrenhäus'l",
"lat": "51.05666",
"lon": "13.74130",
"id": 466763007,
"subType": "biergarten",
"type": "sustenance"
},
{
"name": "Zum Schießhaus",
"lat": "51.05464",
"lon": "13.72727",
"id": 326416539,
"subType": "biergarten",
"type": "sustenance",
"openingHours": "Mo-Su,PH 11:00-23:00",
"additionalInfo": {
"wheelchair_yes": "yes",
"toilets_wheelchair_no": "no",
"opening_hours": "Mo-Su,PH 11:00-23:00"
}
},
{
"name": "Palais Bistro",
"lat": "51.05182",
"lon": "13.73682",
"id": 501015667,
"subType": "biergarten",
"type": "sustenance",
"openingHours": "Apr-Oct: Mo-Su 11:00-24:00, Nov-Mar: Mo-Th 12:00-14:30,17:30-24:00, Nov-Mar: Fr-Su 12:00-24:00",
"additionalInfo": {
"wheelchair_yes": "yes",
"opening_hours": "Apr-Oct: Mo-Su 11:00-24:00, Nov-Mar: Mo-Th 12:00-14:30,17:30-24:00, Nov-Mar: Fr-Su 12:00-24:00"
}
},
{
"name": "Brauhaus am Waldschlößchen",
"lat": "51.06778",
"lon": "13.77786",
"id": 594989978,
"subType": "biergarten",
"type": "sustenance",
"openingHours": "Mo-Su 11:00-01:00",
"additionalInfo": {
"wheelchair_limited": "limited",
"opening_hours": "Mo-Su 11:00-01:00"
}
},
{
"lat": "51.00222",
"lon": "13.68504",
"id": 2469999180,
"subType": "biergarten",
"type": "sustenance"
},
{
"name": "Biergarten Elbsegler",
"lat": "51.05738",
"lon": "13.73913",
"id": 466539133,
"subType": "biergarten",
"type": "sustenance"
},
{
"lat": "51.01010",
"lon": "13.66208",
"id": 7987508388,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"wheelchair_yes": "yes",
"operator": "Zum goldenen Löwen"
}
},
{
"lat": "50.99476",
"lon": "13.72701",
"id": 6765029580,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"operator": "Eutschützer Mühle"
}
},
{
"lat": "50.99295",
"lon": "13.64362",
"id": 6065174964,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"operator": "Alte Schmiede"
}
},
{
"name": "Louisengarten",
"lat": "51.06681",
"lon": "13.75278",
"id": 68516655,
"subType": "biergarten",
"type": "sustenance",
"openingHours": "Apr-Sep Su-Th 16:00+; Apr-Sep Fr,Sa 15:00+ || off \"Bei unfreundlichem Wetter\"",
"additionalInfo": {
"wheelchair_no": "no",
"opening_hours": "Apr-Sep Su-Th 16:00+; Apr-Sep Fr,Sa 15:00+ || off \"Bei unfreundlichem Wetter\"",
"website": "http://www.biergarten-dresden.de",
"note": "Neustädter Winter Hüttn von Oktober-Dezember",
"alt_name": "Neustädter Winter Hüttn"
}
},
{
"lat": "51.00871",
"lon": "13.65959",
"id": 9237211698,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"wheelchair_yes": "yes",
"operator": "Akropolis"
}
},
{
"lat": "51.03435",
"lon": "13.76042",
"id": 595971770,
"subType": "biergarten",
"type": "sustenance",
"openingHours": "Apr-Oct: Mo-Fr 11:00-19:00; Sa-Su 10:00-19:00; PH 10:00-19:00",
"additionalInfo": {
"wheelchair_yes": "yes",
"opening_hours": "Apr-Oct: Mo-Fr 11:00-19:00; Sa-Su 10:00-19:00; PH 10:00-19:00",
"website": "https://www.grosser-garten-dresden.de/de/gaesteservice/gastronomie-shop/",
"phone": "+49 152 37 00 67 53"
}
},
{
"name": "Landgut Hofewiese",
"lat": "51.10989",
"lon": "13.83207",
"id": 8455509378,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"wheelchair_yes": "yes"
}
},
{
"name": "Café & Restaurant Saite",
"lat": "51.07636",
"lon": "13.74825",
"id": 577481323,
"subType": "biergarten",
"type": "sustenance",
"openingHours": "Mo-Fr 18:00-24:00, Su 10:00-15:00",
"additionalInfo": {
"wheelchair_limited": "limited",
"opening_hours": "Mo-Fr 18:00-24:00, Su 10:00-15:00"
}
},
{
"name": "Alt-Dresden",
"lat": "51.04977",
"lon": "13.69881",
"id": 970594717,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"wheelchair_limited": "limited",
"phone": "0351 4135133"
}
},
{
"lat": "51.08677",
"lon": "13.70081",
"id": 413620001,
"subType": "biergarten",
"type": "sustenance"
},
{
"lat": "50.98618",
"lon": "13.63208",
"id": 10355747978,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"operator": "Hirschbergschenke"
}
},
{
"name": "Fährgarten Johannstadt",
"lat": "51.06151",
"lon": "13.76679",
"id": 4415013507072,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"wheelchair_yes": "yes",
"outdoor_seating_yes": "yes",
"outdoor_seating_filter_yes": "yes",
"website": "http://www.faehrgarten.de/",
"phone": "+49 351 4596262",
"brewery_additional": "Radeberger",
"email": "info@faehrgarten.de"
}
},
{
"lat": "51.02343",
"lon": "13.73347",
"id": 4563637454,
"subType": "biergarten",
"type": "sustenance"
},
{
"name": "Besenwirtschaft Steinrücken",
"lat": "51.11590",
"lon": "13.62457",
"id": 7474665554,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"website": "http://www.besenwirtschaft-steinruecken.de/index.html"
}
},
{
"lat": "51.05181",
"lon": "13.81396",
"id": 9819148822,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"operator": "WSV \"Am Blauen Wunder\""
}
},
{
"name": "Zacke",
"lat": "51.01300",
"lon": "13.63618",
"id": 7498805230,
"subType": "biergarten",
"type": "sustenance"
},
{
"name": "Klotzscher Sommerwirtschaft",
"lat": "51.11072",
"lon": "13.76879",
"id": 717794449,
"subType": "biergarten",
"type": "sustenance",
"openingHours": "Mo-Fr 17:00-23:00; Sa-Su 11:00-23:00; Nov-Mar off",
"additionalInfo": {
"opening_hours": "Mo-Fr 17:00-23:00; Sa-Su 11:00-23:00; Nov-Mar off",
"website": "http://klotzschersommerwirtschaft.de",
"phone": "+49 351 8804570",
"fax": "+49 351 8902050",
"email": "Kontakt@klotzscher-sommerwirtschaft.de",
"capacity": "20"
}
},
{
"name": "Brauhaus Watzke",
"lat": "51.07744",
"lon": "13.71540",
"id": 397976739,
"subType": "biergarten",
"type": "sustenance",
"additionalInfo": {
"wheelchair_limited": "limited",
"toilets_wheelchair_no": "no"
}
},
{
"name": "Trachauer Sommergarten",
"lat": "51.09166",
"lon": "13.70289",
"id": 1822089672,
"subType": "biergarten",
"type": "sustenance",
"openingHours": "Mo-Su 17:00-21:00",
"additionalInfo": {
"opening_hours": "Mo-Su 17:00-21:00",
"note": "Auch im Winter geöffnet"
}
},
{
"lat": "51.06520",
"lon": "13.82406",
"id": 11565282314,
"subType": "biergarten",
"type": "sustenance",
"openingHours": "seasonal",
"additionalInfo": {
"outdoor_seating_yes": "yes",
"outdoor_seating_filter_yes": "yes",
"opening_hours": "seasonal"
}
}
]
}

View file

@ -1,25 +0,0 @@
{
"settings": {
"lat": "51.04933",
"lon": "13.73815",
"radiusLevel": 1,
"totalLimit": -1,
"lang": "",
"transliterateIfMissing": false,
"emptyQueryAllowed": false,
"sortByName": false
},
"phrase": ["Fuel", "Fuel "],
"results": [[
"Aircraft fuel station (Filling station) [[1, POI_TYPE, 1.000, 0.00 km]]",
"Gas station (Filling station) [[1, POI_TYPE, 1.000, 0.00 km]]",
"Gas station for boats (Filling station) [[1, POI_TYPE, 1.000, 0.00 km]]"
],
[
"Aircraft fuel station (Filling station) [[1, POI_TYPE, 1.000, 0.00 km]]",
"Gas station (Filling station) [[1, POI_TYPE, 1.000, 0.00 km]]",
"Gas station for boats (Filling station) [[1, POI_TYPE, 1.000, 0.00 km]]"
]],
"amenities": [],
"cities": []
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:focusableInTouchMode="true"
android:orientation="vertical"> android:orientation="vertical">
<net.osmand.plus.widgets.TextViewEx <net.osmand.plus.widgets.TextViewEx
@ -18,8 +19,7 @@
android:paddingRight="@dimen/content_padding" android:paddingRight="@dimen/content_padding"
android:paddingTop="@dimen/content_padding_small" android:paddingTop="@dimen/content_padding_small"
android:paddingBottom="@dimen/content_padding_small" android:paddingBottom="@dimen/content_padding_small"
android:textAppearance="@style/TextAppearance.ListItemTitle" android:textAppearance="@style/TextAppearance.ListItemCategoryTitle"
osmand:typeface="@string/font_roboto_medium"
tools:text="Some Title" tools:text="Some Title"
android:paddingStart="@dimen/content_padding" android:paddingStart="@dimen/content_padding"
android:paddingEnd="@dimen/content_padding" /> android:paddingEnd="@dimen/content_padding" />
@ -43,7 +43,7 @@
android:paddingBottom="@dimen/content_padding" android:paddingBottom="@dimen/content_padding"
android:textColor="?android:textColorSecondary" android:textColor="?android:textColorSecondary"
android:textSize="@dimen/default_list_text_size" android:textSize="@dimen/default_list_text_size"
android:lineSpacingMultiplier="@dimen/bottom_sheet_info_spacing_multiplier" android:lineSpacingMultiplier="@dimen/bottom_sheet_text_spacing_multiplier"
osmand:typeface="@string/font_roboto_regular" osmand:typeface="@string/font_roboto_regular"
tools:text="@string/weight_limit_description" tools:text="@string/weight_limit_description"
android:paddingEnd="@dimen/content_padding" android:paddingEnd="@dimen/content_padding"
@ -52,15 +52,14 @@
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/content_padding_half"
android:layout_marginLeft="@dimen/content_padding" android:layout_marginLeft="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"> android:layout_marginRight="@dimen/content_padding">
<LinearLayout <LinearLayout
android:id="@+id/text" android:id="@+id/text"
android:paddingTop="@dimen/content_padding_half"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal" android:orientation="horizontal"
android:baselineAligned="false"> android:baselineAligned="false">
@ -70,20 +69,19 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
osmand:hintEnabled="false" osmand:hintEnabled="false"
android:gravity="start"
android:layout_weight="1" android:layout_weight="1"
osmand:boxBackgroundColor="@color/material_text_input_layout_bg"> osmand:boxBackgroundColor="@color/material_text_input_layout_bg">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/text_edit" android:id="@+id/text_edit"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="@dimen/favorites_list_item_height"
android:gravity="end" android:gravity="end|center_vertical"
android:inputType="numberDecimal" android:inputType="numberDecimal"
android:maxLines="1" android:maxLines="1"
android:maxLength="5" android:maxLength="5"
android:minHeight="@dimen/favorites_list_item_height" android:paddingTop="0dp"
android:paddingBottom="@dimen/card_content_padding_large" android:paddingBottom="0dp"
android:paddingStart="@dimen/content_padding_small" android:paddingStart="@dimen/content_padding_small"
android:paddingLeft="@dimen/content_padding_small" android:paddingLeft="@dimen/content_padding_small"
android:paddingRight="@dimen/content_padding_small" android:paddingRight="@dimen/content_padding_small"
@ -98,7 +96,7 @@
android:id="@+id/metric" android:id="@+id/metric"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="start|center_vertical"
android:paddingStart="@dimen/content_padding_small" android:paddingStart="@dimen/content_padding_small"
android:paddingRight="@dimen/content_padding_small" android:paddingRight="@dimen/content_padding_small"
android:paddingEnd="@dimen/content_padding_small" android:paddingEnd="@dimen/content_padding_small"
@ -121,5 +119,4 @@
osmand:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" osmand:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:itemCount="3" tools:itemCount="3"
tools:listitem="@layout/point_editor_icon_category_item" /> tools:listitem="@layout/point_editor_icon_category_item" />
</LinearLayout> </LinearLayout>

View file

@ -2,6 +2,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/buttons_container" android:id="@+id/buttons_container"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:minWidth="@dimen/favorites_icon_outline_size"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:gravity="center" android:gravity="center"

View file

@ -13,6 +13,7 @@
--> -->
<string name="shared_string_uninstall_and_restart">Uninstall and Restart</string> <string name="shared_string_uninstall_and_restart">Uninstall and Restart</string>
<string name="speed_cameras_restart_descr">Restart is needed to completely delete speed camera data.</string> <string name="speed_cameras_restart_descr">Restart is needed to completely delete speed camera data.</string>
<string name="shared_string_bearing">Bearing</string>
<string name="quick_action_showhide_mapillary_descr">A toggle to show or hide the Mapillary layer on the map.</string> <string name="quick_action_showhide_mapillary_descr">A toggle to show or hide the Mapillary layer on the map.</string>
<string name="quick_action_mapillary_show">Show Mapillary</string> <string name="quick_action_mapillary_show">Show Mapillary</string>
<string name="quick_action_mapillary_hide">Hide Mapillary</string> <string name="quick_action_mapillary_hide">Hide Mapillary</string>

View file

@ -106,12 +106,16 @@
<item name="colorPrimary">@color/active_color_primary_light</item> <item name="colorPrimary">@color/active_color_primary_light</item>
<item name="dashboard_divider">@color/divider_color_light</item> <item name="dashboard_divider">@color/divider_color_light</item>
<item name="divider_color">@color/divider_color</item> <item name="divider_color">@color/divider_color</item>
<item name="android:textColorPrimary">@color/text_color_primary_light</item>
<item name="android:textColorSecondary">@color/text_color_secondary_light</item>
</style> </style>
<style name="OsmandMaterialDarkTheme" parent="Theme.MaterialComponents"> <style name="OsmandMaterialDarkTheme" parent="Theme.MaterialComponents">
<item name="colorPrimary">@color/active_color_primary_dark</item> <item name="colorPrimary">@color/active_color_primary_dark</item>
<item name="dashboard_divider">@color/divider_color_dark</item> <item name="dashboard_divider">@color/divider_color_dark</item>
<item name="divider_color">@color/divider_color_dark</item> <item name="divider_color">@color/divider_color_dark</item>
<item name="android:textColorPrimary">@color/text_color_primary_dark</item>
<item name="android:textColorSecondary">@color/text_color_secondary_dark</item>
</style> </style>
<!-- MaterialComponents Widgets override --> <!-- MaterialComponents Widgets override -->

View file

@ -289,6 +289,10 @@ public class PointDescription {
return POINT_TYPE_CUSTOM_POI_FILTER.equals(type); return POINT_TYPE_CUSTOM_POI_FILTER.equals(type);
} }
public boolean isGpxPoint() {
return POINT_TYPE_GPX.equals(type);
}
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;

View file

@ -673,6 +673,11 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven
} }
if (trackDetailsMenu.isVisible()) { if (trackDetailsMenu.isVisible()) {
trackDetailsMenu.hide(true); trackDetailsMenu.hide(true);
if (mapContextMenu.isActive() && mapContextMenu.getPointDescription() != null
&& mapContextMenu.getPointDescription().isGpxPoint()) {
mapContextMenu.show();
return;
}
if (prevActivityIntent == null) { if (prevActivityIntent == null) {
return; return;
} }

View file

@ -754,6 +754,10 @@ public class MenuBuilder {
return iconsCache.getIcon(iconId, light ? R.color.ctx_menu_bottom_view_icon_light : R.color.ctx_menu_bottom_view_icon_dark); return iconsCache.getIcon(iconId, light ? R.color.ctx_menu_bottom_view_icon_light : R.color.ctx_menu_bottom_view_icon_dark);
} }
public Drawable getThemedIcon(int iconId) {
return app.getUIUtilities().getThemedIcon(iconId);
}
public Drawable getRowIcon(Context ctx, String fileName) { public Drawable getRowIcon(Context ctx, String fileName) {
Drawable d = RenderingIcons.getBigIcon(ctx, fileName); Drawable d = RenderingIcons.getBigIcon(ctx, fileName);
if (d != null) { if (d != null) {

View file

@ -32,7 +32,6 @@ import net.osmand.data.TransportStop;
import net.osmand.map.OsmandRegions; import net.osmand.map.OsmandRegions;
import net.osmand.map.WorldRegion; import net.osmand.map.WorldRegion;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
import net.osmand.plus.MapMarkersHelper.MapMarker; import net.osmand.plus.MapMarkersHelper.MapMarker;
import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmAndFormatter;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
@ -61,6 +60,7 @@ import net.osmand.plus.mapcontextmenu.controllers.MyLocationMenuController;
import net.osmand.plus.mapcontextmenu.controllers.PointDescriptionMenuController; import net.osmand.plus.mapcontextmenu.controllers.PointDescriptionMenuController;
import net.osmand.plus.mapcontextmenu.controllers.RenderedObjectMenuController; import net.osmand.plus.mapcontextmenu.controllers.RenderedObjectMenuController;
import net.osmand.plus.mapcontextmenu.controllers.SelectedGpxMenuController; import net.osmand.plus.mapcontextmenu.controllers.SelectedGpxMenuController;
import net.osmand.plus.mapcontextmenu.controllers.SelectedGpxMenuController.SelectedGpxPoint;
import net.osmand.plus.mapcontextmenu.controllers.TargetPointMenuController; import net.osmand.plus.mapcontextmenu.controllers.TargetPointMenuController;
import net.osmand.plus.mapcontextmenu.controllers.TransportRouteController; import net.osmand.plus.mapcontextmenu.controllers.TransportRouteController;
import net.osmand.plus.mapcontextmenu.controllers.TransportStopController; import net.osmand.plus.mapcontextmenu.controllers.TransportStopController;
@ -229,8 +229,8 @@ public abstract class MenuController extends BaseMenuController implements Colla
menuController = new RenderedObjectMenuController(mapActivity, pointDescription, (RenderedObject) object); menuController = new RenderedObjectMenuController(mapActivity, pointDescription, (RenderedObject) object);
} else if (object instanceof MapillaryImage) { } else if (object instanceof MapillaryImage) {
menuController = new MapillaryMenuController(mapActivity, pointDescription, (MapillaryImage) object); menuController = new MapillaryMenuController(mapActivity, pointDescription, (MapillaryImage) object);
} else if (object instanceof SelectedGpxFile) { } else if (object instanceof SelectedGpxPoint) {
menuController = new SelectedGpxMenuController(mapActivity, pointDescription, (SelectedGpxFile) object); menuController = new SelectedGpxMenuController(mapActivity, pointDescription, (SelectedGpxPoint) object);
} }
} }
if (menuController == null) { if (menuController == null) {

View file

@ -0,0 +1,214 @@
package net.osmand.plus.mapcontextmenu.builders;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import net.osmand.AndroidUtils;
import net.osmand.GPXUtilities.GPXTrackAnalysis;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.plus.OsmAndFormatter;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.mapcontextmenu.MenuBuilder;
import net.osmand.plus.mapcontextmenu.controllers.SelectedGpxMenuController.SelectedGpxPoint;
import net.osmand.util.Algorithms;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SelectedGpxMenuBuilder extends MenuBuilder {
private SelectedGpxPoint selectedGpxPoint;
private GPXTrackAnalysis analysis;
private WptPt selectedPoint;
public SelectedGpxMenuBuilder(@NonNull MapActivity mapActivity, @NonNull SelectedGpxPoint selectedGpxPoint) {
super(mapActivity);
this.selectedGpxPoint = selectedGpxPoint;
selectedPoint = selectedGpxPoint.getSelectedPoint();
analysis = selectedGpxPoint.getSelectedGpxFile().getTrackAnalysis(mapActivity.getMyApplication());
}
@Override
protected boolean needBuildPlainMenuItems() {
return false;
}
@Override
protected boolean needBuildCoordinatesRow() {
return true;
}
@Override
public void buildInternal(View view) {
buildOverviewRows(view);
buildElevationRows(view);
buildSpeedRows(view);
buildPointRows(view);
}
public void buildOverviewRows(View view) {
buildCategoryView(view, app.getString(R.string.shared_string_overview));
buildRow(view, getThemedIcon(R.drawable.ic_action_polygom_dark), null, app.getString(R.string.distance),
OsmAndFormatter.getFormattedDistance(analysis.totalDistance, app), 0, null,
false, null, false, 0, false, false, false, null, false);
String timeSpan = Algorithms.formatDuration((int) (analysis.timeSpan / 1000), app.accessibilityEnabled());
String timeMoving = Algorithms.formatDuration((int) (analysis.timeMoving / 1000), app.accessibilityEnabled());
String title = app.getString(R.string.shared_string_time_span) + " / " + app.getString(R.string.shared_string_time_moving);
buildRow(view, getThemedIcon(R.drawable.ic_action_time_span), null, title,
timeSpan + " / " + timeMoving, 0, null,
false, null, false, 0, false, false, false, null, false);
Date start = new Date(analysis.startTime);
Date end = new Date(analysis.endTime);
DateFormat format = SimpleDateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
buildRow(view, getThemedIcon(R.drawable.ic_action_time_start), null, app.getString(R.string.shared_string_start_time),
format.format(start) + " / " + format.format(end), 0, null,
false, null, false, 0, false, false, false, null, true);
}
public void buildElevationRows(View view) {
if (analysis.isElevationSpecified()) {
buildCategoryView(view, app.getString(R.string.altitude));
buildRow(view, getThemedIcon(R.drawable.ic_action_altitude_average), null, app.getString(R.string.average_altitude),
OsmAndFormatter.getFormattedAlt(analysis.avgElevation, app), 0, null,
false, null, false, 0, false, false, false, null, false);
String min = OsmAndFormatter.getFormattedAlt(analysis.minElevation, app);
String max = OsmAndFormatter.getFormattedAlt(analysis.maxElevation, app);
buildRow(view, getThemedIcon(R.drawable.ic_action_altitude_range_16), null, app.getString(R.string.altitude_range),
min + " - " + max, 0, null,
false, null, false, 0, false, false, false, null, false);
String asc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationUp, app);
String desc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationDown, app);
buildRow(view, getThemedIcon(R.drawable.ic_action_altitude_descent), null, app.getString(R.string.ascent_descent),
asc + " / " + desc, 0, null,
false, null, false, 0, false, false, false, null, true);
}
}
public void buildSpeedRows(View view) {
if (analysis.isSpeedSpecified()) {
buildCategoryView(view, app.getString(R.string.map_widget_speed));
buildRow(view, getThemedIcon(R.drawable.ic_action_speed), null, app.getString(R.string.average_speed),
OsmAndFormatter.getFormattedSpeed(analysis.avgSpeed, app), 0, null,
false, null, false, 0, false, false, false, null, false);
buildRow(view, getThemedIcon(R.drawable.ic_action_max_speed), null, app.getString(R.string.max_speed),
OsmAndFormatter.getFormattedSpeed(analysis.maxSpeed, app), 0, null,
false, null, false, 0, false, false, false, null, false);
buildRow(view, getThemedIcon(R.drawable.ic_action_altitude_descent), null, app.getString(R.string.distance_moving),
OsmAndFormatter.getFormattedDistance(analysis.totalDistanceMoving, app), 0, null,
false, null, false, 0, false, false, false, null, true);
}
}
public void buildPointRows(View view) {
buildCategoryView(view, app.getString(R.string.plugin_distance_point));
buildRow(view, getThemedIcon(R.drawable.ic_action_polygom_dark), null, app.getString(R.string.distance),
OsmAndFormatter.getFormattedDistance((float) selectedPoint.distance, app), 0, null,
false, null, false, 0, false, false, false, null, false);
if (selectedPoint.time != 0) {
DateFormat format = SimpleDateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
buildRow(view, getThemedIcon(R.drawable.ic_action_time_start), null, app.getString(R.string.shared_string_time),
format.format(selectedPoint.time), 0, null,
false, null, false, 0, false, false, false, null, true);
}
if (!Double.isNaN(selectedPoint.ele)) {
buildRow(view, getThemedIcon(R.drawable.ic_action_altitude), null, app.getString(R.string.altitude),
OsmAndFormatter.getFormattedAlt(selectedPoint.ele, app), 0, null,
false, null, false, 0, false, false, false, null, false);
}
if (!Double.isNaN(selectedPoint.speed)) {
buildRow(view, getThemedIcon(R.drawable.ic_action_speed), null, app.getString(R.string.average_speed),
OsmAndFormatter.getFormattedSpeed((float) selectedPoint.speed, app), 0, null,
false, null, false, 0, false, false, false, null, false);
}
if (selectedGpxPoint.getPointLocation().hasBearing()) {
buildRow(view, getThemedIcon(R.drawable.ic_action_relative_bearing), null, app.getString(R.string.shared_string_bearing),
OsmAndFormatter.getFormattedAzimuth(selectedGpxPoint.getPointLocation().getBearing(), app), 0, null,
false, null, false, 0, false, false, false, null, false);
}
}
private void buildCategoryView(View view, String name) {
if (!isFirstRow()) {
buildRowDivider(view);
}
View categoryView = UiUtilities.getInflater(view.getContext(), !light).inflate(R.layout.preference_category_with_descr, (ViewGroup) view, false);
AndroidUiHelper.updateVisibility(categoryView.findViewById(android.R.id.icon), false);
AndroidUiHelper.updateVisibility(categoryView.findViewById(android.R.id.summary), false);
TextView title = categoryView.findViewById(android.R.id.title);
title.setText(name);
((LinearLayout) view).addView(categoryView);
}
@Override
public boolean hasCustomAddressLine() {
return true;
}
public void buildCustomAddressLine(LinearLayout ll) {
int gpxSmallIconMargin = (int) ll.getResources().getDimension(R.dimen.gpx_small_icon_margin);
int gpxSmallTextMargin = (int) ll.getResources().getDimension(R.dimen.gpx_small_text_margin);
float gpxTextSize = ll.getResources().getDimension(R.dimen.default_desc_text_size);
int textColor = ContextCompat.getColor(ll.getContext(), light ? R.color.text_color_primary_light : R.color.text_color_primary_dark);
buildIcon(ll, gpxSmallIconMargin, R.drawable.ic_action_distance_16);
buildTextView(ll, gpxSmallTextMargin, gpxTextSize, textColor,
OsmAndFormatter.getFormattedDistance(analysis.totalDistance, app));
buildIcon(ll, gpxSmallIconMargin, R.drawable.ic_action_waypoint_16);
buildTextView(ll, gpxSmallTextMargin, gpxTextSize, textColor, "" + analysis.wptPoints);
buildIcon(ll, gpxSmallIconMargin, R.drawable.ic_action_time_16);
buildTextView(ll, gpxSmallTextMargin, gpxTextSize, textColor,
Algorithms.formatDuration((int) (analysis.timeSpan / 1000), app.accessibilityEnabled()) + "");
}
private void buildIcon(LinearLayout ll, int gpxSmallIconMargin, int iconId) {
ImageView icon = new ImageView(ll.getContext());
LinearLayout.LayoutParams llIconParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
AndroidUtils.setMargins(llIconParams, 0, 0, gpxSmallIconMargin, 0);
llIconParams.gravity = Gravity.CENTER_VERTICAL;
icon.setLayoutParams(llIconParams);
icon.setImageDrawable(app.getUIUtilities().getThemedIcon(iconId));
ll.addView(icon);
}
private void buildTextView(LinearLayout ll, int gpxSmallTextMargin, float gpxTextSize, int textColor, String text) {
TextView textView = new TextView(ll.getContext());
LinearLayout.LayoutParams llTextParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
AndroidUtils.setMargins(llTextParams, 0, 0, gpxSmallTextMargin, 0);
textView.setLayoutParams(llTextParams);
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, gpxTextSize);
textView.setTextColor(textColor);
textView.setText(text);
ll.addView(textView);
}
}

View file

@ -7,7 +7,10 @@ import android.os.AsyncTask;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import net.osmand.AndroidUtils;
import net.osmand.GPXUtilities; import net.osmand.GPXUtilities;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.Location;
import net.osmand.data.PointDescription; import net.osmand.data.PointDescription;
import net.osmand.plus.GpxSelectionHelper; import net.osmand.plus.GpxSelectionHelper;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
@ -16,28 +19,30 @@ import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.activities.TrackActivity; import net.osmand.plus.activities.TrackActivity;
import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.helpers.GpxUiHelper;
import net.osmand.plus.mapcontextmenu.MenuBuilder;
import net.osmand.plus.mapcontextmenu.MenuController; import net.osmand.plus.mapcontextmenu.MenuController;
import net.osmand.plus.mapcontextmenu.builders.SelectedGpxMenuBuilder;
import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.backend.OsmandSettings;
import java.io.File; import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class SelectedGpxMenuController extends MenuController { public class SelectedGpxMenuController extends MenuController {
private SelectedGpxFile item; private SelectedGpxPoint selectedGpxPoint;
public SelectedGpxMenuController(@NonNull final MapActivity mapActivity, @NonNull PointDescription pointDescription, @NonNull final SelectedGpxFile item) { public SelectedGpxMenuController(@NonNull final MapActivity mapActivity, @NonNull PointDescription pointDescription,
super(new MenuBuilder(mapActivity), pointDescription, mapActivity); @NonNull final SelectedGpxPoint selectedGpxPoint) {
this.item = item; super(new SelectedGpxMenuBuilder(mapActivity, selectedGpxPoint), pointDescription, mapActivity);
this.selectedGpxPoint = selectedGpxPoint;
builder.setShowOnlinePhotos(false);
leftTitleButtonController = new TitleButtonController() { leftTitleButtonController = new TitleButtonController() {
@Override @Override
public void buttonPressed() { public void buttonPressed() {
Intent intent = new Intent(mapActivity, mapActivity.getMyApplication().getAppCustomization().getTrackActivity()); Intent intent = new Intent(mapActivity, mapActivity.getMyApplication().getAppCustomization().getTrackActivity());
intent.putExtra(TrackActivity.TRACK_FILE_NAME, item.getGpxFile().path); intent.putExtra(TrackActivity.TRACK_FILE_NAME, selectedGpxPoint.getSelectedGpxFile().getGpxFile().path);
intent.putExtra(TrackActivity.OPEN_TRACKS_LIST, true);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
mapActivity.startActivity(intent); mapActivity.startActivity(intent);
} }
@ -48,45 +53,55 @@ public class SelectedGpxMenuController extends MenuController {
rightTitleButtonController = new TitleButtonController() { rightTitleButtonController = new TitleButtonController() {
@Override @Override
public void buttonPressed() { public void buttonPressed() {
new OpenGpxDetailsTask(item).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new OpenGpxDetailsTask(selectedGpxPoint.getSelectedGpxFile(), selectedGpxPoint.getSelectedPoint(), mapActivity).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
}; };
rightTitleButtonController.caption = mapActivity.getString(R.string.analyze_on_map); rightTitleButtonController.caption = mapActivity.getString(R.string.analyze_on_map);
rightTitleButtonController.startIconId = R.drawable.ic_action_track_16; rightTitleButtonController.startIconId = R.drawable.ic_action_track_16;
} }
private class OpenGpxDetailsTask extends AsyncTask<Void, Void, GpxSelectionHelper.GpxDisplayItem> { private static class OpenGpxDetailsTask extends AsyncTask<Void, Void, GpxSelectionHelper.GpxDisplayItem> {
private SelectedGpxFile item; private OsmandApplication app;
ProgressDialog progressDialog;
OpenGpxDetailsTask(SelectedGpxFile item) { private WptPt selectedPoint;
this.item = item; private SelectedGpxFile selectedGpxFile;
private ProgressDialog progressDialog;
private WeakReference<MapActivity> activityRef;
OpenGpxDetailsTask(SelectedGpxFile selectedGpxFile, WptPt selectedPoint, MapActivity mapActivity) {
app = mapActivity.getMyApplication();
this.activityRef = new WeakReference<>(mapActivity);
this.selectedGpxFile = selectedGpxFile;
this.selectedPoint = selectedPoint;
} }
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
if (item.getGpxFile().path != null) { MapActivity activity = activityRef.get();
progressDialog = new ProgressDialog(getMapActivity()); if (activity != null && AndroidUtils.isActivityNotDestroyed(activity)) {
if (selectedGpxFile.getGpxFile().path != null) {
progressDialog = new ProgressDialog(activity);
progressDialog.setTitle(""); progressDialog.setTitle("");
progressDialog.setMessage(getMapActivity().getResources().getString(R.string.loading_data)); progressDialog.setMessage(app.getString(R.string.loading_data));
progressDialog.setCancelable(false); progressDialog.setCancelable(false);
progressDialog.show(); progressDialog.show();
} }
} }
}
@Override @Override
protected GpxSelectionHelper.GpxDisplayItem doInBackground(Void... voids) { protected GpxSelectionHelper.GpxDisplayItem doInBackground(Void... voids) {
GpxSelectionHelper.GpxDisplayGroup gpxDisplayGroup = null; GpxSelectionHelper.GpxDisplayGroup gpxDisplayGroup = null;
GPXUtilities.GPXFile gpxFile = null; GPXUtilities.GPXFile gpxFile = null;
GPXUtilities.Track generalTrack = null; GPXUtilities.Track generalTrack = null;
if (item.getGpxFile().path != null) { if (selectedGpxFile.getGpxFile().path != null) {
gpxFile = GPXUtilities.loadGPXFile(new File(item.getGpxFile().path)); gpxFile = GPXUtilities.loadGPXFile(new File(selectedGpxFile.getGpxFile().path));
} }
if (gpxFile != null) { if (gpxFile != null) {
generalTrack = gpxFile.getGeneralTrack(); generalTrack = gpxFile.getGeneralTrack();
} }
OsmandApplication app = getMapActivity().getMyApplication();
if (generalTrack != null) { if (generalTrack != null) {
gpxFile.addGeneralTrack(); gpxFile.addGeneralTrack();
gpxDisplayGroup = app.getSelectedGpxHelper().buildGeneralGpxDisplayGroup(gpxFile, generalTrack); gpxDisplayGroup = app.getSelectedGpxHelper().buildGeneralGpxDisplayGroup(gpxFile, generalTrack);
@ -105,7 +120,9 @@ public class SelectedGpxMenuController extends MenuController {
@Override @Override
protected void onPostExecute(GpxSelectionHelper.GpxDisplayItem gpxItem) { protected void onPostExecute(GpxSelectionHelper.GpxDisplayItem gpxItem) {
if (progressDialog != null) { MapActivity activity = activityRef.get();
if (activity != null) {
if (progressDialog != null && AndroidUtils.isActivityNotDestroyed(activity)) {
progressDialog.dismiss(); progressDialog.dismiss();
} }
if (gpxItem != null && gpxItem.analysis != null) { if (gpxItem != null && gpxItem.analysis != null) {
@ -119,33 +136,32 @@ public class SelectedGpxMenuController extends MenuController {
list.add(GpxUiHelper.GPXDataSetType.SLOPE); list.add(GpxUiHelper.GPXDataSetType.SLOPE);
} }
if (list.size() > 0) { if (list.size() > 0) {
gpxItem.chartTypes = list.toArray(new GpxUiHelper.GPXDataSetType[list.size()]); gpxItem.chartTypes = list.toArray(new GpxUiHelper.GPXDataSetType[0]);
} }
gpxItem.locationOnMap = selectedPoint;
MapActivity mapActivity = getMapActivity(); OsmandSettings settings = app.getSettings();
OsmandApplication app = mapActivity.getMyApplication();
final OsmandSettings settings = app.getSettings();
settings.setMapLocationToShow(gpxItem.locationStart.lat, gpxItem.locationStart.lon, settings.setMapLocationToShow(gpxItem.locationStart.lat, gpxItem.locationStart.lon,
settings.getLastKnownMapZoom(), settings.getLastKnownMapZoom(),
new PointDescription(PointDescription.POINT_TYPE_WPT, gpxItem.name), new PointDescription(PointDescription.POINT_TYPE_WPT, gpxItem.name),
false, false,
gpxItem); gpxItem);
mapActivity.getContextMenu().hide(); activity.getContextMenu().hide();
MapActivity.launchMapActivityMoveToTop(mapActivity); MapActivity.launchMapActivityMoveToTop(activity);
}
} }
} }
} }
@Override @Override
protected void setObject(Object object) { protected void setObject(Object object) {
if (object instanceof SelectedGpxFile) { if (object instanceof SelectedGpxPoint) {
this.item = (SelectedGpxFile) object; this.selectedGpxPoint = (SelectedGpxPoint) object;
} }
} }
@Override @Override
protected Object getObject() { protected Object getObject() {
return item; return selectedGpxPoint;
} }
@NonNull @NonNull
@ -175,4 +191,29 @@ public class SelectedGpxMenuController extends MenuController {
int color = isLight() ? R.color.active_color_primary_light : R.color.active_color_primary_dark; int color = isLight() ? R.color.active_color_primary_light : R.color.active_color_primary_dark;
return getIcon(R.drawable.ic_action_polygom_dark, color); return getIcon(R.drawable.ic_action_polygom_dark, color);
} }
public static class SelectedGpxPoint {
private final WptPt selectedPoint;
private final SelectedGpxFile selectedGpxFile;
private final Location pointLocation;
public SelectedGpxPoint(SelectedGpxFile selectedGpxFile, WptPt selectedPoint, Location pointLocation) {
this.selectedPoint = selectedPoint;
this.selectedGpxFile = selectedGpxFile;
this.pointLocation = pointLocation;
}
public WptPt getSelectedPoint() {
return selectedPoint;
}
public SelectedGpxFile getSelectedGpxFile() {
return selectedGpxFile;
}
public Location getPointLocation() {
return pointLocation;
}
}
} }

View file

@ -57,7 +57,8 @@ public class HorizontalSelectionAdapter extends RecyclerView.Adapter<HorizontalS
ContextCompat.getColor(app, activeColorResId))); ContextCompat.getColor(app, activeColorResId)));
textView.setTextColor(ContextCompat.getColor(app, R.color.color_white)); textView.setTextColor(ContextCompat.getColor(app, R.color.color_white));
} else { } else {
textView.setTextColor(ContextCompat.getColor(app, R.color.preference_category_title)); textView.setTextColor(ContextCompat.getColor(app,
nightMode ? R.color.active_color_primary_dark : R.color.preference_category_title));
GradientDrawable buttonBackground = (GradientDrawable) AppCompatResources.getDrawable(app, GradientDrawable buttonBackground = (GradientDrawable) AppCompatResources.getDrawable(app,
R.drawable.bg_select_icon_group_button).mutate(); R.drawable.bg_select_icon_group_button).mutate();
buttonBackground.setStroke(AndroidUtils.dpToPx(app, 1), ContextCompat.getColor(app, buttonBackground.setStroke(AndroidUtils.dpToPx(app, 1), ContextCompat.getColor(app,
@ -67,6 +68,7 @@ public class HorizontalSelectionAdapter extends RecyclerView.Adapter<HorizontalS
AndroidUtils.setBackground(holder.button, buttonBackground); AndroidUtils.setBackground(holder.button, buttonBackground);
} }
textView.setText(capitalizeFirstLetter(item)); textView.setText(capitalizeFirstLetter(item));
textView.requestLayout();
holder.button.setOnClickListener(new View.OnClickListener() { holder.button.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {

View file

@ -212,8 +212,8 @@ public class TrackDetailsMenu {
} }
} }
public void updateInfo(final View main) { public void updateInfo(final View main, boolean forceFitTrackOnMap) {
updateView(main); updateView(main, forceFitTrackOnMap);
} }
@Nullable @Nullable
@ -485,7 +485,7 @@ public class TrackDetailsMenu {
return xAxisPoints; return xAxisPoints;
} }
private void updateView(final View parentView) { private void updateView(final View parentView, boolean forceFitTrackOnMap) {
MapActivity mapActivity = getMapActivity(); MapActivity mapActivity = getMapActivity();
GpxDisplayItem gpxItem = getGpxItem(); GpxDisplayItem gpxItem = getGpxItem();
if (mapActivity == null || gpxItem == null) { if (mapActivity == null || gpxItem == null) {
@ -724,7 +724,7 @@ public class TrackDetailsMenu {
xAxisArrow.setVisibility(View.GONE); xAxisArrow.setVisibility(View.GONE);
} }
refreshChart(chart, true); refreshChart(chart, forceFitTrackOnMap);
} }
private void updateChart(LineChart chart) { private void updateChart(LineChart chart) {
@ -737,6 +737,14 @@ public class TrackDetailsMenu {
} }
if (gpxItem.chartHighlightPos != -1) { if (gpxItem.chartHighlightPos != -1) {
chart.highlightValue(gpxItem.chartHighlightPos, 0); chart.highlightValue(gpxItem.chartHighlightPos, 0);
} else if (gpxItem.locationOnMap != null) {
LineData lineData = chart.getLineData();
List<ILineDataSet> ds = lineData != null ? lineData.getDataSets() : null;
if (ds != null && ds.size() > 0) {
OrderedLineDataSet dataSet = (OrderedLineDataSet) ds.get(0);
gpxItem.chartHighlightPos = (float) (gpxItem.locationOnMap.distance / dataSet.getDivX());
chart.highlightValue(gpxItem.chartHighlightPos, 0);
}
} else { } else {
chart.highlightValue(null); chart.highlightValue(null);
} }

View file

@ -23,6 +23,7 @@ import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.base.BaseOsmAndFragment; import net.osmand.plus.base.BaseOsmAndFragment;
import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.mapcontextmenu.MapContextMenu;
public class TrackDetailsMenuFragment extends BaseOsmAndFragment { public class TrackDetailsMenuFragment extends BaseOsmAndFragment {
public static final String TAG = "TrackDetailsMenuFragment"; public static final String TAG = "TrackDetailsMenuFragment";
@ -91,14 +92,20 @@ public class TrackDetailsMenuFragment extends BaseOsmAndFragment {
}); });
} }
updateInfo(); MapContextMenu contextMenu = mapActivity.getContextMenu();
final boolean forceFitTrackOnMap;
if (contextMenu.isActive()) {
forceFitTrackOnMap = !(contextMenu.getPointDescription() != null && contextMenu.getPointDescription().isGpxPoint());
} else {
forceFitTrackOnMap = true;
}
updateInfo(forceFitTrackOnMap);
ViewTreeObserver vto = mainView.getViewTreeObserver(); ViewTreeObserver vto = mainView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override @Override
public void onGlobalLayout() { public void onGlobalLayout() {
ViewTreeObserver obs = mainView.getViewTreeObserver(); ViewTreeObserver obs = mainView.getViewTreeObserver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
obs.removeOnGlobalLayoutListener(this); obs.removeOnGlobalLayoutListener(this);
@ -106,7 +113,7 @@ public class TrackDetailsMenuFragment extends BaseOsmAndFragment {
obs.removeGlobalOnLayoutListener(this); obs.removeGlobalOnLayoutListener(this);
} }
if (getMapActivity() != null) { if (getMapActivity() != null) {
updateInfo(); updateInfo(forceFitTrackOnMap);
} }
} }
}); });
@ -165,7 +172,11 @@ public class TrackDetailsMenuFragment extends BaseOsmAndFragment {
} }
public void updateInfo() { public void updateInfo() {
menu.updateInfo(mainView); updateInfo(true);
}
public void updateInfo(boolean forceFitTrackOnMap) {
menu.updateInfo(mainView, forceFitTrackOnMap);
applyDayNightMode(); applyDayNightMode();
} }

View file

@ -1,10 +1,19 @@
package net.osmand.plus.settings.bottomsheets; package net.osmand.plus.settings.bottomsheets;
import android.annotation.SuppressLint;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.EditText;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.ScrollView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -13,6 +22,7 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import net.osmand.AndroidUtils;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R; import net.osmand.plus.R;
@ -35,6 +45,8 @@ public class VehicleParametersBottomSheet extends BasePreferenceBottomSheet {
public static final String TAG = VehicleParametersBottomSheet.class.getSimpleName(); public static final String TAG = VehicleParametersBottomSheet.class.getSimpleName();
private String selectedItem; private String selectedItem;
private float currentValue; private float currentValue;
private int contentHeightPrevious = 0;
private EditText text;
@Override @Override
public void createMenuItems(Bundle savedInstanceState) { public void createMenuItems(Bundle savedInstanceState) {
@ -45,9 +57,10 @@ public class VehicleParametersBottomSheet extends BasePreferenceBottomSheet {
items.add(createBottomSheetItem(app)); items.add(createBottomSheetItem(app));
} }
@SuppressLint("ClickableViewAccessibility")
private BaseBottomSheetItem createBottomSheetItem(OsmandApplication app) { private BaseBottomSheetItem createBottomSheetItem(OsmandApplication app) {
final SizePreference preference = (SizePreference) getPreference(); final SizePreference preference = (SizePreference) getPreference();
View mainView = UiUtilities.getMaterialInflater(app, nightMode) View mainView = UiUtilities.getMaterialInflater(getContext(), nightMode)
.inflate(R.layout.bottom_sheet_item_edit_with_recyclerview, null); .inflate(R.layout.bottom_sheet_item_edit_with_recyclerview, null);
TextView title = mainView.findViewById(R.id.title); TextView title = mainView.findViewById(R.id.title);
title.setText(preference.getTitle().toString()); title.setText(preference.getTitle().toString());
@ -62,7 +75,8 @@ public class VehicleParametersBottomSheet extends BasePreferenceBottomSheet {
final HorizontalSelectionAdapter adapter = new HorizontalSelectionAdapter(app, nightMode); final HorizontalSelectionAdapter adapter = new HorizontalSelectionAdapter(app, nightMode);
final TextView metric = mainView.findViewById(R.id.metric); final TextView metric = mainView.findViewById(R.id.metric);
metric.setText(app.getString(preference.getAssets().getMetricRes())); metric.setText(app.getString(preference.getAssets().getMetricRes()));
final TextView text = mainView.findViewById(R.id.text_edit); final RecyclerView recyclerView = mainView.findViewById(R.id.recycler_view);
text = mainView.findViewById(R.id.text_edit);
try { try {
currentValue = Float.parseFloat(preference.getValue()); currentValue = Float.parseFloat(preference.getValue());
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
@ -72,6 +86,15 @@ public class VehicleParametersBottomSheet extends BasePreferenceBottomSheet {
String currentValueStr = currentValue == 0.0f ? "" : String.valueOf(currentValue + 0.01f); String currentValueStr = currentValue == 0.0f ? "" : String.valueOf(currentValue + 0.01f);
text.setText(currentValueStr); text.setText(currentValueStr);
text.clearFocus();
text.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
text.onTouchEvent(event);
text.setSelection(text.getText().length());
return true;
}
});
text.addTextChangedListener(new TextWatcher() { text.addTextChangedListener(new TextWatcher() {
@Override @Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@ -94,6 +117,10 @@ public class VehicleParametersBottomSheet extends BasePreferenceBottomSheet {
} }
selectedItem = preference.getEntryFromValue(String.valueOf(currentValue)); selectedItem = preference.getEntryFromValue(String.valueOf(currentValue));
adapter.setSelectedItem(selectedItem); adapter.setSelectedItem(selectedItem);
int itemPosition = adapter.getItemPosition(selectedItem);
if (itemPosition >= 0) {
recyclerView.smoothScrollToPosition(itemPosition);
}
} }
}); });
@ -105,11 +132,12 @@ public class VehicleParametersBottomSheet extends BasePreferenceBottomSheet {
currentValue = preference.getValueFromEntries(selectedItem); currentValue = preference.getValueFromEntries(selectedItem);
String currentValueStr = currentValue == 0.0f ? "" : String.valueOf(currentValue + 0.01f); String currentValueStr = currentValue == 0.0f ? "" : String.valueOf(currentValue + 0.01f);
text.setText(currentValueStr); text.setText(currentValueStr);
if (text.hasFocus()) {
text.setSelection(text.getText().length());
}
adapter.notifyDataSetChanged(); adapter.notifyDataSetChanged();
} }
}); });
RecyclerView recyclerView = mainView.findViewById(R.id.recycler_view);
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
adapter.setSelectedItem(selectedItem); adapter.setSelectedItem(selectedItem);
return new BaseBottomSheetItem.Builder() return new BaseBottomSheetItem.Builder()
@ -117,6 +145,45 @@ public class VehicleParametersBottomSheet extends BasePreferenceBottomSheet {
.create(); .create();
} }
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
final View view = super.onCreateView(inflater, parent, savedInstanceState);
view.getViewTreeObserver().addOnGlobalLayoutListener(getOnGlobalLayoutListener());
return view;
}
private ViewTreeObserver.OnGlobalLayoutListener getOnGlobalLayoutListener() {
final int buttonsHeight = getResources().getDimensionPixelSize(R.dimen.dialog_button_ex_height);
final int shadowHeight = AndroidUtils.dpToPx(getContext(), 8);
final int statusBarHeight = AndroidUtils.getStatusBarHeight(getContext());
return new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect visibleDisplayFrame = new Rect();
final ScrollView scrollView = getView().findViewById(R.id.scroll_view);
scrollView.getWindowVisibleDisplayFrame(visibleDisplayFrame);
int contentHeight = visibleDisplayFrame.bottom - visibleDisplayFrame.top - buttonsHeight
- shadowHeight - statusBarHeight;
if (contentHeightPrevious != contentHeight) {
if (scrollView.getHeight() > contentHeight) {
scrollView.getLayoutParams().height = contentHeight;
} else {
scrollView.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
}
scrollView.requestLayout();
int delay = Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP ? 300 : 1000;
scrollView.postDelayed(new Runnable() {
public void run() {
scrollView.scrollTo(0, scrollView.getHeight());
}
}, delay);
contentHeightPrevious = contentHeight;
}
}
};
}
@Override @Override
protected int getRightBottomButtonTextId() { protected int getRightBottomButtonTextId() {
return R.string.shared_string_apply; return R.string.shared_string_apply;

View file

@ -20,6 +20,7 @@ import net.osmand.plus.settings.bottomsheets.VehicleSizeAssets;
import net.osmand.plus.settings.preferences.ListPreferenceEx; import net.osmand.plus.settings.preferences.ListPreferenceEx;
import net.osmand.plus.settings.preferences.SizePreference; import net.osmand.plus.settings.preferences.SizePreference;
import net.osmand.router.GeneralRouter; import net.osmand.router.GeneralRouter;
import net.osmand.util.Algorithms;
import net.osmand.router.GeneralRouter.GeneralRouterProfile; import net.osmand.router.GeneralRouter.GeneralRouterProfile;
import java.util.Map; import java.util.Map;
@ -94,6 +95,11 @@ public class VehicleParametersFragment extends BaseSettingsFragment implements O
} }
String[] entriesStr = parameter.getPossibleValueDescriptions().clone(); String[] entriesStr = parameter.getPossibleValueDescriptions().clone();
entriesStr[0] = app.getString(R.string.shared_string_none); entriesStr[0] = app.getString(R.string.shared_string_none);
for (int i = 1; i < entriesStr.length; i++) {
int firstCharIndex = Algorithms.findFirstNumberEndIndex(entriesStr[i]);
entriesStr[i] = String.format(app.getString(R.string.ltr_or_rtl_combine_via_space),
entriesStr[i].substring(0, firstCharIndex), entriesStr[i].substring(firstCharIndex));
}
Context ctx = getContext(); Context ctx = getContext();
if (ctx == null) { if (ctx == null) {
@ -137,6 +143,14 @@ public class VehicleParametersFragment extends BaseSettingsFragment implements O
&& !ROUTING_PARAMETER_SYMBOLIC_DEFAULT.equals(currentValue); && !ROUTING_PARAMETER_SYMBOLIC_DEFAULT.equals(currentValue);
imageView.setEnabled(enabled); imageView.setEnabled(enabled);
} }
} else if (preference instanceof SizePreference) {
ImageView imageView = (ImageView) holder.findViewById(android.R.id.icon);
if (imageView != null) {
Object currentValue = ((SizePreference) preference).getValue();
boolean enabled = preference.isEnabled() && !ROUTING_PARAMETER_NUMERIC_DEFAULT.equals(currentValue)
&& !ROUTING_PARAMETER_SYMBOLIC_DEFAULT.equals(currentValue);
imageView.setEnabled(enabled);
}
} }
} }

View file

@ -81,7 +81,7 @@ public class SizePreference extends DialogPreference {
@Override @Override
public CharSequence getSummary() { public CharSequence getSummary() {
String summary = "-"; String summary = entries[0];
String persistedString = getValue(); String persistedString = getValue();
if (!persistedString.equals(defaultValue)) { if (!persistedString.equals(defaultValue)) {
try { try {
@ -89,7 +89,7 @@ public class SizePreference extends DialogPreference {
summary = String.format(getContext().getString(R.string.ltr_or_rtl_combine_via_space), summary = String.format(getContext().getString(R.string.ltr_or_rtl_combine_via_space),
persistedString, getContext().getString(assets.getMetricShortRes())); persistedString, getContext().getString(assets.getMetricShortRes()));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
summary = "-"; summary = entries[0];
} }
} }
return summary; return summary;

View file

@ -28,6 +28,7 @@ import net.osmand.GPXUtilities;
import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.TrkSegment; import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.GPXUtilities.WptPt; import net.osmand.GPXUtilities.WptPt;
import net.osmand.Location;
import net.osmand.data.LatLon; import net.osmand.data.LatLon;
import net.osmand.data.PointDescription; import net.osmand.data.PointDescription;
import net.osmand.data.QuadRect; import net.osmand.data.QuadRect;
@ -43,6 +44,7 @@ import net.osmand.plus.MapMarkersHelper.MapMarkersGroup;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.base.FavoriteImageDrawable; import net.osmand.plus.base.FavoriteImageDrawable;
import net.osmand.plus.mapcontextmenu.controllers.SelectedGpxMenuController.SelectedGpxPoint;
import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu.TrackChartPoints; import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu.TrackChartPoints;
import net.osmand.plus.render.OsmandRenderer; import net.osmand.plus.render.OsmandRenderer;
import net.osmand.plus.render.OsmandRenderer.RenderingContext; import net.osmand.plus.render.OsmandRenderer.RenderingContext;
@ -54,6 +56,7 @@ import net.osmand.render.RenderingRuleProperty;
import net.osmand.render.RenderingRuleSearchRequest; import net.osmand.render.RenderingRuleSearchRequest;
import net.osmand.render.RenderingRulesStorage; import net.osmand.render.RenderingRulesStorage;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
@ -604,16 +607,19 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
for (SelectedGpxFile selectedGpxFile : selectedGpxFiles) { for (SelectedGpxFile selectedGpxFile : selectedGpxFiles) {
List<TrkSegment> segments = selectedGpxFile.getPointsToDisplay(); List<TrkSegment> segments = selectedGpxFile.getPointsToDisplay();
for (TrkSegment segment : segments) { for (TrkSegment segment : segments) {
boolean nearSegment = isPointNearSegment(tb, segment.points, r, mx, my); QuadRect trackBounds = GPXUtilities.calculateBounds(segment.points);
if (nearSegment) { if (QuadRect.trivialOverlap(tb.getLatLonBounds(), trackBounds)) {
res.add(selectedGpxFile); SelectedGpxPoint selectedGpxPoint = findPointNearSegment(tb, selectedGpxFile, segment.points, r, mx, my);
if (selectedGpxPoint != null) {
res.add(selectedGpxPoint);
break; break;
} }
} }
} }
} }
}
private boolean isPointNearSegment(RotatedTileBox tb, List<WptPt> points, int r, int mx, int my) { private SelectedGpxPoint findPointNearSegment(RotatedTileBox tb, SelectedGpxFile selectedGpxFile, List<WptPt> points, int r, int mx, int my) {
WptPt firstPoint = points.get(0); WptPt firstPoint = points.get(0);
int ppx = (int) tb.getPixXFromLatLon(firstPoint.lat, firstPoint.lon); int ppx = (int) tb.getPixXFromLatLon(firstPoint.lat, firstPoint.lon);
int ppy = (int) tb.getPixYFromLatLon(firstPoint.lat, firstPoint.lon); int ppy = (int) tb.getPixYFromLatLon(firstPoint.lat, firstPoint.lon);
@ -625,7 +631,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
int py = (int) tb.getPixYFromLatLon(point.lat, point.lon); int py = (int) tb.getPixYFromLatLon(point.lat, point.lon);
int cross = placeInBbox(px, py, mx, my, r, r); int cross = placeInBbox(px, py, mx, my, r, r);
if (cross == 0) { if (cross == 0) {
return true; return createProjectionPoint(selectedGpxFile, points.get(i - 1), point, tb.getLatLonFromPixel(mx, my));
} }
if ((pcross & cross) == 0) { if ((pcross & cross) == 0) {
int mpx = px; int mpx = px;
@ -636,7 +642,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
int mpynew = mpy / 2 + ppy / 2; int mpynew = mpy / 2 + ppy / 2;
int mcrossnew = placeInBbox(mpxnew, mpynew, mx, my, r, r); int mcrossnew = placeInBbox(mpxnew, mpynew, mx, my, r, r);
if (mcrossnew == 0) { if (mcrossnew == 0) {
return true; return createProjectionPoint(selectedGpxFile, points.get(i - 1), point, tb.getLatLonFromPixel(mx, my));
} }
if ((mcrossnew & mcross) != 0) { if ((mcrossnew & mcross) != 0) {
mpx = mpxnew; mpx = mpxnew;
@ -656,7 +662,38 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
ppx = px; ppx = px;
ppy = py; ppy = py;
} }
return false; return null;
}
private SelectedGpxPoint createProjectionPoint(SelectedGpxFile selectedGpxFile, WptPt prevPoint, WptPt nextPoint, LatLon latLon) {
LatLon projection = MapUtils.getProjection(latLon.getLatitude(), latLon.getLongitude(), prevPoint.lat, prevPoint.lon, nextPoint.lat, nextPoint.lon);
WptPt projectionPoint = new WptPt();
projectionPoint.lat = projection.getLatitude();
projectionPoint.lon = projection.getLongitude();
projectionPoint.heading = prevPoint.heading;
projectionPoint.distance = prevPoint.distance + MapUtils.getDistance(projection, prevPoint.lat, prevPoint.lon);
projectionPoint.ele = getValueByDistInterpolation(projectionPoint.distance, prevPoint.distance, prevPoint.ele, nextPoint.distance, nextPoint.ele);
projectionPoint.speed = getValueByDistInterpolation(projectionPoint.distance, prevPoint.distance, prevPoint.speed, nextPoint.distance, nextPoint.speed);
if (prevPoint.time != 0 && nextPoint.time != 0) {
projectionPoint.time = (long) getValueByDistInterpolation(projectionPoint.distance, prevPoint.distance, prevPoint.time, nextPoint.distance, nextPoint.time);
}
Location projectionLocation = new Location("");
projectionLocation.setLatitude(projectionPoint.lat);
projectionLocation.setLongitude(projectionPoint.lon);
Location nextPointLocation = new Location("");
nextPointLocation.setLatitude(nextPoint.lat);
nextPointLocation.setLongitude(nextPoint.lon);
projectionLocation.setBearing(projectionLocation.bearingTo(nextPointLocation));
return new SelectedGpxPoint(selectedGpxFile, projectionPoint, projectionLocation);
}
private double getValueByDistInterpolation(double projectionDist, double prevDist, double prevVal, double nextDist, double nextVal) {
return prevVal + (projectionDist - prevDist) * ((nextVal - prevVal) / (nextDist - prevDist));
} }
int placeInBbox(int x, int y, int mx, int my, int halfw, int halfh) { int placeInBbox(int x, int y, int mx, int my, int halfw, int halfh) {
@ -672,9 +709,14 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
public PointDescription getObjectName(Object o) { public PointDescription getObjectName(Object o) {
if (o instanceof WptPt) { if (o instanceof WptPt) {
return new PointDescription(PointDescription.POINT_TYPE_WPT, ((WptPt) o).name); return new PointDescription(PointDescription.POINT_TYPE_WPT, ((WptPt) o).name);
} else if (o instanceof SelectedGpxFile) { } else if (o instanceof SelectedGpxPoint) {
SelectedGpxFile selectedGpxFile = (SelectedGpxFile) o; SelectedGpxFile selectedGpxFile = ((SelectedGpxPoint) o).getSelectedGpxFile();
String name = formatName(Algorithms.getFileWithoutDirs(selectedGpxFile.getGpxFile().path)); String name;
if (selectedGpxFile.isShowCurrentTrack()) {
name = view.getContext().getString(R.string.shared_string_currently_recording_track);
} else {
name = formatName(Algorithms.getFileWithoutDirs(selectedGpxFile.getGpxFile().path));
}
return new PointDescription(PointDescription.POINT_TYPE_GPX, name); return new PointDescription(PointDescription.POINT_TYPE_GPX, name);
} }
return null; return null;
@ -720,6 +762,9 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
public LatLon getObjectLocation(Object o) { public LatLon getObjectLocation(Object o) {
if (o instanceof WptPt) { if (o instanceof WptPt) {
return new LatLon(((WptPt) o).lat, ((WptPt) o).lon); return new LatLon(((WptPt) o).lat, ((WptPt) o).lon);
} else if (o instanceof SelectedGpxPoint) {
WptPt point = ((SelectedGpxPoint) o).getSelectedPoint();
return new LatLon(point.lat, point.lon);
} }
return null; return null;
} }

View file

@ -5,6 +5,7 @@ import android.graphics.Paint;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import net.osmand.GPXUtilities;
import net.osmand.GPXUtilities.WptPt; import net.osmand.GPXUtilities.WptPt;
import net.osmand.data.QuadRect; import net.osmand.data.QuadRect;
import net.osmand.data.RotatedTileBox; import net.osmand.data.RotatedTileBox;
@ -61,10 +62,10 @@ public class Renderable {
protected AsynchronousResampler culler = null; // The currently active resampler protected AsynchronousResampler culler = null; // The currently active resampler
protected Paint paint = null; // MUST be set by 'updateLocalPaint' before use protected Paint paint = null; // MUST be set by 'updateLocalPaint' before use
public RenderableSegment(List <WptPt> points, double segmentSize) { public RenderableSegment(List<WptPt> points, double segmentSize) {
this.points = points; this.points = points;
calculateBounds(points);
this.segmentSize = segmentSize; this.segmentSize = segmentSize;
trackBounds = GPXUtilities.calculateBounds(points);
} }
protected void updateLocalPaint(Paint p) { protected void updateLocalPaint(Paint p) {
@ -90,23 +91,6 @@ public class Renderable {
} }
} }
private void calculateBounds(List<WptPt> pts) {
trackBounds = new QuadRect(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY,
Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
updateBounds(pts, 0);
}
protected void updateBounds(List<WptPt> pts, int startIndex) {
pointSize = pts.size();
for (int i = startIndex; i < pointSize; i++) {
WptPt pt = pts.get(i);
trackBounds.right = Math.max(trackBounds.right, pt.lon);
trackBounds.left = Math.min(trackBounds.left, pt.lon);
trackBounds.top = Math.max(trackBounds.top, pt.lat);
trackBounds.bottom = Math.min(trackBounds.bottom, pt.lat);
}
}
public void setRDP(List<WptPt> cull) { public void setRDP(List<WptPt> cull) {
culled = cull; culled = cull;
} }
@ -198,7 +182,9 @@ public class Renderable {
@Override public void drawSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) { @Override public void drawSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {
if (points.size() != pointSize) { if (points.size() != pointSize) {
updateBounds(points, pointSize); int prevSize = pointSize;
pointSize = points.size();
GPXUtilities.updateBounds(trackBounds, points, prevSize);
} }
drawSingleSegment(zoom, p, canvas, tileBox); drawSingleSegment(zoom, p, canvas, tileBox);
} }