diff --git a/.github/ISSUE_TEMPLATE/3-bug-report.md b/.github/ISSUE_TEMPLATE/2-bug-report.md similarity index 100% rename from .github/ISSUE_TEMPLATE/3-bug-report.md rename to .github/ISSUE_TEMPLATE/2-bug-report.md diff --git a/.github/ISSUE_TEMPLATE/2-faq-report.md b/.github/ISSUE_TEMPLATE/2-faq-report.md deleted file mode 100644 index 531d263d7f..0000000000 --- a/.github/ISSUE_TEMPLATE/2-faq-report.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -name: "📚 Outdated FAQ" -about: Report an issue in FAQ ---- - -🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑 - -Please do not file FAQ issues on the GitHub issues tracker. - -Instead use the [Google group](https://groups.google.com/forum/#!forum/osmand) to fix wrong or outdated FAQ. - -🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑 diff --git a/.github/ISSUE_TEMPLATE/4-routing-report.md b/.github/ISSUE_TEMPLATE/3-routing-report.md similarity index 100% rename from .github/ISSUE_TEMPLATE/4-routing-report.md rename to .github/ISSUE_TEMPLATE/3-routing-report.md diff --git a/.github/ISSUE_TEMPLATE/5-feature-request.md b/.github/ISSUE_TEMPLATE/4-feature-request.md similarity index 100% rename from .github/ISSUE_TEMPLATE/5-feature-request.md rename to .github/ISSUE_TEMPLATE/4-feature-request.md diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..bca7b3f727 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,4 @@ +contact_links: + - name: Outdated FAQ + url: https://groups.google.com/forum/#!forum/osmand + about: Fix wrong or outdated FAQ on the forum instead diff --git a/.gitignore b/.gitignore index 33e746a3d6..7386d5e61e 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,8 @@ OsmAndCore_*.aar *.iml .settings .idea -.project +**/.project +**/.classpath out/ # Huawei diff --git a/OsmAnd-api/src/net/osmand/aidlapi/OsmAndCustomizationConstants.java b/OsmAnd-api/src/net/osmand/aidlapi/OsmAndCustomizationConstants.java index af48552bc4..53e838a9a7 100644 --- a/OsmAnd-api/src/net/osmand/aidlapi/OsmAndCustomizationConstants.java +++ b/OsmAnd-api/src/net/osmand/aidlapi/OsmAndCustomizationConstants.java @@ -11,6 +11,7 @@ public interface OsmAndCustomizationConstants { String DRAWER_MY_PLACES_ID = DRAWER_ITEM_ID_SCHEME + "my_places"; String DRAWER_SEARCH_ID = DRAWER_ITEM_ID_SCHEME + "search"; String DRAWER_DIRECTIONS_ID = DRAWER_ITEM_ID_SCHEME + "directions"; + String DRAWER_TRIP_RECORDING_ID = DRAWER_ITEM_ID_SCHEME + "trip_recording"; String DRAWER_CONFIGURE_MAP_ID = DRAWER_ITEM_ID_SCHEME + "configure_map"; String DRAWER_DOWNLOAD_MAPS_ID = DRAWER_ITEM_ID_SCHEME + "download_maps"; String DRAWER_OSMAND_LIVE_ID = DRAWER_ITEM_ID_SCHEME + "osmand_live"; diff --git a/OsmAnd-api/src/net/osmand/aidlapi/map/ALocation.java b/OsmAnd-api/src/net/osmand/aidlapi/map/ALocation.java index 03741138d5..0809944ba9 100644 --- a/OsmAnd-api/src/net/osmand/aidlapi/map/ALocation.java +++ b/OsmAnd-api/src/net/osmand/aidlapi/map/ALocation.java @@ -25,6 +25,24 @@ public class ALocation extends AidlParams { private ALocation() { } + public ALocation(double latitude, double longitude, long time, boolean hasAltitude, double altitude, + boolean hasSpeed, float speed, boolean hasBearing, float bearing, + boolean hasAccuracy, float accuracy, boolean hasVerticalAccuracy, float verticalAccuracy) { + this.latitude = latitude; + this.longitude = longitude; + this.time = time; + this.hasAltitude = hasAltitude; + this.altitude = altitude; + this.hasSpeed = hasSpeed; + this.speed = speed; + this.hasBearing = hasBearing; + this.bearing = bearing; + this.hasAccuracy = hasAccuracy; + this.accuracy = accuracy; + this.hasVerticalAccuracy = hasVerticalAccuracy; + this.verticalAccuracy = verticalAccuracy; + } + public ALocation(Parcel in) { readFromParcel(in); } diff --git a/OsmAnd-java/src/main/java/net/osmand/PlatformUtil.java b/OsmAnd-java/src/main/java/net/osmand/PlatformUtil.java index ffc9bf05b0..fd3fa2e2eb 100644 --- a/OsmAnd-java/src/main/java/net/osmand/PlatformUtil.java +++ b/OsmAnd-java/src/main/java/net/osmand/PlatformUtil.java @@ -23,7 +23,9 @@ public class PlatformUtil { } public static XmlPullParser newXMLPullParser() throws XmlPullParserException{ - return new org.kxml2.io.KXmlParser(); + org.kxml2.io.KXmlParser xmlParser = new org.kxml2.io.KXmlParser(); + xmlParser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + return xmlParser; } public static XmlSerializer newSerializer() { diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapAddressReaderAdapter.java b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapAddressReaderAdapter.java index 18f7b2549e..8722c96acb 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapAddressReaderAdapter.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapAddressReaderAdapter.java @@ -8,6 +8,7 @@ import gnu.trove.set.hash.TIntHashSet; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -627,7 +628,9 @@ public class BinaryMapAddressReaderAdapter { indexOffset = codedIS.getTotalBytesRead(); int oldLimit = codedIS.pushLimit(length); // here offsets are sorted by distance - map.readIndexedStringTable(stringMatcher.getCollator(), req.nameQuery, "", loffsets, 0); + TIntArrayList charsList = new TIntArrayList(); + charsList.add(0); + map.readIndexedStringTable(stringMatcher.getCollator(), Collections.singletonList(req.nameQuery), "", Collections.singletonList(loffsets), charsList); codedIS.popLimit(oldLimit); break; case OsmAndAddressNameIndexData.ATOM_FIELD_NUMBER: diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapIndexReader.java b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapIndexReader.java index 8743940a74..34483b01b7 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapIndexReader.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapIndexReader.java @@ -83,7 +83,7 @@ public class BinaryMapIndexReader { public final static int TRANSPORT_STOP_ZOOM = 24; public static final int SHIFT_COORDINATES = 5; - public static final int LABEL_ZOOM_ENCODE = 26; + public static final int LABEL_ZOOM_ENCODE = 31 - SHIFT_COORDINATES; private final static Log log = PlatformUtil.getLog(BinaryMapIndexReader.class); public static boolean READ_STATS = false; public static final SearchPoiTypeFilter ACCEPT_ALL_POI_TYPE_FILTER = new SearchPoiTypeFilter() { @@ -2161,9 +2161,9 @@ public class BinaryMapIndexReader { private static boolean testAddressSearch = false; private static boolean testAddressSearchName = false; private static boolean testAddressJustifySearch = false; - private static boolean testPoiSearch = false; + private static boolean testPoiSearch = true; private static boolean testPoiSearchOnPath = false; - private static boolean testTransportSearch = true; + private static boolean testTransportSearch = false; private static int sleft = MapUtils.get31TileNumberX(27.55079); private static int sright = MapUtils.get31TileNumberX(27.55317); @@ -2177,7 +2177,7 @@ public class BinaryMapIndexReader { public static void main(String[] args) throws IOException { File fl = new File(System.getProperty("maps") + "/Synthetic_test_rendering.obf"); - fl = new File("/home/madwasp79/OsmAnd-maps/Poly_center2.obf"); + fl = new File(System.getProperty("maps") +"/Wikivoyage.obf__"); RandomAccessFile raf = new RandomAccessFile(fl, "r"); @@ -2325,7 +2325,7 @@ public class BinaryMapIndexReader { private static void testPoiSearchByName(BinaryMapIndexReader reader) throws IOException { println("Searching by name..."); - SearchRequest req = buildSearchPoiRequest(0, 0, "Art", + SearchRequest req = buildSearchPoiRequest(0, 0, "central ukraine", 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, null); reader.searchPoiByName(req); @@ -2385,54 +2385,72 @@ public class BinaryMapIndexReader { } - int readIndexedStringTable(Collator instance, String query, String prefix, TIntArrayList list, int charMatches) throws IOException { + void readIndexedStringTable(Collator instance, List queries, String prefix, List listOffsets, TIntArrayList matchedCharacters) throws IOException { String key = null; + boolean[] matched = new boolean[matchedCharacters.size()]; + boolean shouldWeReadSubtable = false; while (true) { int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: - return charMatches; + return; case OsmandOdb.IndexedStringTable.KEY_FIELD_NUMBER : key = codedIS.readString(); - if(prefix.length() > 0){ + if (prefix.length() > 0) { key = prefix + key; } - // check query is part of key (the best matching) - if(CollatorStringMatcher.cmatches(instance, key, query, StringMatcherMode.CHECK_ONLY_STARTS_WITH)){ - if(query.length() >= charMatches){ - if(query.length() > charMatches){ - charMatches = query.length(); - list.clear(); - } - } else { - key = null; + shouldWeReadSubtable = false; + for (int i = 0; i < queries.size(); i++) { + int charMatches = matchedCharacters.get(i); + String query = queries.get(i); + matched[i] = false; + if (query == null) { + continue; } - // check key is part of query - } else if (CollatorStringMatcher.cmatches(instance, query, key, StringMatcherMode.CHECK_ONLY_STARTS_WITH)) { - if (key.length() >= charMatches) { - if (key.length() > charMatches) { - charMatches = key.length(); - list.clear(); + + // check query is part of key (the best matching) + if (CollatorStringMatcher.cmatches(instance, key, query, StringMatcherMode.CHECK_ONLY_STARTS_WITH)) { + if (query.length() >= charMatches) { + if (query.length() > charMatches) { + matchedCharacters.set(i, query.length()); + listOffsets.get(i).clear(); + } + matched[i] = true; + } + // check key is part of query + } else if (CollatorStringMatcher.cmatches(instance, query, key, StringMatcherMode.CHECK_ONLY_STARTS_WITH)) { + if (key.length() >= charMatches) { + if (key.length() > charMatches) { + matchedCharacters.set(i, key.length()); + listOffsets.get(i).clear(); + } + matched[i] = true; } - } else { - key = null; } - } else { - key = null; + shouldWeReadSubtable |= matched[i]; } break; case OsmandOdb.IndexedStringTable.VAL_FIELD_NUMBER : int val = readInt(); - if (key != null) { - list.add(val); + for (int i = 0; i < queries.size(); i++) { + if (matched[i]) { + listOffsets.get(i).add(val); + } } break; case OsmandOdb.IndexedStringTable.SUBTABLES_FIELD_NUMBER : int len = codedIS.readRawVarint32(); int oldLim = codedIS.pushLimit(len); - if (key != null) { - charMatches = readIndexedStringTable(instance, query, key, list, charMatches); + if (shouldWeReadSubtable && key != null) { + List subqueries = new ArrayList<>(queries); + // reset query so we don't search what was not matched + for(int i = 0; i < queries.size(); i++) { + if(!matched[i]) { + subqueries.set(i, null); + } + } + readIndexedStringTable(instance, subqueries, key, listOffsets, matchedCharacters); } else { codedIS.skipRawBytes(codedIS.getBytesUntilLimit()); } diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapPoiReaderAdapter.java b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapPoiReaderAdapter.java index cb348cf9b4..742eae1f61 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapPoiReaderAdapter.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapPoiReaderAdapter.java @@ -1,10 +1,6 @@ package net.osmand.binary; -import gnu.trove.list.array.TIntArrayList; -import gnu.trove.map.hash.TIntLongHashMap; -import gnu.trove.set.hash.TLongHashSet; - import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -12,6 +8,14 @@ import java.util.Comparator; import java.util.LinkedList; import java.util.List; +import org.apache.commons.logging.Log; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.WireFormat; + +import gnu.trove.list.array.TIntArrayList; +import gnu.trove.map.hash.TIntLongHashMap; +import gnu.trove.set.hash.TLongHashSet; import net.osmand.Collator; import net.osmand.CollatorStringMatcher; import net.osmand.CollatorStringMatcher.StringMatcherMode; @@ -26,11 +30,6 @@ import net.osmand.osm.MapPoiTypes; import net.osmand.osm.PoiCategory; import net.osmand.util.MapUtils; -import org.apache.commons.logging.Log; - -import com.google.protobuf.CodedInputStream; -import com.google.protobuf.WireFormat; - public class BinaryMapPoiReaderAdapter { private static final Log LOG = PlatformUtil.getLog(BinaryMapPoiReaderAdapter.class); @@ -38,7 +37,12 @@ public class BinaryMapPoiReaderAdapter { private static final int CATEGORY_MASK = (1 << SHIFT_BITS_CATEGORY) - 1; private static final int ZOOM_TO_SKIP_FILTER_READ = 6; private static final int ZOOM_TO_SKIP_FILTER = 3; - private static final int BUCKET_SEARCH_BY_NAME = 5; + private static final int BUCKET_SEARCH_BY_NAME = 15; // should be bigger 100? + private static final int BASE_POI_SHIFT = SHIFT_BITS_CATEGORY;// 7 + private static final int FINAL_POI_SHIFT = BinaryMapIndexReader.SHIFT_COORDINATES;// 5 + private static final int BASE_POI_ZOOM = 31 - BASE_POI_SHIFT;// 24 zoom + private static final int FINAL_POI_ZOOM = 31 - FINAL_POI_SHIFT;// 26 zoom + public static class PoiSubType { public boolean text; @@ -332,7 +336,7 @@ public class BinaryMapPoiReaderAdapter { }); int p = BUCKET_SEARCH_BY_NAME * 3; if (p < offKeys.length) { - for (int i = p + BUCKET_SEARCH_BY_NAME; ; i += BUCKET_SEARCH_BY_NAME) { + for (int i = p + BUCKET_SEARCH_BY_NAME;; i += BUCKET_SEARCH_BY_NAME) { if (i > offKeys.length) { Arrays.sort(offKeys, p, offKeys.length); break; @@ -344,7 +348,6 @@ public class BinaryMapPoiReaderAdapter { } } - LOG.info("Searched poi structure in " + (System.currentTimeMillis() - time) + "ms. Found " + offKeys.length + " subtrees"); for (int j = 0; j < offKeys.length; j++) { @@ -370,7 +373,8 @@ public class BinaryMapPoiReaderAdapter { private TIntLongHashMap readPoiNameIndex(Collator instance, String query, SearchRequest req) throws IOException { TIntLongHashMap offsets = new TIntLongHashMap(); - TIntArrayList dataOffsets = null; + List listOffsets = null; + List listOfSepOffsets = new ArrayList(); int offset = 0; while (true) { int t = codedIS.readTag(); @@ -381,24 +385,51 @@ public class BinaryMapPoiReaderAdapter { case OsmandOdb.OsmAndPoiNameIndex.TABLE_FIELD_NUMBER: { int length = readInt(); int oldLimit = codedIS.pushLimit(length); - dataOffsets = new TIntArrayList(); offset = codedIS.getTotalBytesRead(); - map.readIndexedStringTable(instance, query, "", dataOffsets, 0); + List queries = new ArrayList<>(); + for (String word : query.split(" ")) { + if (word.trim().length() > 0) { + queries.add(word.trim()); + } + } + TIntArrayList charsList = new TIntArrayList(queries.size()); + listOffsets = new ArrayList(queries.size()); + while(listOffsets.size() < queries.size()) { + charsList.add(0); + listOffsets.add(new TIntArrayList()); + } + map.readIndexedStringTable(instance, queries, "", listOffsets, charsList); codedIS.popLimit(oldLimit); break; } case OsmandOdb.OsmAndPoiNameIndex.DATA_FIELD_NUMBER: { - if (dataOffsets != null) { - dataOffsets.sort(); // 1104125 - for (int i = 0; i < dataOffsets.size(); i++) { - codedIS.seek(dataOffsets.get(i) + offset); - int len = codedIS.readRawVarint32(); - int oldLim = codedIS.pushLimit(len); - readPoiNameIndexData(offsets, req); - codedIS.popLimit(oldLim); - if (req.isCancelled()) { - codedIS.skipRawBytes(codedIS.getBytesUntilLimit()); - return offsets; + if (listOffsets != null) { + for (TIntArrayList dataOffsets : listOffsets) { + TIntLongHashMap offsetMap = new TIntLongHashMap(); + listOfSepOffsets.add(offsetMap); + dataOffsets.sort(); // 1104125 + for (int i = 0; i < dataOffsets.size(); i++) { + codedIS.seek(dataOffsets.get(i) + offset); + int len = codedIS.readRawVarint32(); + int oldLim = codedIS.pushLimit(len); + readPoiNameIndexData(offsetMap, req); + codedIS.popLimit(oldLim); + if (req.isCancelled()) { + codedIS.skipRawBytes(codedIS.getBytesUntilLimit()); + return offsets; + } + } + } + } + if (listOfSepOffsets.size() > 0) { + offsets.putAll(listOfSepOffsets.get(0)); + for (int j = 1; j < listOfSepOffsets.size(); j++) { + TIntLongHashMap mp = listOfSepOffsets.get(j); + // offsets.retainAll(mp); -- calculate intresection of mp & offsets + for (int chKey : offsets.keys()) { + if (!mp.containsKey(chKey)) { + offsets.remove(chKey); + } } } } @@ -688,6 +719,8 @@ public class BinaryMapPoiReaderAdapter { Amenity am = null; int x = 0; int y = 0; + int precisionXY = 0; + boolean hasLocation = false; StringBuilder retValue = new StringBuilder(); PoiCategory amenityType = null; LinkedList textTags = null; @@ -714,12 +747,22 @@ public class BinaryMapPoiReaderAdapter { am.setRoutePoint(arp); } } + if (hasLocation) { + if (precisionXY != 0) { + int[] xy = MapUtils.calculateFinalXYFromBaseAndPrecisionXY(BASE_POI_ZOOM, FINAL_POI_ZOOM, precisionXY, x >> BASE_POI_SHIFT, y >> BASE_POI_SHIFT, true); + int x31 = xy[0] << FINAL_POI_SHIFT; + int y31 = xy[1] << FINAL_POI_SHIFT; + am.setLocation(MapUtils.get31LatitudeY(y31), MapUtils.get31LongitudeX(x31)); + } else { + am.setLocation(MapUtils.get31LatitudeY(y), MapUtils.get31LongitudeX(x)); + } + } return am; case OsmandOdb.OsmAndPoiBoxDataAtom.DX_FIELD_NUMBER: - x = (codedIS.readSInt32() + (px << (24 - zoom))) << 7; + x = (codedIS.readSInt32() + (px << (BASE_POI_ZOOM - zoom))) << BASE_POI_SHIFT; break; case OsmandOdb.OsmAndPoiBoxDataAtom.DY_FIELD_NUMBER: - y = (codedIS.readSInt32() + (py << (24 - zoom))) << 7; + y = (codedIS.readSInt32() + (py << (BASE_POI_ZOOM - zoom))) << BASE_POI_SHIFT; req.numberOfVisitedObjects++; if (checkBounds) { if (left31 > x || right31 < x || top31 > y || bottom31 < y) { @@ -728,7 +771,8 @@ public class BinaryMapPoiReaderAdapter { } } am = new Amenity(); - am.setLocation(MapUtils.get31LatitudeY(y), MapUtils.get31LongitudeX(x)); + hasLocation = true; + //am.setLocation(MapUtils.get31LatitudeY(y), MapUtils.get31LongitudeX(x)); // set precise coordinates break; case OsmandOdb.OsmAndPoiBoxDataAtom.SUBCATEGORIES_FIELD_NUMBER: int subtypev = codedIS.readUInt32(); @@ -801,6 +845,11 @@ public class BinaryMapPoiReaderAdapter { case OsmandOdb.OsmAndPoiBoxDataAtom.NOTE_FIELD_NUMBER: am.setDescription(codedIS.readString()); break; + case OsmandOdb.OsmAndPoiBoxDataAtom.PRECISIONXY_FIELD_NUMBER: + if (hasLocation) { + precisionXY = codedIS.readInt32(); + } + break; default: skipUnknownField(t); break; diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/OsmandOdb.java b/OsmAnd-java/src/main/java/net/osmand/binary/OsmandOdb.java index 97a2abeb91..f531dfbfde 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/OsmandOdb.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/OsmandOdb.java @@ -54371,6 +54371,24 @@ public final class OsmandOdb { */ com.google.protobuf.ByteString getTextValuesBytes(int index); + + // optional int32 precisionXY = 16; + /** + * optional int32 precisionXY = 16; + * + *
+     * precision in 1-xy-xy-xy binary format
+     * 
+ */ + boolean hasPrecisionXY(); + /** + * optional int32 precisionXY = 16; + * + *
+     * precision in 1-xy-xy-xy binary format
+     * 
+ */ + int getPrecisionXY(); } /** * Protobuf type {@code OsmAnd.OBF.OsmAndPoiBoxDataAtom} @@ -54539,6 +54557,11 @@ public final class OsmandOdb { textValues_.add(input.readBytes()); break; } + case 128: { + bitField0_ |= 0x00000200; + precisionXY_ = input.readInt32(); + break; + } } } } catch (com.google.protobuf.InvalidProtocolBufferException e) { @@ -55048,6 +55071,30 @@ public final class OsmandOdb { return textValues_.getByteString(index); } + // optional int32 precisionXY = 16; + public static final int PRECISIONXY_FIELD_NUMBER = 16; + private int precisionXY_; + /** + * optional int32 precisionXY = 16; + * + *
+     * precision in 1-xy-xy-xy binary format
+     * 
+ */ + public boolean hasPrecisionXY() { + return ((bitField0_ & 0x00000200) == 0x00000200); + } + /** + * optional int32 precisionXY = 16; + * + *
+     * precision in 1-xy-xy-xy binary format
+     * 
+ */ + public int getPrecisionXY() { + return precisionXY_; + } + private void initFields() { dx_ = 0; dy_ = 0; @@ -55062,6 +55109,7 @@ public final class OsmandOdb { note_ = ""; textCategories_ = java.util.Collections.emptyList(); textValues_ = com.google.protobuf.LazyStringArrayList.EMPTY; + precisionXY_ = 0; } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { @@ -55122,6 +55170,9 @@ public final class OsmandOdb { for (int i = 0; i < textValues_.size(); i++) { output.writeBytes(15, textValues_.getByteString(i)); } + if (((bitField0_ & 0x00000200) == 0x00000200)) { + output.writeInt32(16, precisionXY_); + } getUnknownFields().writeTo(output); } @@ -55203,6 +55254,10 @@ public final class OsmandOdb { size += dataSize; size += 1 * getTextValuesList().size(); } + if (((bitField0_ & 0x00000200) == 0x00000200)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(16, precisionXY_); + } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -55345,6 +55400,8 @@ public final class OsmandOdb { bitField0_ = (bitField0_ & ~0x00000800); textValues_ = com.google.protobuf.LazyStringArrayList.EMPTY; bitField0_ = (bitField0_ & ~0x00001000); + precisionXY_ = 0; + bitField0_ = (bitField0_ & ~0x00002000); return this; } @@ -55430,6 +55487,10 @@ public final class OsmandOdb { bitField0_ = (bitField0_ & ~0x00001000); } result.textValues_ = textValues_; + if (((from_bitField0_ & 0x00002000) == 0x00002000)) { + to_bitField0_ |= 0x00000200; + } + result.precisionXY_ = precisionXY_; result.bitField0_ = to_bitField0_; onBuilt(); return result; @@ -55525,6 +55586,9 @@ public final class OsmandOdb { } onChanged(); } + if (other.hasPrecisionXY()) { + setPrecisionXY(other.getPrecisionXY()); + } this.mergeUnknownFields(other.getUnknownFields()); return this; } @@ -56506,6 +56570,55 @@ public final class OsmandOdb { return this; } + // optional int32 precisionXY = 16; + private int precisionXY_ ; + /** + * optional int32 precisionXY = 16; + * + *
+       * precision in 1-xy-xy-xy binary format
+       * 
+ */ + public boolean hasPrecisionXY() { + return ((bitField0_ & 0x00002000) == 0x00002000); + } + /** + * optional int32 precisionXY = 16; + * + *
+       * precision in 1-xy-xy-xy binary format
+       * 
+ */ + public int getPrecisionXY() { + return precisionXY_; + } + /** + * optional int32 precisionXY = 16; + * + *
+       * precision in 1-xy-xy-xy binary format
+       * 
+ */ + public Builder setPrecisionXY(int value) { + bitField0_ |= 0x00002000; + precisionXY_ = value; + onChanged(); + return this; + } + /** + * optional int32 precisionXY = 16; + * + *
+       * precision in 1-xy-xy-xy binary format
+       * 
+ */ + public Builder clearPrecisionXY() { + bitField0_ = (bitField0_ & ~0x00002000); + precisionXY_ = 0; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:OsmAnd.OBF.OsmAndPoiBoxDataAtom) } @@ -65008,37 +65121,38 @@ public final class OsmandOdb { "tegories\030\003 \003(\r\022\025\n\rsubcategories\030\005 \003(\r\"i\n" + "\020OsmAndPoiBoxData\022\014\n\004zoom\030\001 \001(\r\022\t\n\001x\030\002 \001" + "(\r\022\t\n\001y\030\003 \001(\r\0221\n\007poiData\030\005 \003(\0132 .OsmAnd." + - "OBF.OsmAndPoiBoxDataAtom\"\360\001\n\024OsmAndPoiBo", + "OBF.OsmAndPoiBoxDataAtom\"\205\002\n\024OsmAndPoiBo", "xDataAtom\022\n\n\002dx\030\002 \002(\021\022\n\n\002dy\030\003 \002(\021\022\022\n\ncat" + "egories\030\004 \003(\r\022\025\n\rsubcategories\030\005 \003(\r\022\014\n\004" + "name\030\006 \001(\t\022\016\n\006nameEn\030\007 \001(\t\022\n\n\002id\030\010 \001(\004\022\024" + "\n\014openingHours\030\n \001(\t\022\014\n\004site\030\013 \001(\t\022\r\n\005ph" + "one\030\014 \001(\t\022\014\n\004note\030\r \001(\t\022\026\n\016textCategorie" + - "s\030\016 \003(\r\022\022\n\ntextValues\030\017 \003(\t\"\032\n\007IdTable\022\017" + - "\n\007routeId\030\001 \003(\022\"F\n\017RestrictionData\022\014\n\004ty" + - "pe\030\001 \002(\005\022\014\n\004from\030\002 \002(\005\022\n\n\002to\030\003 \002(\005\022\013\n\003vi" + - "a\030\004 \001(\005\"x\n\tRouteData\022\016\n\006points\030\001 \002(\014\022\022\n\n" + - "pointTypes\030\004 \001(\014\022\022\n\npointNames\030\005 \001(\014\022\r\n\005", - "types\030\007 \002(\014\022\017\n\007routeId\030\014 \002(\005\022\023\n\013stringNa" + - "mes\030\016 \001(\014\"\304\005\n\022OsmAndRoutingIndex\022\014\n\004name" + - "\030\001 \002(\t\022?\n\005rules\030\002 \003(\01320.OsmAnd.OBF.OsmAn" + - "dRoutingIndex.RouteEncodingRule\022>\n\trootB" + - "oxes\030\003 \003(\0132+.OsmAnd.OBF.OsmAndRoutingInd" + - "ex.RouteDataBox\022A\n\014basemapBoxes\030\004 \003(\0132+." + - "OsmAnd.OBF.OsmAndRoutingIndex.RouteDataB" + - "ox\022=\n\006blocks\030\005 \003(\0132-.OsmAnd.OBF.OsmAndRo" + - "utingIndex.RouteDataBlock\032;\n\021RouteEncodi" + - "ngRule\022\013\n\003tag\030\003 \002(\t\022\r\n\005value\030\005 \002(\t\022\n\n\002id", - "\030\007 \001(\r\032\231\001\n\014RouteDataBox\022\014\n\004left\030\001 \002(\021\022\r\n" + - "\005right\030\002 \002(\021\022\013\n\003top\030\003 \002(\021\022\016\n\006bottom\030\004 \002(" + - "\021\022\023\n\013shiftToData\030\005 \001(\007\022:\n\005boxes\030\007 \003(\0132+." + - "OsmAnd.OBF.OsmAndRoutingIndex.RouteDataB" + - "ox\032\303\001\n\016RouteDataBlock\022$\n\007idTable\030\005 \001(\0132\023" + - ".OsmAnd.OBF.IdTable\022*\n\013dataObjects\030\006 \003(\013" + - "2\025.OsmAnd.OBF.RouteData\0221\n\014restrictions\030" + - "\007 \003(\0132\033.OsmAnd.OBF.RestrictionData\022,\n\013st" + - "ringTable\030\010 \001(\0132\027.OsmAnd.OBF.StringTable" + - "B\036\n\021net.osmand.binaryB\tOsmandOdb" + "s\030\016 \003(\r\022\022\n\ntextValues\030\017 \003(\t\022\023\n\013precision" + + "XY\030\020 \001(\005\"\032\n\007IdTable\022\017\n\007routeId\030\001 \003(\022\"F\n\017" + + "RestrictionData\022\014\n\004type\030\001 \002(\005\022\014\n\004from\030\002 " + + "\002(\005\022\n\n\002to\030\003 \002(\005\022\013\n\003via\030\004 \001(\005\"x\n\tRouteDat" + + "a\022\016\n\006points\030\001 \002(\014\022\022\n\npointTypes\030\004 \001(\014\022\022\n", + "\npointNames\030\005 \001(\014\022\r\n\005types\030\007 \002(\014\022\017\n\007rout" + + "eId\030\014 \002(\005\022\023\n\013stringNames\030\016 \001(\014\"\304\005\n\022OsmAn" + + "dRoutingIndex\022\014\n\004name\030\001 \002(\t\022?\n\005rules\030\002 \003" + + "(\01320.OsmAnd.OBF.OsmAndRoutingIndex.Route" + + "EncodingRule\022>\n\trootBoxes\030\003 \003(\0132+.OsmAnd" + + ".OBF.OsmAndRoutingIndex.RouteDataBox\022A\n\014" + + "basemapBoxes\030\004 \003(\0132+.OsmAnd.OBF.OsmAndRo" + + "utingIndex.RouteDataBox\022=\n\006blocks\030\005 \003(\0132" + + "-.OsmAnd.OBF.OsmAndRoutingIndex.RouteDat" + + "aBlock\032;\n\021RouteEncodingRule\022\013\n\003tag\030\003 \002(\t", + "\022\r\n\005value\030\005 \002(\t\022\n\n\002id\030\007 \001(\r\032\231\001\n\014RouteDat" + + "aBox\022\014\n\004left\030\001 \002(\021\022\r\n\005right\030\002 \002(\021\022\013\n\003top" + + "\030\003 \002(\021\022\016\n\006bottom\030\004 \002(\021\022\023\n\013shiftToData\030\005 " + + "\001(\007\022:\n\005boxes\030\007 \003(\0132+.OsmAnd.OBF.OsmAndRo" + + "utingIndex.RouteDataBox\032\303\001\n\016RouteDataBlo" + + "ck\022$\n\007idTable\030\005 \001(\0132\023.OsmAnd.OBF.IdTable" + + "\022*\n\013dataObjects\030\006 \003(\0132\025.OsmAnd.OBF.Route" + + "Data\0221\n\014restrictions\030\007 \003(\0132\033.OsmAnd.OBF." + + "RestrictionData\022,\n\013stringTable\030\010 \001(\0132\027.O" + + "smAnd.OBF.StringTableB\036\n\021net.osmand.bina", + "ryB\tOsmandOdb" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -65296,7 +65410,7 @@ public final class OsmandOdb { internal_static_OsmAnd_OBF_OsmAndPoiBoxDataAtom_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_OsmAndPoiBoxDataAtom_descriptor, - new java.lang.String[] { "Dx", "Dy", "Categories", "Subcategories", "Name", "NameEn", "Id", "OpeningHours", "Site", "Phone", "Note", "TextCategories", "TextValues", }); + new java.lang.String[] { "Dx", "Dy", "Categories", "Subcategories", "Name", "NameEn", "Id", "OpeningHours", "Site", "Phone", "Note", "TextCategories", "TextValues", "PrecisionXY", }); internal_static_OsmAnd_OBF_IdTable_descriptor = getDescriptor().getMessageTypes().get(36); internal_static_OsmAnd_OBF_IdTable_fieldAccessorTable = new diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/RouteDataObject.java b/OsmAnd-java/src/main/java/net/osmand/binary/RouteDataObject.java index bc81ca667e..f444a56b04 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/RouteDataObject.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/RouteDataObject.java @@ -4,6 +4,7 @@ import net.osmand.Location; import net.osmand.PlatformUtil; import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion; import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule; +import net.osmand.data.LatLon; import net.osmand.util.Algorithms; import net.osmand.util.MapUtils; import net.osmand.util.TransliterationHelper; @@ -37,7 +38,9 @@ public class RouteDataObject { public int[] nameIds; // mixed array [0, height, cumulative_distance height, cumulative_distance, height, ...] - length is length(points)*2 public float[] heightDistanceArray = null; + public float heightByCurrentLocation; private static final Log LOG = PlatformUtil.getLog(RouteDataObject.class); + public RouteDataObject(RouteRegion region) { this.region = region; } @@ -124,7 +127,7 @@ public class RouteDataObject { equals = this.pointTypes[i] == thatObj.pointTypes[i]; } else if (pointTypes[i].length != thatObj.pointTypes[i].length) { equals = false; - } else { + } else { for (int j = 0; j < this.pointTypes[i].length && equals; j++) { String thisTag = region.routeEncodingRules.get(pointTypes[i][j]).getTag(); String thisValue = region.routeEncodingRules.get(pointTypes[i][j]).getValue(); @@ -147,7 +150,7 @@ public class RouteDataObject { equals = this.pointNameTypes[i] == thatObj.pointNameTypes[i]; } else if (pointNameTypes[i].length != thatObj.pointNameTypes[i].length) { equals = false; - } else { + } else { for (int j = 0; j < this.pointNameTypes[i].length && equals; j++) { String thisTag = region.routeEncodingRules.get(pointNameTypes[i][j]).getTag(); String thisValue = pointNames[i][j]; @@ -165,53 +168,67 @@ public class RouteDataObject { } public float[] calculateHeightArray() { - if(heightDistanceArray != null) { + return calculateHeightArray(null); + } + + public float[] calculateHeightArray(LatLon currentLocation) { + if (heightDistanceArray != null) { return heightDistanceArray; } int startHeight = Algorithms.parseIntSilently(getValue("osmand_ele_start"), HEIGHT_UNDEFINED); int endHeight = Algorithms.parseIntSilently(getValue("osmand_ele_end"), startHeight); - if(startHeight == HEIGHT_UNDEFINED) { + if (startHeight == HEIGHT_UNDEFINED) { heightDistanceArray = new float[0]; return heightDistanceArray; } - heightDistanceArray = new float[2*getPointsLength()]; + heightDistanceArray = new float[2 * getPointsLength()]; double plon = 0; double plat = 0; - float prevHeight = startHeight; - for(int k = 0; k < getPointsLength(); k++) { + float prevHeight = heightByCurrentLocation = startHeight; + double prevDistance = 0; + for (int k = 0; k < getPointsLength(); k++) { double lon = MapUtils.get31LongitudeX(getPoint31XTile(k)); double lat = MapUtils.get31LatitudeY(getPoint31YTile(k)); - if(k > 0) { + if (k > 0) { double dd = MapUtils.getDistance(plat, plon, lat, lon); float height = HEIGHT_UNDEFINED; - if(k == getPointsLength() - 1) { + if (k == getPointsLength() - 1) { height = endHeight; } else { String asc = getValue(k, "osmand_ele_asc"); - if(asc != null && asc.length() > 0) { + if (asc != null && asc.length() > 0) { height = (prevHeight + Float.parseFloat(asc)); } else { String desc = getValue(k, "osmand_ele_desc"); - if(desc != null && desc.length() > 0) { + if (desc != null && desc.length() > 0) { height = (prevHeight - Float.parseFloat(desc)); } } } - heightDistanceArray[2*k] = (float) dd; - heightDistanceArray[2*k+1] = height; - if(height != HEIGHT_UNDEFINED) { + heightDistanceArray[2 * k] = (float) dd; + heightDistanceArray[2 * k + 1] = height; + + if (currentLocation != null) { + double distance = MapUtils.getDistance(currentLocation, lat, lon); + if (height != HEIGHT_UNDEFINED && distance < prevDistance) { + prevDistance = distance; + heightByCurrentLocation = height; + } + } + + if (height != HEIGHT_UNDEFINED) { // interpolate undefined double totalDistance = dd; int startUndefined = k; - while(startUndefined - 1 >= 0 && heightDistanceArray[2*(startUndefined - 1)+1] == HEIGHT_UNDEFINED) { - startUndefined --; - totalDistance += heightDistanceArray[2*(startUndefined)]; + while (startUndefined - 1 >= 0 && heightDistanceArray[2 * (startUndefined - 1) + 1] == HEIGHT_UNDEFINED) { + startUndefined--; + totalDistance += heightDistanceArray[2 * (startUndefined)]; } - if(totalDistance > 0) { + if (totalDistance > 0) { double angle = (height - prevHeight) / totalDistance; - for(int j = startUndefined; j < k; j++) { - heightDistanceArray[2*j+1] = (float) ((heightDistanceArray[2*j] * angle) + heightDistanceArray[2*j-1]); + for (int j = startUndefined; j < k; j++) { + heightDistanceArray[2 * j + 1] = (float) ((heightDistanceArray[2 * j] * angle) + heightDistanceArray[2 * j - 1]); } } prevHeight = height; @@ -223,6 +240,9 @@ public class RouteDataObject { } plat = lat; plon = lon; + if (currentLocation != null) { + prevDistance = MapUtils.getDistance(currentLocation, plat, plon); + } } return heightDistanceArray; } @@ -231,34 +251,34 @@ public class RouteDataObject { return id; } - public String getName(){ - if(names != null ) { + public String getName() { + if (names != null) { return names.get(region.nameTypeRule); } return null; } - public String getName(String lang){ + public String getName(String lang) { return getName(lang, false); } - public String getName(String lang, boolean transliterate){ - if(names != null ) { - if(Algorithms.isEmpty(lang)) { + public String getName(String lang, boolean transliterate) { + if (names != null) { + if (Algorithms.isEmpty(lang)) { return names.get(region.nameTypeRule); } int[] kt = names.keys(); - for(int i = 0 ; i < kt.length; i++) { + for (int i = 0; i < kt.length; i++) { int k = kt[i]; - if(region.routeEncodingRules.size() > k) { - if(("name:"+lang).equals(region.routeEncodingRules.get(k).getTag())) { + if (region.routeEncodingRules.size() > k) { + if (("name:" + lang).equals(region.routeEncodingRules.get(k).getTag())) { return names.get(k); } } } String nmDef = names.get(region.nameTypeRule); - if(transliterate && nmDef != null && nmDef.length() > 0) { + if (transliterate && nmDef != null && nmDef.length() > 0) { return TransliterationHelper.transliterate(nmDef); } return nmDef; @@ -279,20 +299,20 @@ public class RouteDataObject { // return getDestinationRef(direction); //} if (names != null) { - if(Algorithms.isEmpty(lang)) { + if (Algorithms.isEmpty(lang)) { return names.get(region.refTypeRule); } int[] kt = names.keys(); - for(int i = 0 ; i < kt.length; i++) { + for (int i = 0; i < kt.length; i++) { int k = kt[i]; - if(region.routeEncodingRules.size() > k) { - if(("ref:"+lang).equals(region.routeEncodingRules.get(k).getTag())) { + if (region.routeEncodingRules.size() > k) { + if (("ref:" + lang).equals(region.routeEncodingRules.get(k).getTag())) { return names.get(k); } } } String refDefault = names.get(region.refTypeRule); - if(transliterate && refDefault != null && refDefault.length() > 0) { + if (transliterate && refDefault != null && refDefault.length() > 0) { return TransliterationHelper.transliterate(refDefault); } return refDefault; @@ -307,13 +327,13 @@ public class RouteDataObject { String refTagDefault = "destination:ref"; String refDefault = null; - for(int i = 0 ; i < kt.length; i++) { + for (int i = 0; i < kt.length; i++) { int k = kt[i]; - if(region.routeEncodingRules.size() > k) { - if(refTag.equals(region.routeEncodingRules.get(k).getTag())) { + if (region.routeEncodingRules.size() > k) { + if (refTag.equals(region.routeEncodingRules.get(k).getTag())) { return names.get(k); } - if(refTagDefault.equals(region.routeEncodingRules.get(k).getTag())) { + if (refTagDefault.equals(region.routeEncodingRules.get(k).getTag())) { refDefault = names.get(k); } } @@ -326,12 +346,12 @@ public class RouteDataObject { return null; } - public String getDestinationName(String lang, boolean transliterate, boolean direction){ + public String getDestinationName(String lang, boolean transliterate, boolean direction) { //Issue #3289: Treat destination:ref like a destination, not like a ref String destRef = ((getDestinationRef(direction) == null) || getDestinationRef(direction).equals(getRef(lang, transliterate, direction))) ? "" : getDestinationRef(direction); String destRef1 = Algorithms.isEmpty(destRef) ? "" : destRef + ", "; - if(names != null) { + if (names != null) { int[] kt = names.keys(); // Issue #3181: Parse destination keys in this order: @@ -341,35 +361,35 @@ public class RouteDataObject { // destination String destinationTagLangFB = "destination:lang:XX"; - if(!Algorithms.isEmpty(lang)) { + if (!Algorithms.isEmpty(lang)) { destinationTagLangFB = (direction == true) ? "destination:lang:" + lang + ":forward" : "destination:lang:" + lang + ":backward"; } String destinationTagFB = (direction == true) ? "destination:forward" : "destination:backward"; String destinationTagLang = "destination:lang:XX"; - if(!Algorithms.isEmpty(lang)) { + if (!Algorithms.isEmpty(lang)) { destinationTagLang = "destination:lang:" + lang; } String destinationTagDefault = "destination"; String destinationDefault = null; - for(int i = 0 ; i < kt.length; i++) { + for (int i = 0; i < kt.length; i++) { int k = kt[i]; - if(region.routeEncodingRules.size() > k) { - if(!Algorithms.isEmpty(lang) && destinationTagLangFB.equals(region.routeEncodingRules.get(k).getTag())) { + if (region.routeEncodingRules.size() > k) { + if (!Algorithms.isEmpty(lang) && destinationTagLangFB.equals(region.routeEncodingRules.get(k).getTag())) { return destRef1 + ((transliterate) ? TransliterationHelper.transliterate(names.get(k)) : names.get(k)); } - if(destinationTagFB.equals(region.routeEncodingRules.get(k).getTag())) { + if (destinationTagFB.equals(region.routeEncodingRules.get(k).getTag())) { return destRef1 + ((transliterate) ? TransliterationHelper.transliterate(names.get(k)) : names.get(k)); } - if(!Algorithms.isEmpty(lang) && destinationTagLang.equals(region.routeEncodingRules.get(k).getTag())) { + if (!Algorithms.isEmpty(lang) && destinationTagLang.equals(region.routeEncodingRules.get(k).getTag())) { return destRef1 + ((transliterate) ? TransliterationHelper.transliterate(names.get(k)) : names.get(k)); } - if(destinationTagDefault.equals(region.routeEncodingRules.get(k).getTag())) { + if (destinationTagDefault.equals(region.routeEncodingRules.get(k).getTag())) { destinationDefault = names.get(k); } } } - if(destinationDefault != null) { + if (destinationDefault != null) { return destRef1 + ((transliterate) ? TransliterationHelper.transliterate(destinationDefault) : destinationDefault); } } @@ -400,14 +420,14 @@ public class RouteDataObject { RestrictionInfo ri = new RestrictionInfo(); ri.toWay = getRestrictionId(k); ri.type = getRestrictionType(k); - if(restrictionsVia != null && k < restrictionsVia.length) { + if (restrictionsVia != null && k < restrictionsVia.length) { ri.viaWay = restrictionsVia[k]; } return ri; } public long getRestrictionVia(int i) { - if(restrictionsVia != null && restrictionsVia.length > i) { + if (restrictionsVia != null && restrictionsVia.length > i) { return restrictionsVia[i]; } return 0; @@ -441,7 +461,7 @@ public class RouteDataObject { } if (insNames) { pointNames = new String[opointNames.length + 1][]; - pointNameTypes = new int[opointNameTypes.length +1][]; + pointNameTypes = new int[opointNameTypes.length + 1][]; } int i = 0; for (; i < pos; i++) { @@ -590,7 +610,7 @@ public class RouteDataObject { } public static float parseSpeed(String v, float def) { - if(v.equals("none")) { + if (v.equals("none")) { return RouteDataObject.NONE_MAX_SPEED; } else { int i = Algorithms.findFirstNumberEndIndex(v); @@ -614,20 +634,20 @@ public class RouteDataObject { f += Float.parseFloat(v.substring(0, i)); String pref = v.substring(i, v.length()).trim(); float add = 0; - for(int ik = 0; ik < pref.length(); ik++) { - if(Algorithms.isDigit(pref.charAt(ik)) || pref.charAt(ik) == '.' || pref.charAt(ik) == '-') { + for (int ik = 0; ik < pref.length(); ik++) { + if (Algorithms.isDigit(pref.charAt(ik)) || pref.charAt(ik) == '.' || pref.charAt(ik) == '-') { int first = Algorithms.findFirstNumberEndIndex(pref.substring(ik)); - if(first != -1) { + if (first != -1) { add = parseLength(pref.substring(ik), 0); pref = pref.substring(0, ik); } break; } } - if(pref.contains("km")) { + if (pref.contains("km")) { f *= 1000; } - if(pref.contains("\"") || pref.contains("in")) { + if (pref.contains("\"") || pref.contains("in")) { f *= 0.0254; } else if (pref.contains("\'") || pref.contains("ft") || pref.contains("feet")) { // foot to meters @@ -673,27 +693,27 @@ public class RouteDataObject { return false; } - public boolean roundabout(){ + public boolean roundabout() { int sz = types.length; - for(int i=0; i endPoint) { + if (startPoint > endPoint) { int k = endPoint; endPoint = startPoint; startPoint = k; } double d = 0; - for(int k = startPoint; k < endPoint && k < getPointsLength() -1; k++) { + for (int k = startPoint; k < endPoint && k < getPointsLength() - 1; k++) { int x = getPoint31XTile(k); int y = getPoint31YTile(k); int kx = getPoint31XTile(k + 1); @@ -974,16 +994,16 @@ public class RouteDataObject { // translate into meters total += simplifyDistance(x, y, px, py); } while (total < dist); - return -Math.atan2( x - px, y - py ); + return -Math.atan2(x - px, y - py); } private double simplifyDistance(int x, int y, int px, int py) { return Math.abs(px - x) * 0.011d + Math.abs(py - y) * 0.01863d; } - private static void assertTrueLength(String vl, float exp){ + private static void assertTrueLength(String vl, float exp) { float dest = parseLength(vl, 0); - if(exp != dest) { + if (exp != dest) { System.err.println("FAIL " + vl + " " + dest); } else { System.out.println("OK " + vl); @@ -992,24 +1012,24 @@ public class RouteDataObject { public static void main(String[] args) { assertTrueLength("10 km", 10000); - assertTrueLength("0.01 km", 10); - assertTrueLength("0.01 km 10 m", 20); - assertTrueLength("10 m", 10); - assertTrueLength("10m", 10); - assertTrueLength("3.4 m", 3.4f); - assertTrueLength("3.40 m", 3.4f); - assertTrueLength("10 m 10m", 20); - assertTrueLength("14'10\"", 4.5212f); - assertTrueLength("14.5'", 4.4196f); - assertTrueLength("14.5 ft", 4.4196f); - assertTrueLength("14'0\"", 4.2672f); - assertTrueLength("15ft", 4.572f); - assertTrueLength("15 ft 1 in", 4.5974f); - assertTrueLength("4.1 metres", 4.1f); - assertTrueLength("14'0''", 4.2672f); - assertTrueLength("14 feet", 4.2672f); - assertTrueLength("14 mile", 22530.76f); - assertTrueLength("14 cm", 0.14f); + assertTrueLength("0.01 km", 10); + assertTrueLength("0.01 km 10 m", 20); + assertTrueLength("10 m", 10); + assertTrueLength("10m", 10); + assertTrueLength("3.4 m", 3.4f); + assertTrueLength("3.40 m", 3.4f); + assertTrueLength("10 m 10m", 20); + assertTrueLength("14'10\"", 4.5212f); + assertTrueLength("14.5'", 4.4196f); + assertTrueLength("14.5 ft", 4.4196f); + assertTrueLength("14'0\"", 4.2672f); + assertTrueLength("15ft", 4.572f); + assertTrueLength("15 ft 1 in", 4.5974f); + assertTrueLength("4.1 metres", 4.1f); + assertTrueLength("14'0''", 4.2672f); + assertTrueLength("14 feet", 4.2672f); + assertTrueLength("14 mile", 22530.76f); + assertTrueLength("14 cm", 0.14f); // float badValue = -1; // assertTrueLength("none", badValue); @@ -1054,7 +1074,7 @@ public class RouteDataObject { public RestrictionInfo next; // optional to simulate linked list public int length() { - if(next == null) { + if (next == null) { return 1; } return next.length() + 1; @@ -1064,16 +1084,16 @@ public class RouteDataObject { public void setRestriction(int k, long to, int type, long viaWay) { long valto = (to << RouteDataObject.RESTRICTION_SHIFT) | ((long) type & RouteDataObject.RESTRICTION_MASK); restrictions[k] = valto; - if(viaWay != 0) { + if (viaWay != 0) { setRestrictionVia(k, viaWay); } } public void setRestrictionVia(int k, long viaWay) { - if(restrictionsVia != null) { + if (restrictionsVia != null) { long[] nrestrictionsVia = new long[Math.max(k + 1, restrictions.length)]; System.arraycopy(restrictions, 0, nrestrictionsVia, 0, restrictions.length); - restrictionsVia = nrestrictionsVia; + restrictionsVia = nrestrictionsVia; } else { restrictionsVia = new long[k + 1]; } diff --git a/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java b/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java index 861a7c9b93..32fb9873e2 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java @@ -279,6 +279,19 @@ public class Amenity extends MapObject { } public String getTagContent(String tag, String lang) { + String translateName = getStrictTagContent(tag, lang); + if (translateName != null) { + return translateName; + } + for (String nm : getAdditionalInfoKeys()) { + if (nm.startsWith(tag + ":")) { + return getAdditionalInfo(nm); + } + } + return null; + } + + public String getStrictTagContent(String tag, String lang) { if (lang != null) { String translateName = getAdditionalInfo(tag + ":" + lang); if (!Algorithms.isEmpty(translateName)) { @@ -293,11 +306,6 @@ public class Amenity extends MapObject { if (!Algorithms.isEmpty(enName)) { return enName; } - for (String nm : getAdditionalInfoKeys()) { - if (nm.startsWith(tag + ":")) { - return getAdditionalInfo(nm); - } - } return null; } diff --git a/OsmAnd-java/src/main/java/net/osmand/map/OsmandRegions.java b/OsmAnd-java/src/main/java/net/osmand/map/OsmandRegions.java index 033beb3789..882231d8a6 100644 --- a/OsmAnd-java/src/main/java/net/osmand/map/OsmandRegions.java +++ b/OsmAnd-java/src/main/java/net/osmand/map/OsmandRegions.java @@ -436,6 +436,7 @@ public class OsmandRegions { cx /= object.getPointsLength(); cy /= object.getPointsLength(); rd.regionCenter = new LatLon(MapUtils.get31LatitudeY((int) cy), MapUtils.get31LongitudeX((int) cx)); + rd.boundingBox = findBoundingBox(object); } rd.regionParentFullName = mapIndexFields.get(mapIndexFields.parentFullName, object); @@ -461,6 +462,43 @@ public class OsmandRegions { return rd; } + private QuadRect findBoundingBox(BinaryMapDataObject object) { + if (object.getPointsLength() == 0) { + return new QuadRect(0, 0, 0, 0); + } + + double currentX = object.getPoint31XTile(0); + double currentY = object.getPoint31YTile(0); + double minX = currentX; + double maxX = currentX; + double minY = currentY; + double maxY = currentY; + + if (object.getPointsLength() > 1) { + for (int i = 1; i < object.getPointsLength(); i++) { + currentX = object.getPoint31XTile(i); + currentY = object.getPoint31YTile(i); + if (currentX > maxX) { + maxX = currentX; + } else if (currentX < minX) { + minX = currentX; + } + if (currentY > maxY) { + maxY = currentY; + } else if (currentY < minY) { + minY = currentY; + } + } + } + + minX = MapUtils.get31LongitudeX((int) minX); + maxX = MapUtils.get31LongitudeX((int) maxX); + double revertedMinY = MapUtils.get31LatitudeY((int) maxY); + double revertedMaxY = MapUtils.get31LatitudeY((int) minY); + + return new QuadRect(minX, revertedMinY, maxX, revertedMaxY); + } + private String getSearchIndex(BinaryMapDataObject object) { MapIndex mi = object.getMapIndex(); TIntObjectIterator it = object.getObjectNames().iterator(); diff --git a/OsmAnd-java/src/main/java/net/osmand/map/WorldRegion.java b/OsmAnd-java/src/main/java/net/osmand/map/WorldRegion.java index 38fbc40c3d..b6fdb4f7d4 100644 --- a/OsmAnd-java/src/main/java/net/osmand/map/WorldRegion.java +++ b/OsmAnd-java/src/main/java/net/osmand/map/WorldRegion.java @@ -1,6 +1,7 @@ package net.osmand.map; import net.osmand.data.LatLon; +import net.osmand.data.QuadRect; import net.osmand.util.Algorithms; import java.io.Serializable; @@ -40,6 +41,7 @@ public class WorldRegion implements Serializable { protected String regionDownloadName; protected boolean regionMapDownload; protected LatLon regionCenter; + protected QuadRect boundingBox; public static class RegionParams { protected String regionLeftHandDriving; @@ -74,12 +76,12 @@ public class WorldRegion implements Serializable { } } - - + + public boolean isRegionMapDownload() { return regionMapDownload; } - + public String getLocaleName() { if(!Algorithms.isEmpty(regionNameLocale)) { return regionNameLocale; @@ -90,14 +92,14 @@ public class WorldRegion implements Serializable { if(!Algorithms.isEmpty(regionName)) { return regionName; } - + return capitalize(regionFullName.replace('_', ' ')); } - + public String getRegionDownloadName() { return regionDownloadName; } - + public String getRegionDownloadNameLC() { return regionDownloadName == null ? null : regionDownloadName.toLowerCase(); } @@ -109,7 +111,7 @@ public class WorldRegion implements Serializable { public LatLon getRegionCenter() { return regionCenter; } - + public String getRegionSearchText() { return regionSearchText; } @@ -143,7 +145,7 @@ public class WorldRegion implements Serializable { this.regionDownloadName = downloadName; superregion = null; subregions = new LinkedList(); - + } public WorldRegion(String id) { this(id, null); @@ -152,7 +154,7 @@ public class WorldRegion implements Serializable { public String getRegionId() { return regionFullName; } - + private String capitalize(String s) { String[] words = s.split(" "); if (words[0].length() > 0) { @@ -182,4 +184,20 @@ public class WorldRegion implements Serializable { } return res; } + + public boolean containsRegion(WorldRegion region) { + if (this.boundingBox != null && region.boundingBox != null) { + return this.boundingBox.contains(region.boundingBox); + } + return false; + } + + public boolean isContinent() { + if (superregion != null) { + String superRegionId = superregion.getRegionId(); + String thisRegionId = getRegionId(); + return WORLD.equals(superRegionId) && !RUSSIA_REGION_ID.equals(thisRegionId); + } + return false; + } } \ No newline at end of file diff --git a/OsmAnd-java/src/main/java/net/osmand/osm/RouteActivityType.java b/OsmAnd-java/src/main/java/net/osmand/osm/RouteActivityType.java new file mode 100644 index 0000000000..7c54388f64 --- /dev/null +++ b/OsmAnd-java/src/main/java/net/osmand/osm/RouteActivityType.java @@ -0,0 +1,266 @@ +package net.osmand.osm; + +import java.util.ArrayList; +import java.util.List; + +public class RouteActivityType { + private static final List values = new ArrayList<>(); + public static final String DEFAULT_ICON = "special_marker"; + public static final String DEFAULT_COLOR = "orange"; + + public static final RouteActivityType WATER = createType("water", "yellow").icon("special_kayak").reg(); + public static final RouteActivityType WINTER = createType("winter", "yellow").icon("special_skiing").reg(); + public static final RouteActivityType SNOWMOBILE = createType("snowmobile", "yellow").icon("special_snowmobile").reg(); + public static final RouteActivityType RIDING = createType("riding", "yellow").icon("special_horse").reg(); + public static final RouteActivityType RACING = createType("racing", "yellow").icon("raceway").reg(); + public static final RouteActivityType MOUNTAINBIKE = createType("mountainbike", "blue").icon("sport_cycling").reg(); + public static final RouteActivityType CYCLING = createType("cycling", "blue").icon("special_bicycle").reg(); + public static final RouteActivityType HIKING = createType("hiking", "orange").icon("special_trekking").reg(); + public static final RouteActivityType RUNNING = createType("running", "orange").icon("running").reg(); + public static final RouteActivityType WALKING = createType("walking", "orange").icon("special_walking").reg(); + public static final RouteActivityType OFFROAD = createType("offroad", "yellow").icon("special_offroad").reg(); + public static final RouteActivityType MOTORBIKE = createType("motorbike", "green").icon("special_motorcycle").reg(); + public static final RouteActivityType CAR = createType("car", "green").icon("shop_car").reg(); + // less specific bottom order + String name; + String color; + String icon; + + RouteActivityType(String nm, String clr) { + this.name = nm; + this.color = clr; + } + + public String getName() { + return name; + } + + public String getColor() { + return color; + } + + public String getIcon() { + return icon; + } + + public static RouteActivityType getOrCreateTypeFromName(String name) { + for (RouteActivityType rat : values) { + if (rat.name.equalsIgnoreCase(name)) { + return rat; + } + } + return createType(name.toLowerCase(), DEFAULT_COLOR).icon(DEFAULT_ICON).reg(); + } + + private static RouteActivityTypeBuilder createType(String name, String color) { + RouteActivityTypeBuilder builder = new RouteActivityTypeBuilder(); + builder.routeActivityType = new RouteActivityType(name, color); + return builder; + } + + public static RouteActivityType getTypeFromTags(String[] tags) { + RouteActivityType activityType = null; + for (String tg : tags) { + RouteActivityType rat = RouteActivityType.convertFromOsmGPXTag(tg); + if (rat != null) { + if (activityType == null || values.indexOf(activityType) > values.indexOf(rat)) { + activityType = rat; + } + } + } + return activityType; + } + + public static RouteActivityType convertFromOsmGPXTag(String tg) { + String t = tg.toLowerCase(); + if ("mountain hiking".equalsIgnoreCase(t)) { + return HIKING; + } + if ("motorcar".equalsIgnoreCase(t)) { + return CAR; + } + if ("laufen".equalsIgnoreCase(t)) { + return RUNNING; + } + if ("pedestrian".equalsIgnoreCase(t)) { + return WALKING; + } + switch (t) { + case "mountainbiking": + case "mtb": + case "mountainbike": + case "mountain bike": + case "mountain biking": + case "mountbarker": + case "mtb-tour": + case "ciclismo-mtb-gravel": + case "vtt": + case "btt": + case "vth": + case "mtb ride": + return MOUNTAINBIKE; + case "hiking": + case "route=hiking": + case "mountain hiking": + case "hiking trail": + case "wandern": + case "hike": + case "randonnée": + case "trekking": + case "climbing": + return HIKING; + case "bike": + case "biking": + case "bicycling": + case "bicycle": + case "cycling": + case "cycle": + case "cycleway": + case "cykel": + case "handcycle": + case "cyclotourisme": + case "route=bicycle": + case "cyclotourism": + case "fietsen": + case "вело": + case "велосипед": + case "rower": + case "trasa rowerem": + case "vélo": + case "velo": + case "radtour": + case "bici": + case "fiets": + case "fahrrad": + case "ncn": + case "icn": + case "lcn": + case "network=ncn": + case "network=icn": + case "network=lcn": + return CYCLING; + case "car": + case "motorcar": + case "by car": + case "auto": + case "автомобиль": + case "automobile": + case "autós": + case "driving": + case "drive": + case "van": + case "авто": + case "на автомобиле": + case "bus": + case "truck": + case "taxi": + return CAR; + case "running": + case "run": + case "rungis": + case "trail running": + case "trailrunning": + case "бег": + case "laufen": + case "langlauf": + case "lauf": + case "course": + case "jogging": + case "fitotrack": + return RUNNING; + case "wanderung": + case "walking": + case "walk": + case "nightwalk": + case "walkway": + case "пешком": + case "пеший": + case "pěšky": + case "marche": + case "pedestrian": + case "foot": + case "footing": + case "on_foot": + case "byfoot": + case "onfoot": + case "sightseeing": + case "geocaching": + case "etnanatura": + case "etna": + case "iwn": + case "lwn": + case "rwn": + case "network=iwn": + case "network=lwn": + case "network=rwn": + return WALKING; + case "ling-moto": + case "motorbiking": + case "motorcycle": + case "motorrad": + case "motorbike": + case "motor bike": + case "FVbike": + case "Motorrad": + return MOTORBIKE; + case "offroad": + case "off-road": + case "off road": + case "4x4": + case "terrain": + case "quad": + case "enduro": + case "feldwege": + case "feldweg": + return OFFROAD; + case "boat": + case "water": + case "boating": + case "kayak": + case "river": + case "lake": + case "lakes": + case "canal": + return WATER; + case "ski": + case "skiing": + case "skating": + case "skitour": + case "winter": + case "wintersports": + case "snowboard": + case "лыжи": + case "лыжня": + case "nordic": + case "piste": + return WINTER; + case "snowmobile=designated": + case "snowmobile=permissive": + case "snowmobile=yes": + case "snowmobile": + return SNOWMOBILE; + case "ride": + case "horse": + case "horse trail": + return RIDING; + case "racing": + return RACING; + } + return null; + } + + public static class RouteActivityTypeBuilder { + + private RouteActivityType routeActivityType; + + public RouteActivityTypeBuilder icon(String icon) { + routeActivityType.icon = icon; + return this; + } + + private RouteActivityType reg() { + values.add(routeActivityType); + return routeActivityType; + } + } +} \ No newline at end of file diff --git a/OsmAnd-java/src/main/java/net/osmand/osm/edit/EntityParser.java b/OsmAnd-java/src/main/java/net/osmand/osm/edit/EntityParser.java index 9caa54b55c..bd37de6ba0 100644 --- a/OsmAnd-java/src/main/java/net/osmand/osm/edit/EntityParser.java +++ b/OsmAnd-java/src/main/java/net/osmand/osm/edit/EntityParser.java @@ -21,7 +21,7 @@ import net.osmand.osm.edit.Relation.RelationMember; import net.osmand.util.Algorithms; public class EntityParser { - + public static void parseMapObject(MapObject mo, Entity e, Map tags) { mo.setId(e.getId()); if(mo instanceof Amenity) { @@ -123,7 +123,7 @@ public class EntityParser { mo.setName(ref); } } - + private static void setNameFromBrand(MapObject mo, Map tags) { String ref = tags.get(OSMTagKey.BRAND.getValue()); if(ref != null){ @@ -140,7 +140,7 @@ public class EntityParser { op += " [" + ref + "]"; mo.setName(op); } - + private static String getWebSiteURL(Map tagValues, boolean checkWikipedia) { String siteUrl = null; @@ -170,14 +170,14 @@ public class EntityParser { } return siteUrl; } - - + + public static List parseAmenities(MapPoiTypes poiTypes, Entity entity, Map tags, List amenitiesList) { amenitiesList.clear(); // it could be collection of amenities boolean relation = entity instanceof Relation; - boolean purerelation = relation && + boolean purerelation = relation && !("multipolygon".equals(tags.get("type")) || "boundary".equals(tags.get("type"))); Collection> it = MapRenderingTypes.splitTagsIntoDifferentObjects(tags); for (Map ts : it) { @@ -201,9 +201,7 @@ public class EntityParser { } return amenitiesList; } - - - + private static boolean checkAmenitiesToAdd(Amenity a, List amenitiesList){ // check amenity for duplication for(Amenity b : amenitiesList){ @@ -212,9 +210,9 @@ public class EntityParser { } } return true; - + } - + public static Building parseBuilding(Entity e){ Building b = new Building(); parseMapObject(b, e, e.getTags()); @@ -228,7 +226,7 @@ public class EntityParser { List nodes = ((Way) e).getNodes(); for(int i = 0; i < nodes.size(); i++) { Node node = nodes.get(i); - if(node != null && "yes".equals(node.getTag(OSMTagKey.ENTRANCE)) && + if(node != null && "yes".equals(node.getTag(OSMTagKey.ENTRANCE)) && !Algorithms.isEmpty(node.getTag(OSMTagKey.REF))) { b.addEntrance(node.getTag(OSMTagKey.REF), node.getLatLon()); } @@ -236,11 +234,11 @@ public class EntityParser { } return b; } - + public static City parseCity(Node el) { return parseCity(el, CityType.valueFromString(el.getTag(OSMTagKey.PLACE.getValue()))); } - + public static City parseCity(Entity el, CityType t) { if(t == null) { return null; @@ -252,15 +250,15 @@ public class EntityParser { c.setIsin(isin); return c; } - - + + public static TransportRoute parserRoute(Relation r, String ref){ TransportRoute rt = new TransportRoute(); parseMapObject(rt, r, r.getTags()); rt.setRef(ref); return rt; } - + public static TransportStop parseTransportStop(Entity e){ TransportStop st = new TransportStop(); parseMapObject(st, e, e.getTags()); diff --git a/OsmAnd-java/src/main/java/net/osmand/osm/io/OsmBaseStorage.java b/OsmAnd-java/src/main/java/net/osmand/osm/io/OsmBaseStorage.java index d72fb952ce..074149b095 100644 --- a/OsmAnd-java/src/main/java/net/osmand/osm/io/OsmBaseStorage.java +++ b/OsmAnd-java/src/main/java/net/osmand/osm/io/OsmBaseStorage.java @@ -183,7 +183,7 @@ public class OsmBaseStorage { protected static final int moduleProgress = 1 << 10; public void startElement(XmlPullParser parser, String name) { - if(!parseStarted){ + if (!parseStarted) { initRootElement(parser, name); } if (ELEM_MODIFY.equals(name) ) { @@ -283,13 +283,14 @@ public class OsmBaseStorage { if (type != null) { if(currentParsedEntity != null){ EntityId entityId = new EntityId(type, currentParsedEntity.getId()); - if(acceptEntityToLoad(entityId, currentParsedEntity)){ + if (acceptEntityToLoad(entityId, currentParsedEntity)) { Entity oldEntity = entities.put(entityId, currentParsedEntity); - if(parseEntityInfo && currentParsedEntityInfo != null){ + if (parseEntityInfo && currentParsedEntityInfo != null) { entityInfo.put(entityId, currentParsedEntityInfo); } - if(!supressWarnings && oldEntity!= null){ - throw new UnsupportedOperationException("Entity with id=" + oldEntity.getId() +" is duplicated in osm map"); //$NON-NLS-1$ //$NON-NLS-2$ + if (!supressWarnings && oldEntity != null) { + throw new UnsupportedOperationException( + "Entity with id=" + oldEntity.getId() + " is duplicated in osm map"); //$NON-NLS-1$ //$NON-NLS-2$ } } else { // System.gc(); @@ -308,8 +309,8 @@ public class OsmBaseStorage { protected boolean acceptEntityToLoad(EntityId entityId, Entity entity) { - for(IOsmStorageFilter f : filters){ - if(!f.acceptEntityToLoad(this, entityId, entity)){ + for (IOsmStorageFilter f : filters) { + if (!f.acceptEntityToLoad(this, entityId, entity)) { return false; } } diff --git a/OsmAnd-java/src/main/java/net/osmand/router/GeneralRouter.java b/OsmAnd-java/src/main/java/net/osmand/router/GeneralRouter.java index fad98d466d..be5bba2f5f 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/GeneralRouter.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/GeneralRouter.java @@ -620,9 +620,7 @@ public class GeneralRouter implements VehicleRouter { public double calculateTurnTime(RouteSegment segment, int segmentEnd, RouteSegment prev, int prevSegmentEnd) { float ts = getPenaltyTransition(segment.getRoad()); float prevTs = getPenaltyTransition(prev.getRoad()); - float totalPenalty = 0; - if (prevTs != ts) { totalPenalty += Math.abs(ts - prevTs) / 2; } @@ -638,7 +636,9 @@ public class GeneralRouter implements VehicleRouter { // } // } // } - + if (shortestRoute) { + return totalPenalty; + } if(segment.getRoad().roundabout() && !prev.getRoad().roundabout()) { double rt = getRoundaboutTurn(); if(rt > 0) { diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java b/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java new file mode 100644 index 0000000000..6730965d1f --- /dev/null +++ b/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java @@ -0,0 +1,448 @@ +package net.osmand.router; + +import net.osmand.GPXUtilities; +import net.osmand.PlatformUtil; +import net.osmand.osm.edit.Node; +import net.osmand.osm.edit.OsmMapUtils; +import net.osmand.util.MapUtils; +import org.apache.commons.logging.Log; + +import java.util.ArrayList; +import java.util.List; + +public class RouteColorize { + + public int zoom; + public double[] latitudes; + public double[] longitudes; + public double[] values; + public double minValue; + public double maxValue; + public double[][] palette; + + private List dataList; + + public static final int DARK_GREY = rgbaToDecimal(92, 92, 92, 255); + public static final int LIGHT_GREY = rgbaToDecimal(200, 200, 200, 255); + public static final int RED = rgbaToDecimal(255,1,1,255); + public static final int GREEN = rgbaToDecimal(46,185,0,191); + public static final int YELLOW = rgbaToDecimal(255,222,2,227); + + public enum ValueType { + ELEVATION, + SPEED, + SLOPE, + NONE + } + + private final int VALUE_INDEX = 0; + private final int DECIMAL_COLOR_INDEX = 1;//sRGB decimal format + private final int RED_COLOR_INDEX = 1;//RGB + private final int GREEN_COLOR_INDEX = 2;//RGB + private final int BLUE_COLOR_INDEX = 3;//RGB + private final int ALPHA_COLOR_INDEX = 4;//RGBA + + private ValueType valueType; + + public static int SLOPE_RANGE = 150;//150 meters + private static final double MIN_DIFFERENCE_SLOPE = 0.05d;//5% + + private static final Log LOG = PlatformUtil.getLog(RouteColorize.class); + + /** + * @param minValue can be NaN + * @param maxValue can be NaN + * @param palette array {{value,color},...} - color in sRGB (decimal) format OR {{value,RED,GREEN,BLUE,ALPHA},...} - color in RGBA format + */ + public RouteColorize(int zoom, double[] latitudes, double[] longitudes, double[] values, double minValue, double maxValue, double[][] palette) { + this.zoom = zoom; + this.latitudes = latitudes; + this.longitudes = longitudes; + this.values = values; + this.minValue = minValue; + this.maxValue = maxValue; + this.palette = palette; + + if (Double.isNaN(minValue) || Double.isNaN(maxValue)) { + calculateMinMaxValue(); + } + checkPalette(); + sortPalette(); + } + + /** + * @param type ELEVATION, SPEED, SLOPE + */ + public RouteColorize(int zoom, GPXUtilities.GPXFile gpxFile, ValueType type) { + + if (!gpxFile.hasTrkPt()) { + LOG.warn("GPX file is not consist of track points"); + return; + } + + List latList = new ArrayList<>(); + List lonList = new ArrayList<>(); + List valList = new ArrayList<>(); + for (GPXUtilities.Track t : gpxFile.tracks) { + for (GPXUtilities.TrkSegment ts : t.segments) { + for (GPXUtilities.WptPt p : ts.points) { + latList.add(p.lat); + lonList.add(p.lon); + if (type == ValueType.SPEED) { + valList.add(p.speed); + } else { + valList.add(p.ele); + } + } + } + } + + this.zoom = zoom; + latitudes = listToArray(latList); + longitudes = listToArray(lonList); + + if (type == ValueType.SLOPE) { + values = calculateSlopesByElevations(latitudes, longitudes, listToArray(valList), SLOPE_RANGE); + } else { + values = listToArray(valList); + } + + calculateMinMaxValue(); + valueType = type; + checkPalette(); + sortPalette(); + } + + /** + * Calculate slopes from elevations needs for right colorizing + * + * @param slopeRange - in what range calculate the derivative, usually we used 150 meters + * @return slopes array, in the begin and the end present NaN values! + */ + public double[] calculateSlopesByElevations(double[] latitudes, double[] longitudes, double[] elevations, double slopeRange) { + + double[] newElevations = elevations; + for (int i = 2; i < elevations.length - 2; i++) { + newElevations[i] = elevations[i - 2] + + elevations[i - 1] + + elevations[i] + + elevations[i + 1] + + elevations[i + 2]; + newElevations[i] /= 5; + } + elevations = newElevations; + + double[] slopes = new double[elevations.length]; + if (latitudes.length != longitudes.length || latitudes.length != elevations.length) { + LOG.warn("Sizes of arrays latitudes, longitudes and values are not match"); + return slopes; + } + + double[] distances = new double[elevations.length]; + double totalDistance = 0.0d; + distances[0] = totalDistance; + for (int i = 0; i < elevations.length - 1; i++) { + totalDistance += MapUtils.getDistance(latitudes[i], longitudes[i], latitudes[i + 1], longitudes[i + 1]); + distances[i + 1] = totalDistance; + } + + for (int i = 0; i < elevations.length; i++) { + if (distances[i] < slopeRange / 2 || distances[i] > totalDistance - slopeRange / 2) { + slopes[i] = Double.NaN; + } else { + double[] arg = findDerivativeArguments(distances, elevations, i, slopeRange); + slopes[i] = (arg[1] - arg[0]) / (arg[3] - arg[2]); + } + } + return slopes; + } + + public List getResult(boolean simplify) { + List result = new ArrayList<>(); + if (simplify) { + result = simplify(); + } else { + for (int i = 0; i < latitudes.length; i++) { + result.add(new RouteColorizationPoint(i, latitudes[i], longitudes[i], values[i])); + } + } + for (RouteColorizationPoint data : result) { + data.color = getColorByValue(data.val); + } + return result; + } + + public int getColorByValue(double value) { + if (Double.isNaN(value)) { + value = (minValue + maxValue) / 2; + } + for (int i = 0; i < palette.length - 1; i++) { + if (value == palette[i][VALUE_INDEX]) + return (int) palette[i][DECIMAL_COLOR_INDEX]; + if (value >= palette[i][VALUE_INDEX] && value <= palette[i + 1][VALUE_INDEX]) { + int minPaletteColor = (int) palette[i][DECIMAL_COLOR_INDEX]; + int maxPaletteColor = (int) palette[i + 1][DECIMAL_COLOR_INDEX]; + double minPaletteValue = palette[i][VALUE_INDEX]; + double maxPaletteValue = palette[i + 1][VALUE_INDEX]; + double percent = (value - minPaletteValue) / (maxPaletteValue - minPaletteValue); + double resultRed = getRed(minPaletteColor) + percent * (getRed(maxPaletteColor) - getRed(minPaletteColor)); + double resultGreen = getGreen(minPaletteColor) + percent * (getGreen(maxPaletteColor) - getGreen(minPaletteColor)); + double resultBlue = getBlue(minPaletteColor) + percent * (getBlue(maxPaletteColor) - getBlue(minPaletteColor)); + double resultAlpha = getAlpha(minPaletteColor) + percent * (getAlpha(maxPaletteColor) - getAlpha(minPaletteColor)); + return rgbaToDecimal((int) resultRed, (int) resultGreen, (int) resultBlue, (int) resultAlpha); + } + } + return getDefaultColor(); + } + + public void setPalette(double[][] palette) { + this.palette = palette; + checkPalette(); + sortPalette(); + } + + private int getDefaultColor() { + return rgbaToDecimal(0, 0, 0, 0); + } + + private List simplify() { + if (dataList == null) { + dataList = new ArrayList<>(); + for (int i = 0; i < latitudes.length; i++) { + //System.out.println(latitudes[i] + " " + longitudes[i] + " " + values[i]); + dataList.add(new RouteColorizationPoint(i, latitudes[i], longitudes[i], values[i])); + } + } + List nodes = new ArrayList<>(); + List result = new ArrayList<>(); + for (RouteColorizationPoint data : dataList) { + nodes.add(new net.osmand.osm.edit.Node(data.lat, data.lon, data.id)); + } + OsmMapUtils.simplifyDouglasPeucker(nodes, zoom + 5, 1, result, true); + + List simplified = new ArrayList<>(); + + for (int i = 1; i < result.size() - 1; i++) { + int prevId = (int) result.get(i - 1).getId(); + int currentId = (int) result.get(i).getId(); + List sublist = dataList.subList(prevId, currentId); + simplified.addAll(getExtremums(sublist)); + } + return simplified; + } + + private List getExtremums(List subDataList) { + if (subDataList.size() <= 2) { + return subDataList; + } + + List result = new ArrayList<>(); + double min; + double max; + min = max = subDataList.get(0).val; + for (RouteColorizationPoint pt : subDataList) { + if (min > pt.val) { + min = pt.val; + } + if (max < pt.val) { + max = pt.val; + } + } + + double diff = max - min; + + result.add(subDataList.get(0)); + for (int i = 1; i < subDataList.size() - 1; i++) { + double prev = subDataList.get(i - 1).val; + double current = subDataList.get(i).val; + double next = subDataList.get(i + 1).val; + RouteColorizationPoint currentData = subDataList.get(i); + + if ((current > prev && current > next) || (current < prev && current < next) + || (current < prev && current == next) || (current == prev && current < next) + || (current > prev && current == next) || (current == prev && current > next)) { + RouteColorizationPoint prevInResult; + if (result.size() > 0) { + prevInResult = result.get(0); + if (prevInResult.val / diff > MIN_DIFFERENCE_SLOPE) { + result.add(currentData); + } + } else + result.add(currentData); + } + } + result.add(subDataList.get(subDataList.size() - 1)); + return result; + } + + private void checkPalette() { + if (palette == null || palette.length < 2 || palette[0].length < 2 || palette[1].length < 2) { + LOG.info("Will use default palette"); + palette = new double[3][2]; + + double[][] defaultPalette = { + {minValue, GREEN}, + {valueType == ValueType.SLOPE ? 0 : (minValue + maxValue) / 2, YELLOW}, + {maxValue, RED} + }; + palette = defaultPalette; + } + double min; + double max = min = palette[0][VALUE_INDEX]; + int minIndex = 0; + int maxIndex = 0; + double[][] sRGBPalette = new double[palette.length][2]; + for (int i = 0; i < palette.length; i++) { + double[] p = palette[i]; + if (p.length == 2) { + sRGBPalette[i] = p; + } else if (p.length == 4) { + int color = rgbaToDecimal((int) p[RED_COLOR_INDEX], (int) p[GREEN_COLOR_INDEX], (int) p[BLUE_COLOR_INDEX], 255); + sRGBPalette[i] = new double[]{p[VALUE_INDEX], color}; + } else if (p.length >= 5) { + int color = rgbaToDecimal((int) p[RED_COLOR_INDEX], (int) p[GREEN_COLOR_INDEX], (int) p[BLUE_COLOR_INDEX], (int) p[ALPHA_COLOR_INDEX]); + sRGBPalette[i] = new double[]{p[VALUE_INDEX], color}; + } + if (p[VALUE_INDEX] > max) { + max = p[VALUE_INDEX]; + maxIndex = i; + } + if (p[VALUE_INDEX] < min) { + min = p[VALUE_INDEX]; + minIndex = i; + } + } + palette = sRGBPalette; + if (minValue < min) { + palette[minIndex][VALUE_INDEX] = minValue; + } + if (maxValue > max) { + palette[maxIndex][VALUE_INDEX] = maxValue; + } + } + + private void sortPalette() { + java.util.Arrays.sort(palette, new java.util.Comparator() { + public int compare(double[] a, double[] b) { + return Double.compare(a[VALUE_INDEX], b[VALUE_INDEX]); + } + }); + } + + /** + * @return double[minElevation, maxElevation, minDist, maxDist] + */ + private double[] findDerivativeArguments(double[] distances, double[] elevations, int index, double slopeRange) { + double[] result = new double[4]; + double minDist = distances[index] - slopeRange / 2; + double maxDist = distances[index] + slopeRange / 2; + result[0] = Double.NaN; + result[1] = Double.NaN; + result[2] = minDist; + result[3] = maxDist; + int closestMaxIndex = -1; + int closestMinIndex = -1; + for (int i = index; i < distances.length; i++) { + if (distances[i] == maxDist) { + result[1] = elevations[i]; + break; + } + if (distances[i] > maxDist) { + closestMaxIndex = i; + break; + } + } + for (int i = index; i >= 0; i--) { + if (distances[i] == minDist) { + result[0] = elevations[i]; + break; + } + if (distances[i] < minDist) { + closestMinIndex = i; + break; + } + } + if (closestMaxIndex > 0) { + double diff = distances[closestMaxIndex] - distances[closestMaxIndex - 1]; + double coef = (maxDist - distances[closestMaxIndex - 1]) / diff; + if (coef > 1 || coef < 0) { + LOG.warn("Coefficient fo max must be 0..1 , coef=" + coef); + } + result[1] = (1 - coef) * elevations[closestMaxIndex - 1] + coef * elevations[closestMaxIndex]; + } + if (closestMinIndex >= 0) { + double diff = distances[closestMinIndex + 1] - distances[closestMinIndex]; + double coef = (minDist - distances[closestMinIndex]) / diff; + if (coef > 1 || coef < 0) { + LOG.warn("Coefficient for min must be 0..1 , coef=" + coef); + } + result[0] = (1 - coef) * elevations[closestMinIndex] + coef * elevations[closestMinIndex + 1]; + } + if (Double.isNaN(result[0]) || Double.isNaN(result[1])) { + LOG.warn("Elevations wasn't calculated"); + } + return result; + } + + private void calculateMinMaxValue() { + if (values.length == 0) + return; + minValue = maxValue = Double.NaN; + for (double value : values) { + if ((Double.isNaN(maxValue) || Double.isNaN(minValue)) && !Double.isNaN(value)) + maxValue = minValue = value; + if (minValue > value) + minValue = value; + if (maxValue < value) + maxValue = value; + } + } + + private double[] listToArray(List doubleList) { + double[] result = new double[doubleList.size()]; + for (int i = 0; i < doubleList.size(); i++) { + result[i] = doubleList.get(i); + } + return result; + } + + private static int rgbaToDecimal(int r, int g, int b, int a) { + int value = ((a & 0xFF) << 24) | + ((r & 0xFF) << 16) | + ((g & 0xFF) << 8) | + ((b & 0xFF) << 0); + return value; + } + + private int getRed(int value) { + return (value >> 16) & 0xFF; + } + + private int getGreen(int value) { + return (value >> 8) & 0xFF; + } + + private int getBlue(int value) { + return (value >> 0) & 0xFF; + } + + private int getAlpha(int value) { + return (value >> 24) & 0xff; + } + + public static class RouteColorizationPoint { + int id; + public double lat; + public double lon; + public double val; + public int color; + + RouteColorizationPoint(int id, double lat, double lon, double val) { + this.id = id; + this.lat = lat; + this.lon = lon; + this.val = val; + } + } + +} diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RouteStatisticsHelper.java b/OsmAnd-java/src/main/java/net/osmand/router/RouteStatisticsHelper.java index 4fe500de2b..43022a1a37 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RouteStatisticsHelper.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RouteStatisticsHelper.java @@ -367,8 +367,9 @@ public class RouteStatisticsHelper { RouteDataObject obj = segment.obj; for (int type : obj.getTypes()) { BinaryMapRouteReaderAdapter.RouteTypeRule tp = obj.region.quickGetEncodingRule(type); - if (tp.getTag().equals("highway") || tp.getTag().equals("route") || - tp.getTag().equals("railway") || tp.getTag().equals("aeroway") || tp.getTag().equals("aerialway")) { + if (tp.getTag().equals("highway") || tp.getTag().equals("route") + || tp.getTag().equals("railway") || tp.getTag().equals("aeroway") + || tp.getTag().equals("aerialway") || tp.getTag().equals("piste:type")) { if (!mainTagAdded) { req.setStringFilter(rrs.PROPS.R_TAG, tp.getTag()); req.setStringFilter(rrs.PROPS.R_VALUE, tp.getValue()); diff --git a/OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java b/OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java index a9d85ce118..36c0034a26 100644 --- a/OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java +++ b/OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java @@ -53,6 +53,8 @@ public class Algorithms { public static final int XML_FILE_SIGNATURE = 0x3c3f786d; public static final int OBF_FILE_SIGNATURE = 0x08029001; public static final int SQLITE_FILE_SIGNATURE = 0x53514C69; + public static final int BZIP_FILE_SIGNATURE = 0x425a; + public static final int GZIP_FILE_SIGNATURE = 0x1f8b; public static String normalizeSearchText(String s) { boolean norm = false; @@ -127,6 +129,17 @@ public class Algorithms { return def; } + public static double parseDoubleSilently(String input, double def) { + if (input != null && input.length() > 0) { + try { + return Double.parseDouble(input); + } catch (NumberFormatException e) { + return def; + } + } + return def; + } + public static String getFileNameWithoutExtension(File f) { return getFileNameWithoutExtension(f.getName()); } @@ -311,6 +324,24 @@ public class Algorithms { return test == ZIP_FILE_SIGNATURE; } + public static boolean checkFileSignature(InputStream inputStream, int fileSignature) throws IOException { + if (inputStream == null) return false; + int firstBytes; + if (isSmallFileSignature(fileSignature)) { + firstBytes = readSmallInt(inputStream); + } else { + firstBytes = readInt(inputStream); + } + if (inputStream.markSupported()) { + inputStream.reset(); + } + return firstBytes == fileSignature; + } + + public static boolean isSmallFileSignature(int fileSignature) { + return fileSignature == BZIP_FILE_SIGNATURE || fileSignature == GZIP_FILE_SIGNATURE; + } + /** * Checks, whether the child directory is a subdirectory of the parent * directory. @@ -347,6 +378,14 @@ public class Algorithms { return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4); } + public static int readSmallInt(InputStream in) throws IOException { + int ch1 = in.read(); + int ch2 = in.read(); + if ((ch1 | ch2) < 0) + throw new EOFException(); + return ((ch1 << 8) + ch2); + } + public static String capitalizeFirstLetterAndLowercase(String s) { if (s != null && s.length() > 1) { // not very efficient algorithm @@ -526,6 +565,13 @@ public class Algorithms { } } + public static ByteArrayInputStream createByteArrayIS(InputStream in) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + streamCopy(in, out); + in.close(); + return new ByteArrayInputStream(out.toByteArray()); + } + @SuppressWarnings("ResultOfMethodCallIgnored") public static void updateAllExistingImgTilesToOsmandFormat(File f) { if (f.isDirectory()) { diff --git a/OsmAnd-java/src/main/java/net/osmand/util/MapUtils.java b/OsmAnd-java/src/main/java/net/osmand/util/MapUtils.java index 7a37eee3aa..97e921a3d4 100644 --- a/OsmAnd-java/src/main/java/net/osmand/util/MapUtils.java +++ b/OsmAnd-java/src/main/java/net/osmand/util/MapUtils.java @@ -49,6 +49,47 @@ public class MapUtils { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_', '~' }; + public static int calculateFromBaseZoomPrecisionXY(int baseZoom, int finalZoom, int xFinal, int yFinal) { + int px = xFinal; + int py = yFinal; + int precisionNumber = 1; + for (int zoom = finalZoom - 1; zoom >= baseZoom; zoom--) { + int x = px / 2; // (int) MapUtils.getTileNumberX(zoom, lon); + int y = py / 2; // (int) MapUtils.getTileNumberY(zoom, lat); + int deltax = px - x * 2; + int deltay = py - y * 2; + precisionNumber = (precisionNumber << 2) + (deltax << 1) + deltay; + // StringBuilder spaces = new StringBuilder(); + // for (int i = 0; i < 32 - zoom; i++) { + // spaces.append(' '); + // } + // System.out.println(String.format("%d %s + %d %s %s + %d", zoom, Integer.toBinaryString(x), deltax, spaces.toString(), Integer.toBinaryString(y), deltay)); + px = x; + py = y; + } + // System.out.println(String.format("Bits: %d %s (%d)", Integer.toBinaryString(precisionNumber).length(), Integer.toBinaryString(precisionNumber), precisionNumber)); + return precisionNumber; + } + + public static int[] calculateFinalXYFromBaseAndPrecisionXY(int bazeZoom, int finalZoom, + int precisionXY, int xBase, int yBase, boolean ignoreNotEnoughPrecision) { + // System.out.println(String.format("Base x, y at zoom %d: %d %d", zoomToStart, xBaseApproximation, yBaseApproximation)); + // calculate finish approximation using precisionNumber + int finalX = xBase; + int finalY = yBase; + int precisionCalc = precisionXY; + for (int zoom = bazeZoom; zoom < finalZoom; zoom++) { + if (precisionCalc <= 1 && precisionCalc > 0 && !ignoreNotEnoughPrecision) { + throw new IllegalArgumentException("Not enough bits to retrieve zoom approximation"); + } + finalY = finalY * 2 + (precisionXY & 1); + finalX = finalX * 2 + ((precisionXY & 2) >> 1); + precisionXY = precisionXY >> 2; + } + // System.out.println(String.format("Calc x, y at zoom %d: %d %d", finalZoom, finalX, finalY)); + return new int[] { finalX, finalY }; + } + public static double getDistance(LatLon l, double latitude, double longitude) { diff --git a/OsmAnd-telegram/res/values-el/strings.xml b/OsmAnd-telegram/res/values-el/strings.xml index ce8aec1832..ff81d49e6c 100644 --- a/OsmAnd-telegram/res/values-el/strings.xml +++ b/OsmAnd-telegram/res/values-el/strings.xml @@ -36,7 +36,7 @@ Κλείσιμο Ανενεργό Εγκατάσταση - Διαμοιρασμός + Κοινοποίηση Προηγούμενο Συνέχεια Ακύρωση diff --git a/OsmAnd-telegram/res/values-uk/strings.xml b/OsmAnd-telegram/res/values-uk/strings.xml index 40c91d4ed3..1cf8bf63b6 100644 --- a/OsmAnd-telegram/res/values-uk/strings.xml +++ b/OsmAnd-telegram/res/values-uk/strings.xml @@ -135,10 +135,10 @@ мл км м - мор.м. + nmi хв/м хв/км - вузл + вузлів м/с км/г мл/г diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramLocationProvider.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramLocationProvider.kt index 719e21b886..2a9f66aee4 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramLocationProvider.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramLocationProvider.kt @@ -46,8 +46,8 @@ class TelegramLocationProvider(private val app: TelegramApplication) : SensorEve private var fusedLocationProviderClient: FusedLocationProviderClient? = null private val locationRequest = LocationRequest().apply { interval = 1000 - fastestInterval = 500 - maxWaitTime = 2000 + //fastestInterval = 500 + maxWaitTime = 0 priority = LocationRequest.PRIORITY_HIGH_ACCURACY } private val locationCallback = object : LocationCallback() { diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt index 2d85e971b0..80d3e05098 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt @@ -82,11 +82,11 @@ class TelegramService : Service(), TelegramIncomingMessagesListener, // Sets the fastest rate for active location updates. This interval is exact, and your // application will never receive updates more frequently than this value. - fastestInterval = 500 + //fastestInterval = 500 // Sets the maximum time when batched location updates are delivered. Updates may be // delivered sooner than this interval. - maxWaitTime = 2000 + maxWaitTime = 0 priority = LocationRequest.PRIORITY_HIGH_ACCURACY } diff --git a/OsmAnd/.gitignore b/OsmAnd/.gitignore index 3149fb4209..a4e941e9d2 100644 --- a/OsmAnd/.gitignore +++ b/OsmAnd/.gitignore @@ -39,11 +39,7 @@ mx_* valgrind/ bin/ dist/ -assets/specialphrases/* -assets/voice/* -assets/fonts/* -assets/feature_articles/* -assets/World_basemap_mini* + gen/ local.properties raw/ @@ -56,6 +52,11 @@ out/ use/ osmand.properties osmand.xml +assets/specialphrases/* +assets/voice/* +assets/fonts/* +assets/feature_articles/* +assets/*.obf assets/style.css assets/poi_categories.json assets/help/*.html diff --git a/OsmAnd/AndroidManifest-huawei.xml b/OsmAnd/AndroidManifest-huawei.xml index c71b7a3a93..f45fb81404 100644 --- a/OsmAnd/AndroidManifest-huawei.xml +++ b/OsmAnd/AndroidManifest-huawei.xml @@ -4,7 +4,7 @@ - \ No newline at end of file + diff --git a/OsmAnd/assets/bundled_assets.xml b/OsmAnd/assets/bundled_assets.xml index d5626ac886..132bf2d6ef 100644 --- a/OsmAnd/assets/bundled_assets.xml +++ b/OsmAnd/assets/bundled_assets.xml @@ -109,4 +109,5 @@ + diff --git a/OsmAnd/assets/fonts/RobotoSlab-Regular.ttf b/OsmAnd/assets/fonts/RobotoSlab-Regular.ttf deleted file mode 100755 index eb52a79073..0000000000 Binary files a/OsmAnd/assets/fonts/RobotoSlab-Regular.ttf and /dev/null differ diff --git a/OsmAnd/build-common.gradle b/OsmAnd/build-common.gradle index 379b5c7a20..38dce71283 100644 --- a/OsmAnd/build-common.gradle +++ b/OsmAnd/build-common.gradle @@ -106,6 +106,14 @@ android { } } +clean.doFirst { + delete fileTree('assets') { + include '*.obf' + include 'fonts/**' + include 'feature_articles/**' + include 'voice/**' + } +} task validateTranslate { println "Validating translations" @@ -126,9 +134,15 @@ task validateTranslate { } } +task downloadDefaultWikivoyage { + doLast { + ant.get(src: 'https://builder.osmand.net/wikivoyage-gen/sqlite/Default_wikivoyage_2.travel.obf', dest: 'assets/Default_wikivoyage.travel.obf', skipexisting: 'true') + } +} + task downloadWorldMiniBasemap { doLast { - ant.get(src: 'http://builder.osmand.net/basemap/World_basemap_mini_2.obf', dest: 'assets/World_basemap_mini.obf', skipexisting: 'true') + ant.get(src: 'https://builder.osmand.net/basemap/World_basemap_mini_2.obf', dest: 'assets/World_basemap_mini.obf', skipexisting: 'true') } } @@ -254,7 +268,8 @@ task collectExternalResources { copyWidgetIconsHdpi, copyWidgetIconsXhdpi, copyPoiCategories, - downloadWorldMiniBasemap + downloadWorldMiniBasemap, + downloadDefaultWikivoyage } // Legacy core build @@ -358,4 +373,5 @@ dependencies { } implementation 'com.jaredrummler:colorpicker:1.1.0' implementation 'org.bouncycastle:bcpkix-jdk15on:1.56' + implementation 'com.google.android.play:core:1.9.1' } \ No newline at end of file diff --git a/OsmAnd/build-library.gradle b/OsmAnd/build-library.gradle index c282fa6fa2..2104c9e3cb 100644 --- a/OsmAnd/build-library.gradle +++ b/OsmAnd/build-library.gradle @@ -24,6 +24,7 @@ android { // Build that doesn't include 3D OpenGL legacy { dimension "coreversion" + resValue "string", "app_edition", "" } } } @@ -56,5 +57,5 @@ project.afterEvaluate { } dependencies { - implementation 'com.google.android.gms:play-services-location:17.1.0' + implementation 'com.google.android.gms:play-services-location:18.0.0' } diff --git a/OsmAnd/build.gradle b/OsmAnd/build.gradle index 11c9388fa0..b4516d335e 100644 --- a/OsmAnd/build.gradle +++ b/OsmAnd/build.gradle @@ -85,6 +85,7 @@ android { } amazonFree { java.srcDirs = ["src-nogms", "src-google"] + manifest.srcFile "AndroidManifest-gplayFree.xml" } amazonFull { java.srcDirs = ["src-nogms", "src-google"] @@ -186,21 +187,9 @@ dependencies { opengldebugImplementation "net.osmand:OsmAndCore_android:0.1-SNAPSHOT@aar" openglImplementation "net.osmand:OsmAndCore_androidNativeRelease:0.1-SNAPSHOT@aar" openglImplementation "net.osmand:OsmAndCore_android:0.1-SNAPSHOT@aar" - implementation ("com.getkeepsafe.taptargetview:taptargetview:1.12.0"){ - exclude group: 'com.android.support' - } - implementation 'com.github.PhilJay:MPAndroidChart:v3.0.1' - implementation ("com.github.HITGIF:TextFieldBoxes:1.4.5"){ - exclude group: 'com.android.support' - } - implementation('com.github.scribejava:scribejava-apis:7.1.1'){ - exclude group: "com.fasterxml.jackson.core" - } - implementation 'com.jaredrummler:colorpicker:1.1.0' - implementation "org.bouncycastle:bcpkix-jdk15on:1.56" huaweiImplementation 'com.huawei.hms:iap:5.0.2.300' - gplayFreeImplementation 'com.google.android.gms:play-services-location:17.1.0' - gplayFullImplementation 'com.google.android.gms:play-services-location:17.1.0' + gplayFreeImplementation 'com.google.android.gms:play-services-location:18.0.0' + gplayFullImplementation 'com.google.android.gms:play-services-location:18.0.0' } diff --git a/OsmAnd/res/drawable-mdpi/btn_background_active_dark.xml b/OsmAnd/res/drawable-mdpi/btn_background_active_dark.xml new file mode 100644 index 0000000000..88873ec3b4 --- /dev/null +++ b/OsmAnd/res/drawable-mdpi/btn_background_active_dark.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/OsmAnd/res/drawable-mdpi/btn_background_inactive_dark.xml b/OsmAnd/res/drawable-mdpi/btn_background_inactive_dark.xml new file mode 100644 index 0000000000..07c3aed3e9 --- /dev/null +++ b/OsmAnd/res/drawable-mdpi/btn_background_inactive_dark.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/OsmAnd/res/drawable-mdpi/btn_background_inactive_light.xml b/OsmAnd/res/drawable-mdpi/btn_background_inactive_light.xml new file mode 100644 index 0000000000..03ca0abfe5 --- /dev/null +++ b/OsmAnd/res/drawable-mdpi/btn_background_inactive_light.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/OsmAnd/res/drawable-mdpi/btn_background_stroked_active_dark.xml b/OsmAnd/res/drawable-mdpi/btn_background_stroked_active_dark.xml new file mode 100644 index 0000000000..1cdfa50a92 --- /dev/null +++ b/OsmAnd/res/drawable-mdpi/btn_background_stroked_active_dark.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/OsmAnd/res/drawable-mdpi/btn_background_stroked_active_light.xml b/OsmAnd/res/drawable-mdpi/btn_background_stroked_active_light.xml new file mode 100644 index 0000000000..543dd7ef1f --- /dev/null +++ b/OsmAnd/res/drawable-mdpi/btn_background_stroked_active_light.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/OsmAnd/res/drawable-mdpi/btn_background_stroked_inactive_dark.xml b/OsmAnd/res/drawable-mdpi/btn_background_stroked_inactive_dark.xml new file mode 100644 index 0000000000..f669c9d34f --- /dev/null +++ b/OsmAnd/res/drawable-mdpi/btn_background_stroked_inactive_dark.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/OsmAnd/res/drawable-mdpi/btn_background_stroked_inactive_light.xml b/OsmAnd/res/drawable-mdpi/btn_background_stroked_inactive_light.xml new file mode 100644 index 0000000000..feacf12888 --- /dev/null +++ b/OsmAnd/res/drawable-mdpi/btn_background_stroked_inactive_light.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/OsmAnd/res/drawable-xxhdpi/img_plugin_mapillary.webp b/OsmAnd/res/drawable-xxhdpi/img_plugin_mapillary.webp new file mode 100644 index 0000000000..c7ac5d1d25 Binary files /dev/null and b/OsmAnd/res/drawable-xxhdpi/img_plugin_mapillary.webp differ diff --git a/OsmAnd/res/drawable/btn_background_active_light.xml b/OsmAnd/res/drawable/btn_background_active_light.xml new file mode 100644 index 0000000000..4d8d8e82a5 --- /dev/null +++ b/OsmAnd/res/drawable/btn_background_active_light.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/OsmAnd/res/drawable/btn_unstroked_dark.xml b/OsmAnd/res/drawable/btn_unstroked_dark.xml new file mode 100644 index 0000000000..b1debd42fd --- /dev/null +++ b/OsmAnd/res/drawable/btn_unstroked_dark.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/drawable/btn_unstroked_light.xml b/OsmAnd/res/drawable/btn_unstroked_light.xml new file mode 100644 index 0000000000..07db445277 --- /dev/null +++ b/OsmAnd/res/drawable/btn_unstroked_light.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/drawable/ic_action_file_report.xml b/OsmAnd/res/drawable/ic_action_file_report.xml new file mode 100644 index 0000000000..d2da33d3ca --- /dev/null +++ b/OsmAnd/res/drawable/ic_action_file_report.xml @@ -0,0 +1,30 @@ + + + + + + + diff --git a/OsmAnd/res/drawable/ic_action_multi_download.xml b/OsmAnd/res/drawable/ic_action_multi_download.xml new file mode 100644 index 0000000000..13c1b8bcba --- /dev/null +++ b/OsmAnd/res/drawable/ic_action_multi_download.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/OsmAnd/res/drawable/ic_action_ruler_line.xml b/OsmAnd/res/drawable/ic_action_ruler_line.xml new file mode 100644 index 0000000000..0fb6bfc5fa --- /dev/null +++ b/OsmAnd/res/drawable/ic_action_ruler_line.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/OsmAnd/res/drawable/ic_action_update.xml b/OsmAnd/res/drawable/ic_action_update.xml new file mode 100644 index 0000000000..eb6037c6ba --- /dev/null +++ b/OsmAnd/res/drawable/ic_action_update.xml @@ -0,0 +1,12 @@ + + + + diff --git a/OsmAnd/res/layout/bottom_sheet_button_with_icon.xml b/OsmAnd/res/layout/bottom_sheet_button_with_icon.xml new file mode 100644 index 0000000000..7c56093fbc --- /dev/null +++ b/OsmAnd/res/layout/bottom_sheet_button_with_icon.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/bottom_sheet_select_segment.xml b/OsmAnd/res/layout/bottom_sheet_select_segment.xml new file mode 100644 index 0000000000..ee4dfec006 --- /dev/null +++ b/OsmAnd/res/layout/bottom_sheet_select_segment.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/dialog_edit_gpx_description.xml b/OsmAnd/res/layout/dialog_edit_gpx_description.xml index 1d2428f82b..66c0d57f0a 100644 --- a/OsmAnd/res/layout/dialog_edit_gpx_description.xml +++ b/OsmAnd/res/layout/dialog_edit_gpx_description.xml @@ -21,11 +21,13 @@ android:layout_marginLeft="@dimen/card_padding" android:padding="@dimen/context_menu_padding_margin_small" android:paddingStart="@dimen/context_menu_padding_margin_small" - android:paddingEnd="@dimen/context_menu_padding_margin_small"> + android:paddingEnd="@dimen/context_menu_padding_margin_small" + android:background="@null"> @@ -43,18 +45,20 @@ osmand:typeface="@string/font_roboto_medium" /> + android:layout_gravity="center_vertical"> + android:layout_height="match_parent" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginLeft="@dimen/content_padding" + android:layout_marginTop="@dimen/content_padding_half" + android:layout_marginRight="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginBottom="@dimen/content_padding_half"> + android:orientation="vertical"> - + android:layout_height="@dimen/action_bar_height" + android:background="?attr/pstsTabBackground" + android:orientation="horizontal" + android:gravity="center_vertical"> - + - + + + + + + + + + + + + + + android:layout_height="match_parent" + android:background="?attr/activity_background_basic"> + android:visibility="gone" + tools:visibility="visible"> + osmand:typeface="@string/font_roboto_medium" /> diff --git a/OsmAnd/res/layout/favorite_category_dialog_item.xml b/OsmAnd/res/layout/favorite_category_dialog_item.xml index 92aa731363..c5e3e52d7a 100644 --- a/OsmAnd/res/layout/favorite_category_dialog_item.xml +++ b/OsmAnd/res/layout/favorite_category_dialog_item.xml @@ -1,25 +1,27 @@ -