diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 16c27abd73..0000000000 --- a/AUTHORS +++ /dev/null @@ -1,17 +0,0 @@ -Copyright © OsmAnd 2010–2014 -### Credits to all major contributors/developers: - * Victor Shcherb – all parts of the project, originator - * Pavol Zibrita – first contributor and developer of some utilities - * Dusan Kazik – one of the first contributors - * Andre Van Atten – project supporter, active forum participant, one of the first users. - * Dr. Hardy Mueller – map appearance concept and base renderers, international consistency and testing, usability, app scoping, concepts, documentation, wiki, market research. - * Yvecai – main contributor to Contour Lines and Hillshade maps - * Alexey Pelykh – C++ developer, created native library and made application much snappier. - * Max (Zahnstocher) – Java contributor, active forum participant. - * Harry van der Wolf – contributor to country boundaries, configuration files, address files, and much else; active forum participant. - * Robin ‘ypid’ Schneider – opening hours contributor - -### Other Pull requests -Copyright © All authors of translations and pull requests could be found in commits history: - - Translations are under special “contributor” name ‘weblate’ - - Pull requests have two committers, first is original contributor and second is project maintainer diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 0000000000..c418f81d10 --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,7 @@ +### Credits to all major contributors/developers: +Major contributors /developers listed here https://github.com/osmandapp/osmandapp.github.io/blob/master/website/help/about.html#L8 + +### Other Pull requests +Copyright © All authors of translations and pull requests could be found in commits history: + - Translations are under special “contributor” name ‘weblate’ + - Pull requests have two committers, first is original contributor and second is project maintainer diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapTransportReaderAdapter.java b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapTransportReaderAdapter.java index 3cf386843c..3a31b0132a 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapTransportReaderAdapter.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapTransportReaderAdapter.java @@ -5,17 +5,20 @@ import gnu.trove.map.hash.TIntObjectHashMap; import java.io.IOException; import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import net.osmand.binary.BinaryMapIndexReader.SearchRequest; -import net.osmand.binary.OsmandOdb.TransportRouteSchedule; import net.osmand.data.TransportSchedule; import net.osmand.data.TransportStop; +import net.osmand.data.TransportStopExit; import net.osmand.osm.edit.Node; import net.osmand.osm.edit.Way; import net.osmand.util.MapUtils; import net.sf.junidecode.Junidecode; -import com.google.protobuf.ByteString; import com.google.protobuf.CodedInputStream; import com.google.protobuf.WireFormat; @@ -238,6 +241,11 @@ public class BinaryMapTransportReaderAdapter { stringTable.putIfAbsent(i, ""); return ((char) i)+""; } + + private String regStr(TIntObjectHashMap stringTable, int i) throws IOException{ + stringTable.putIfAbsent(i, ""); + return ((char) i)+""; + } public net.osmand.data.TransportRoute getTransportRoute(int filePointer, TIntObjectHashMap stringTable, boolean onlyDescription) throws IOException { @@ -446,12 +454,26 @@ public class BinaryMapTransportReaderAdapter { } protected void initializeNames(TIntObjectHashMap stringTable, TransportStop s) { + for (TransportStopExit exit : s.getExits()) { + if (exit.getRef().length() > 0) { + exit.setRef(stringTable.get(exit.getRef().charAt(0))); + } + } if (s.getName().length() > 0) { s.setName(stringTable.get(s.getName().charAt(0))); } if (s.getEnName(false).length() > 0) { s.setEnName(stringTable.get(s.getEnName(false).charAt(0))); } + Map namesMap = new HashMap<>(s.getNamesMap(false)); + if (!s.getNamesMap(false).isEmpty()) { + s.getNamesMap(false).clear(); + } + Iterator> it = namesMap.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry e = it.next(); + s.setName(stringTable.get(e.getKey().charAt(0)),stringTable.get(e.getValue().charAt(0))); + } } @@ -517,6 +539,7 @@ public class BinaryMapTransportReaderAdapter { TransportStop dataObject = new TransportStop(); dataObject.setLocation(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, x, y); dataObject.setFileOffset(shift); + List names = null; while(true){ int t = codedIS.readTag(); tag = WireFormat.getTagFieldNumber(t); @@ -543,16 +566,76 @@ public class BinaryMapTransportReaderAdapter { } else { skipUnknownField(t); } - + break; + case OsmandOdb.TransportStop.ADDITIONALNAMEPAIRS_FIELD_NUMBER : + if (req.stringTable != null) { + int sizeL = codedIS.readRawVarint32(); + int oldRef = codedIS.pushLimit(sizeL); + while (codedIS.getBytesUntilLimit() > 0) { + dataObject.setName(regStr(req.stringTable,codedIS.readRawVarint32()), + regStr(req.stringTable,codedIS.readRawVarint32())); + } + codedIS.popLimit(oldRef); + } else { + skipUnknownField(t); + } break; case OsmandOdb.TransportStop.ID_FIELD_NUMBER : dataObject.setId(codedIS.readSInt64()); break; + case OsmandOdb.TransportStop.EXITS_FIELD_NUMBER : + int length = codedIS.readRawVarint32(); + int oldLimit = codedIS.pushLimit(length); + + TransportStopExit transportStopExit = readTransportStopExit(cleft, ctop, req); + dataObject.addExit(transportStopExit); + codedIS.popLimit(oldLimit); + break; default: skipUnknownField(t); break; } } } - + + private TransportStopExit readTransportStopExit(int cleft, int ctop, SearchRequest req) throws IOException { + + TransportStopExit dataObject = new TransportStopExit(); + int x = 0; + int y = 0; + + while (true) { + int t = codedIS.readTag(); + int tag = WireFormat.getTagFieldNumber(t); + + switch (tag) { + case 0: + if (dataObject.getName("en").length() == 0) { + dataObject.setEnName(Junidecode.unidecode(dataObject.getName())); + } + if (x != 0 || y != 0) { + dataObject.setLocation(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, x, y); + } + return dataObject; + case OsmandOdb.TransportStopExit.REF_FIELD_NUMBER: + if (req.stringTable != null) { + dataObject.setRef(regStr(req.stringTable)); + } else { + skipUnknownField(t); + } + break; + case OsmandOdb.TransportStopExit.DX_FIELD_NUMBER: + x = codedIS.readSInt32() + cleft; + break; + case OsmandOdb.TransportStopExit.DY_FIELD_NUMBER: + y = codedIS.readSInt32() + ctop; + 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 4a29912436..36237c73d4 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/OsmandOdb.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/OsmandOdb.java @@ -35628,6 +35628,49 @@ public final class OsmandOdb { */ int getNameEn(); + // optional bytes additionalNamePairs = 8; + /** + * optional bytes additionalNamePairs = 8; + * + *
+     * array of bytes (array of pairs <raw var int>)
+     * 
+ */ + boolean hasAdditionalNamePairs(); + /** + * optional bytes additionalNamePairs = 8; + * + *
+     * array of bytes (array of pairs <raw var int>)
+     * 
+ */ + com.google.protobuf.ByteString getAdditionalNamePairs(); + + // repeated .OsmAnd.OBF.TransportStopExit exits = 9; + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + java.util.List + getExitsList(); + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + net.osmand.binary.OsmandOdb.TransportStopExit getExits(int index); + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + int getExitsCount(); + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + java.util.List + getExitsOrBuilderList(); + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + net.osmand.binary.OsmandOdb.TransportStopExitOrBuilder getExitsOrBuilder( + int index); + // repeated uint32 routes = 16; /** * repeated uint32 routes = 16; @@ -35730,10 +35773,23 @@ public final class OsmandOdb { nameEn_ = input.readUInt32(); break; } + case 66: { + bitField0_ |= 0x00000020; + additionalNamePairs_ = input.readBytes(); + break; + } + case 74: { + if (!((mutable_bitField0_ & 0x00000040) == 0x00000040)) { + exits_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000040; + } + exits_.add(input.readMessage(net.osmand.binary.OsmandOdb.TransportStopExit.PARSER, extensionRegistry)); + break; + } case 128: { - if (!((mutable_bitField0_ & 0x00000020) == 0x00000020)) { + if (!((mutable_bitField0_ & 0x00000080) == 0x00000080)) { routes_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000020; + mutable_bitField0_ |= 0x00000080; } routes_.add(input.readUInt32()); break; @@ -35741,9 +35797,9 @@ public final class OsmandOdb { case 130: { int length = input.readRawVarint32(); int limit = input.pushLimit(length); - if (!((mutable_bitField0_ & 0x00000020) == 0x00000020) && input.getBytesUntilLimit() > 0) { + if (!((mutable_bitField0_ & 0x00000080) == 0x00000080) && input.getBytesUntilLimit() > 0) { routes_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000020; + mutable_bitField0_ |= 0x00000080; } while (input.getBytesUntilLimit() > 0) { routes_.add(input.readUInt32()); @@ -35759,7 +35815,10 @@ public final class OsmandOdb { throw new com.google.protobuf.InvalidProtocolBufferException( e.getMessage()).setUnfinishedMessage(this); } finally { - if (((mutable_bitField0_ & 0x00000020) == 0x00000020)) { + if (((mutable_bitField0_ & 0x00000040) == 0x00000040)) { + exits_ = java.util.Collections.unmodifiableList(exits_); + } + if (((mutable_bitField0_ & 0x00000080) == 0x00000080)) { routes_ = java.util.Collections.unmodifiableList(routes_); } this.unknownFields = unknownFields.build(); @@ -35914,6 +35973,66 @@ public final class OsmandOdb { return nameEn_; } + // optional bytes additionalNamePairs = 8; + public static final int ADDITIONALNAMEPAIRS_FIELD_NUMBER = 8; + private com.google.protobuf.ByteString additionalNamePairs_; + /** + * optional bytes additionalNamePairs = 8; + * + *
+     * array of bytes (array of pairs <raw var int>)
+     * 
+ */ + public boolean hasAdditionalNamePairs() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes additionalNamePairs = 8; + * + *
+     * array of bytes (array of pairs <raw var int>)
+     * 
+ */ + public com.google.protobuf.ByteString getAdditionalNamePairs() { + return additionalNamePairs_; + } + + // repeated .OsmAnd.OBF.TransportStopExit exits = 9; + public static final int EXITS_FIELD_NUMBER = 9; + private java.util.List exits_; + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public java.util.List getExitsList() { + return exits_; + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public java.util.List + getExitsOrBuilderList() { + return exits_; + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public int getExitsCount() { + return exits_.size(); + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public net.osmand.binary.OsmandOdb.TransportStopExit getExits(int index) { + return exits_.get(index); + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public net.osmand.binary.OsmandOdb.TransportStopExitOrBuilder getExitsOrBuilder( + int index) { + return exits_.get(index); + } + // repeated uint32 routes = 16; public static final int ROUTES_FIELD_NUMBER = 16; private java.util.List routes_; @@ -35955,6 +36074,8 @@ public final class OsmandOdb { id_ = 0L; name_ = 0; nameEn_ = 0; + additionalNamePairs_ = com.google.protobuf.ByteString.EMPTY; + exits_ = java.util.Collections.emptyList(); routes_ = java.util.Collections.emptyList(); } private byte memoizedIsInitialized = -1; @@ -35978,6 +36099,12 @@ public final class OsmandOdb { memoizedIsInitialized = 0; return false; } + for (int i = 0; i < getExitsCount(); i++) { + if (!getExits(i).isInitialized()) { + memoizedIsInitialized = 0; + return false; + } + } memoizedIsInitialized = 1; return true; } @@ -36000,6 +36127,12 @@ public final class OsmandOdb { if (((bitField0_ & 0x00000010) == 0x00000010)) { output.writeUInt32(7, nameEn_); } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeBytes(8, additionalNamePairs_); + } + for (int i = 0; i < exits_.size(); i++) { + output.writeMessage(9, exits_.get(i)); + } for (int i = 0; i < routes_.size(); i++) { output.writeUInt32(16, routes_.get(i)); } @@ -36032,6 +36165,14 @@ public final class OsmandOdb { size += com.google.protobuf.CodedOutputStream .computeUInt32Size(7, nameEn_); } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(8, additionalNamePairs_); + } + for (int i = 0; i < exits_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(9, exits_.get(i)); + } { int dataSize = 0; for (int i = 0; i < routes_.size(); i++) { @@ -36149,6 +36290,7 @@ public final class OsmandOdb { } private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getExitsFieldBuilder(); } } private static Builder create() { @@ -36167,8 +36309,16 @@ public final class OsmandOdb { bitField0_ = (bitField0_ & ~0x00000008); nameEn_ = 0; bitField0_ = (bitField0_ & ~0x00000010); - routes_ = java.util.Collections.emptyList(); + additionalNamePairs_ = com.google.protobuf.ByteString.EMPTY; bitField0_ = (bitField0_ & ~0x00000020); + if (exitsBuilder_ == null) { + exits_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000040); + } else { + exitsBuilder_.clear(); + } + routes_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000080); return this; } @@ -36217,9 +36367,22 @@ public final class OsmandOdb { to_bitField0_ |= 0x00000010; } result.nameEn_ = nameEn_; - if (((bitField0_ & 0x00000020) == 0x00000020)) { + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.additionalNamePairs_ = additionalNamePairs_; + if (exitsBuilder_ == null) { + if (((bitField0_ & 0x00000040) == 0x00000040)) { + exits_ = java.util.Collections.unmodifiableList(exits_); + bitField0_ = (bitField0_ & ~0x00000040); + } + result.exits_ = exits_; + } else { + result.exits_ = exitsBuilder_.build(); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { routes_ = java.util.Collections.unmodifiableList(routes_); - bitField0_ = (bitField0_ & ~0x00000020); + bitField0_ = (bitField0_ & ~0x00000080); } result.routes_ = routes_; result.bitField0_ = to_bitField0_; @@ -36253,10 +36416,39 @@ public final class OsmandOdb { if (other.hasNameEn()) { setNameEn(other.getNameEn()); } + if (other.hasAdditionalNamePairs()) { + setAdditionalNamePairs(other.getAdditionalNamePairs()); + } + if (exitsBuilder_ == null) { + if (!other.exits_.isEmpty()) { + if (exits_.isEmpty()) { + exits_ = other.exits_; + bitField0_ = (bitField0_ & ~0x00000040); + } else { + ensureExitsIsMutable(); + exits_.addAll(other.exits_); + } + onChanged(); + } + } else { + if (!other.exits_.isEmpty()) { + if (exitsBuilder_.isEmpty()) { + exitsBuilder_.dispose(); + exitsBuilder_ = null; + exits_ = other.exits_; + bitField0_ = (bitField0_ & ~0x00000040); + exitsBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getExitsFieldBuilder() : null; + } else { + exitsBuilder_.addAllMessages(other.exits_); + } + } + } if (!other.routes_.isEmpty()) { if (routes_.isEmpty()) { routes_ = other.routes_; - bitField0_ = (bitField0_ & ~0x00000020); + bitField0_ = (bitField0_ & ~0x00000080); } else { ensureRoutesIsMutable(); routes_.addAll(other.routes_); @@ -36284,6 +36476,12 @@ public final class OsmandOdb { return false; } + for (int i = 0; i < getExitsCount(); i++) { + if (!getExits(i).isInitialized()) { + + return false; + } + } return true; } @@ -36551,12 +36749,304 @@ public final class OsmandOdb { return this; } + // optional bytes additionalNamePairs = 8; + private com.google.protobuf.ByteString additionalNamePairs_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes additionalNamePairs = 8; + * + *
+       * array of bytes (array of pairs <raw var int>)
+       * 
+ */ + public boolean hasAdditionalNamePairs() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes additionalNamePairs = 8; + * + *
+       * array of bytes (array of pairs <raw var int>)
+       * 
+ */ + public com.google.protobuf.ByteString getAdditionalNamePairs() { + return additionalNamePairs_; + } + /** + * optional bytes additionalNamePairs = 8; + * + *
+       * array of bytes (array of pairs <raw var int>)
+       * 
+ */ + public Builder setAdditionalNamePairs(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + additionalNamePairs_ = value; + onChanged(); + return this; + } + /** + * optional bytes additionalNamePairs = 8; + * + *
+       * array of bytes (array of pairs <raw var int>)
+       * 
+ */ + public Builder clearAdditionalNamePairs() { + bitField0_ = (bitField0_ & ~0x00000020); + additionalNamePairs_ = getDefaultInstance().getAdditionalNamePairs(); + onChanged(); + return this; + } + + // repeated .OsmAnd.OBF.TransportStopExit exits = 9; + private java.util.List exits_ = + java.util.Collections.emptyList(); + private void ensureExitsIsMutable() { + if (!((bitField0_ & 0x00000040) == 0x00000040)) { + exits_ = new java.util.ArrayList(exits_); + bitField0_ |= 0x00000040; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + net.osmand.binary.OsmandOdb.TransportStopExit, net.osmand.binary.OsmandOdb.TransportStopExit.Builder, net.osmand.binary.OsmandOdb.TransportStopExitOrBuilder> exitsBuilder_; + + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public java.util.List getExitsList() { + if (exitsBuilder_ == null) { + return java.util.Collections.unmodifiableList(exits_); + } else { + return exitsBuilder_.getMessageList(); + } + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public int getExitsCount() { + if (exitsBuilder_ == null) { + return exits_.size(); + } else { + return exitsBuilder_.getCount(); + } + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public net.osmand.binary.OsmandOdb.TransportStopExit getExits(int index) { + if (exitsBuilder_ == null) { + return exits_.get(index); + } else { + return exitsBuilder_.getMessage(index); + } + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public Builder setExits( + int index, net.osmand.binary.OsmandOdb.TransportStopExit value) { + if (exitsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureExitsIsMutable(); + exits_.set(index, value); + onChanged(); + } else { + exitsBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public Builder setExits( + int index, net.osmand.binary.OsmandOdb.TransportStopExit.Builder builderForValue) { + if (exitsBuilder_ == null) { + ensureExitsIsMutable(); + exits_.set(index, builderForValue.build()); + onChanged(); + } else { + exitsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public Builder addExits(net.osmand.binary.OsmandOdb.TransportStopExit value) { + if (exitsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureExitsIsMutable(); + exits_.add(value); + onChanged(); + } else { + exitsBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public Builder addExits( + int index, net.osmand.binary.OsmandOdb.TransportStopExit value) { + if (exitsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureExitsIsMutable(); + exits_.add(index, value); + onChanged(); + } else { + exitsBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public Builder addExits( + net.osmand.binary.OsmandOdb.TransportStopExit.Builder builderForValue) { + if (exitsBuilder_ == null) { + ensureExitsIsMutable(); + exits_.add(builderForValue.build()); + onChanged(); + } else { + exitsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public Builder addExits( + int index, net.osmand.binary.OsmandOdb.TransportStopExit.Builder builderForValue) { + if (exitsBuilder_ == null) { + ensureExitsIsMutable(); + exits_.add(index, builderForValue.build()); + onChanged(); + } else { + exitsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public Builder addAllExits( + java.lang.Iterable values) { + if (exitsBuilder_ == null) { + ensureExitsIsMutable(); + super.addAll(values, exits_); + onChanged(); + } else { + exitsBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public Builder clearExits() { + if (exitsBuilder_ == null) { + exits_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000040); + onChanged(); + } else { + exitsBuilder_.clear(); + } + return this; + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public Builder removeExits(int index) { + if (exitsBuilder_ == null) { + ensureExitsIsMutable(); + exits_.remove(index); + onChanged(); + } else { + exitsBuilder_.remove(index); + } + return this; + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public net.osmand.binary.OsmandOdb.TransportStopExit.Builder getExitsBuilder( + int index) { + return getExitsFieldBuilder().getBuilder(index); + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public net.osmand.binary.OsmandOdb.TransportStopExitOrBuilder getExitsOrBuilder( + int index) { + if (exitsBuilder_ == null) { + return exits_.get(index); } else { + return exitsBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public java.util.List + getExitsOrBuilderList() { + if (exitsBuilder_ != null) { + return exitsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(exits_); + } + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public net.osmand.binary.OsmandOdb.TransportStopExit.Builder addExitsBuilder() { + return getExitsFieldBuilder().addBuilder( + net.osmand.binary.OsmandOdb.TransportStopExit.getDefaultInstance()); + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public net.osmand.binary.OsmandOdb.TransportStopExit.Builder addExitsBuilder( + int index) { + return getExitsFieldBuilder().addBuilder( + index, net.osmand.binary.OsmandOdb.TransportStopExit.getDefaultInstance()); + } + /** + * repeated .OsmAnd.OBF.TransportStopExit exits = 9; + */ + public java.util.List + getExitsBuilderList() { + return getExitsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + net.osmand.binary.OsmandOdb.TransportStopExit, net.osmand.binary.OsmandOdb.TransportStopExit.Builder, net.osmand.binary.OsmandOdb.TransportStopExitOrBuilder> + getExitsFieldBuilder() { + if (exitsBuilder_ == null) { + exitsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + net.osmand.binary.OsmandOdb.TransportStopExit, net.osmand.binary.OsmandOdb.TransportStopExit.Builder, net.osmand.binary.OsmandOdb.TransportStopExitOrBuilder>( + exits_, + ((bitField0_ & 0x00000040) == 0x00000040), + getParentForChildren(), + isClean()); + exits_ = null; + } + return exitsBuilder_; + } + // repeated uint32 routes = 16; private java.util.List routes_ = java.util.Collections.emptyList(); private void ensureRoutesIsMutable() { - if (!((bitField0_ & 0x00000020) == 0x00000020)) { + if (!((bitField0_ & 0x00000080) == 0x00000080)) { routes_ = new java.util.ArrayList(routes_); - bitField0_ |= 0x00000020; + bitField0_ |= 0x00000080; } } /** @@ -36640,7 +37130,7 @@ public final class OsmandOdb { */ public Builder clearRoutes() { routes_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000020); + bitField0_ = (bitField0_ & ~0x00000080); onChanged(); return this; } @@ -36656,6 +37146,652 @@ public final class OsmandOdb { // @@protoc_insertion_point(class_scope:OsmAnd.OBF.TransportStop) } + public interface TransportStopExitOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // required sint32 dx = 1; + /** + * required sint32 dx = 1; + * + *
+     * delta x
+     * 
+ */ + boolean hasDx(); + /** + * required sint32 dx = 1; + * + *
+     * delta x
+     * 
+ */ + int getDx(); + + // required sint32 dy = 2; + /** + * required sint32 dy = 2; + * + *
+     * delta y
+     * 
+ */ + boolean hasDy(); + /** + * required sint32 dy = 2; + * + *
+     * delta y
+     * 
+ */ + int getDy(); + + // required uint32 ref = 3; + /** + * required uint32 ref = 3; + */ + boolean hasRef(); + /** + * required uint32 ref = 3; + */ + int getRef(); + } + /** + * Protobuf type {@code OsmAnd.OBF.TransportStopExit} + */ + public static final class TransportStopExit extends + com.google.protobuf.GeneratedMessage + implements TransportStopExitOrBuilder { + // Use TransportStopExit.newBuilder() to construct. + private TransportStopExit(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private TransportStopExit(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final TransportStopExit defaultInstance; + public static TransportStopExit getDefaultInstance() { + return defaultInstance; + } + + public TransportStopExit getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private TransportStopExit( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + dx_ = input.readSInt32(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + dy_ = input.readSInt32(); + break; + } + case 24: { + bitField0_ |= 0x00000004; + ref_ = input.readUInt32(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return net.osmand.binary.OsmandOdb.internal_static_OsmAnd_OBF_TransportStopExit_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return net.osmand.binary.OsmandOdb.internal_static_OsmAnd_OBF_TransportStopExit_fieldAccessorTable + .ensureFieldAccessorsInitialized( + net.osmand.binary.OsmandOdb.TransportStopExit.class, net.osmand.binary.OsmandOdb.TransportStopExit.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public TransportStopExit parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new TransportStopExit(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // required sint32 dx = 1; + public static final int DX_FIELD_NUMBER = 1; + private int dx_; + /** + * required sint32 dx = 1; + * + *
+     * delta x
+     * 
+ */ + public boolean hasDx() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * required sint32 dx = 1; + * + *
+     * delta x
+     * 
+ */ + public int getDx() { + return dx_; + } + + // required sint32 dy = 2; + public static final int DY_FIELD_NUMBER = 2; + private int dy_; + /** + * required sint32 dy = 2; + * + *
+     * delta y
+     * 
+ */ + public boolean hasDy() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * required sint32 dy = 2; + * + *
+     * delta y
+     * 
+ */ + public int getDy() { + return dy_; + } + + // required uint32 ref = 3; + public static final int REF_FIELD_NUMBER = 3; + private int ref_; + /** + * required uint32 ref = 3; + */ + public boolean hasRef() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * required uint32 ref = 3; + */ + public int getRef() { + return ref_; + } + + private void initFields() { + dx_ = 0; + dy_ = 0; + ref_ = 0; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + if (!hasDx()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasDy()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasRef()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeSInt32(1, dx_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeSInt32(2, dy_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeUInt32(3, ref_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeSInt32Size(1, dx_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeSInt32Size(2, dy_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(3, ref_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static net.osmand.binary.OsmandOdb.TransportStopExit parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static net.osmand.binary.OsmandOdb.TransportStopExit parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static net.osmand.binary.OsmandOdb.TransportStopExit parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static net.osmand.binary.OsmandOdb.TransportStopExit parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static net.osmand.binary.OsmandOdb.TransportStopExit parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static net.osmand.binary.OsmandOdb.TransportStopExit parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static net.osmand.binary.OsmandOdb.TransportStopExit parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static net.osmand.binary.OsmandOdb.TransportStopExit parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static net.osmand.binary.OsmandOdb.TransportStopExit parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static net.osmand.binary.OsmandOdb.TransportStopExit parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(net.osmand.binary.OsmandOdb.TransportStopExit prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code OsmAnd.OBF.TransportStopExit} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements net.osmand.binary.OsmandOdb.TransportStopExitOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return net.osmand.binary.OsmandOdb.internal_static_OsmAnd_OBF_TransportStopExit_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return net.osmand.binary.OsmandOdb.internal_static_OsmAnd_OBF_TransportStopExit_fieldAccessorTable + .ensureFieldAccessorsInitialized( + net.osmand.binary.OsmandOdb.TransportStopExit.class, net.osmand.binary.OsmandOdb.TransportStopExit.Builder.class); + } + + // Construct using net.osmand.binary.OsmandOdb.TransportStopExit.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + dx_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + dy_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + ref_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return net.osmand.binary.OsmandOdb.internal_static_OsmAnd_OBF_TransportStopExit_descriptor; + } + + public net.osmand.binary.OsmandOdb.TransportStopExit getDefaultInstanceForType() { + return net.osmand.binary.OsmandOdb.TransportStopExit.getDefaultInstance(); + } + + public net.osmand.binary.OsmandOdb.TransportStopExit build() { + net.osmand.binary.OsmandOdb.TransportStopExit result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public net.osmand.binary.OsmandOdb.TransportStopExit buildPartial() { + net.osmand.binary.OsmandOdb.TransportStopExit result = new net.osmand.binary.OsmandOdb.TransportStopExit(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.dx_ = dx_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.dy_ = dy_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.ref_ = ref_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof net.osmand.binary.OsmandOdb.TransportStopExit) { + return mergeFrom((net.osmand.binary.OsmandOdb.TransportStopExit)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(net.osmand.binary.OsmandOdb.TransportStopExit other) { + if (other == net.osmand.binary.OsmandOdb.TransportStopExit.getDefaultInstance()) return this; + if (other.hasDx()) { + setDx(other.getDx()); + } + if (other.hasDy()) { + setDy(other.getDy()); + } + if (other.hasRef()) { + setRef(other.getRef()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + if (!hasDx()) { + + return false; + } + if (!hasDy()) { + + return false; + } + if (!hasRef()) { + + return false; + } + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + net.osmand.binary.OsmandOdb.TransportStopExit parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (net.osmand.binary.OsmandOdb.TransportStopExit) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // required sint32 dx = 1; + private int dx_ ; + /** + * required sint32 dx = 1; + * + *
+       * delta x
+       * 
+ */ + public boolean hasDx() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * required sint32 dx = 1; + * + *
+       * delta x
+       * 
+ */ + public int getDx() { + return dx_; + } + /** + * required sint32 dx = 1; + * + *
+       * delta x
+       * 
+ */ + public Builder setDx(int value) { + bitField0_ |= 0x00000001; + dx_ = value; + onChanged(); + return this; + } + /** + * required sint32 dx = 1; + * + *
+       * delta x
+       * 
+ */ + public Builder clearDx() { + bitField0_ = (bitField0_ & ~0x00000001); + dx_ = 0; + onChanged(); + return this; + } + + // required sint32 dy = 2; + private int dy_ ; + /** + * required sint32 dy = 2; + * + *
+       * delta y
+       * 
+ */ + public boolean hasDy() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * required sint32 dy = 2; + * + *
+       * delta y
+       * 
+ */ + public int getDy() { + return dy_; + } + /** + * required sint32 dy = 2; + * + *
+       * delta y
+       * 
+ */ + public Builder setDy(int value) { + bitField0_ |= 0x00000002; + dy_ = value; + onChanged(); + return this; + } + /** + * required sint32 dy = 2; + * + *
+       * delta y
+       * 
+ */ + public Builder clearDy() { + bitField0_ = (bitField0_ & ~0x00000002); + dy_ = 0; + onChanged(); + return this; + } + + // required uint32 ref = 3; + private int ref_ ; + /** + * required uint32 ref = 3; + */ + public boolean hasRef() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * required uint32 ref = 3; + */ + public int getRef() { + return ref_; + } + /** + * required uint32 ref = 3; + */ + public Builder setRef(int value) { + bitField0_ |= 0x00000004; + ref_ = value; + onChanged(); + return this; + } + /** + * required uint32 ref = 3; + */ + public Builder clearRef() { + bitField0_ = (bitField0_ & ~0x00000004); + ref_ = 0; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:OsmAnd.OBF.TransportStopExit) + } + + static { + defaultInstance = new TransportStopExit(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:OsmAnd.OBF.TransportStopExit) + } + public interface TransportStopsTreeOrBuilder extends com.google.protobuf.MessageOrBuilder { @@ -61027,6 +62163,11 @@ public final class OsmandOdb { private static com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_OsmAnd_OBF_TransportStop_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_OsmAnd_OBF_TransportStopExit_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_OsmAnd_OBF_TransportStopExit_fieldAccessorTable; private static com.google.protobuf.Descriptors.Descriptor internal_static_OsmAnd_OBF_TransportStopsTree_descriptor; private static @@ -61240,80 +62381,84 @@ public final class OsmandOdb { "ayOfWeekRestriction\030\007 \003(\r\022\034\n\024dayOfYearRe" + "striction\030\010 \003(\r\"W\n\022TransportRouteStop\022\n\n" + "\002id\030\001 \002(\022\022\n\n\002dx\030\002 \002(\021\022\n\n\002dy\030\003 \002(\021\022\014\n\004nam" + - "e\030\006 \002(\r\022\017\n\007name_en\030\007 \001(\r\"b\n\rTransportSto" + - "p\022\n\n\002dx\030\001 \002(\021\022\n\n\002dy\030\002 \002(\021\022\n\n\002id\030\005 \002(\022\022\014\n" + - "\004name\030\006 \002(\r\022\017\n\007name_en\030\007 \001(\r\022\016\n\006routes\030\020" + - " \003(\r\"\272\001\n\022TransportStopsTree\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\0220\n\010subtrees\030\007 \003(\0132\036.OsmAnd.OBF.Tra", - "nsportStopsTree\022(\n\005leafs\030\010 \003(\0132\031.OsmAnd." + - "OBF.TransportStop\022\016\n\006baseId\030\020 \001(\004\"\256\001\n\024Os" + - "mAndTransportIndex\022\014\n\004name\030\001 \001(\t\022+\n\006rout" + - "es\030\003 \001(\0132\033.OsmAnd.OBF.TransportRoutes\022-\n" + - "\005stops\030\006 \001(\0132\036.OsmAnd.OBF.TransportStops" + - "Tree\022,\n\013stringTable\030\t \002(\0132\027.OsmAnd.OBF.S" + - "tringTable\"\312\002\n\016OsmAndPoiIndex\022\014\n\004name\030\001 " + - "\002(\t\022-\n\nboundaries\030\002 \002(\0132\031.OsmAnd.OBF.Osm" + - "AndTileBox\0228\n\017categoriesTable\030\003 \003(\0132\037.Os" + - "mAnd.OBF.OsmAndCategoryTable\0221\n\tnameInde", - "x\030\004 \001(\0132\036.OsmAnd.OBF.OsmAndPoiNameIndex\022" + - "6\n\rsubtypesTable\030\005 \001(\0132\037.OsmAnd.OBF.OsmA" + - "ndSubtypesTable\022\'\n\005boxes\030\006 \003(\0132\030.OsmAnd." + - "OBF.OsmAndPoiBox\022-\n\007poiData\030\t \003(\0132\034.OsmA" + - "nd.OBF.OsmAndPoiBoxData\"\331\001\n\022OsmAndPoiNam" + - "eIndex\022-\n\005table\030\003 \002(\0132\036.OsmAnd.OBF.Index" + - "edStringTable\022C\n\004data\030\005 \003(\01325.OsmAnd.OBF" + - ".OsmAndPoiNameIndex.OsmAndPoiNameIndexDa" + - "ta\032O\n\026OsmAndPoiNameIndexData\0225\n\005atoms\030\003 " + - "\003(\0132&.OsmAnd.OBF.OsmAndPoiNameIndexDataA", - "tom\"Q\n\032OsmAndPoiNameIndexDataAtom\022\014\n\004zoo" + - "m\030\002 \001(\r\022\t\n\001x\030\003 \001(\r\022\t\n\001y\030\004 \001(\r\022\017\n\007shiftTo" + - "\030\016 \001(\007\">\n\023OsmAndCategoryTable\022\020\n\010categor" + - "y\030\001 \002(\t\022\025\n\rsubcategories\030\003 \003(\t\"E\n\023OsmAnd" + - "SubtypesTable\022.\n\010subtypes\030\004 \003(\0132\034.OsmAnd" + - ".OBF.OsmAndPoiSubtype\"\205\001\n\020OsmAndPoiSubty" + - "pe\022\014\n\004name\030\001 \002(\t\022\017\n\007tagname\030\002 \001(\t\022\016\n\006isT" + - "ext\030\003 \002(\010\022\021\n\tfrequency\030\005 \001(\r\022\031\n\021subtypeV" + - "aluesSize\030\006 \001(\r\022\024\n\014subtypeValue\030\010 \003(\t\"\255\001" + - "\n\014OsmAndPoiBox\022\014\n\004zoom\030\001 \002(\r\022\014\n\004left\030\002 \002", - "(\021\022\013\n\003top\030\003 \002(\021\0223\n\ncategories\030\004 \001(\0132\037.Os" + - "mAnd.OBF.OsmAndPoiCategories\022*\n\010subBoxes" + - "\030\n \003(\0132\030.OsmAnd.OBF.OsmAndPoiBox\022\023\n\013shif" + - "tToData\030\016 \001(\007\"@\n\023OsmAndPoiCategories\022\022\n\n" + - "categories\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 .OsmAn" + - "d.OBF.OsmAndPoiBoxDataAtom\"\360\001\n\024OsmAndPoi" + - "BoxDataAtom\022\n\n\002dx\030\002 \002(\021\022\n\n\002dy\030\003 \002(\021\022\022\n\nc" + - "ategories\030\004 \003(\r\022\025\n\rsubcategories\030\005 \003(\r\022\014", - "\n\004name\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\005" + - "phone\030\014 \001(\t\022\014\n\004note\030\r \001(\t\022\026\n\016textCategor" + - "ies\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\004" + - "type\030\001 \002(\005\022\014\n\004from\030\002 \002(\005\022\n\n\002to\030\003 \002(\005\022\013\n\003" + - "via\030\004 \001(\005\"x\n\tRouteData\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\007routeId\030\014 \002(\005\022\023\n\013string" + - "Names\030\016 \001(\014\"\304\005\n\022OsmAndRoutingIndex\022\014\n\004na", - "me\030\001 \002(\t\022?\n\005rules\030\002 \003(\01320.OsmAnd.OBF.Osm" + - "AndRoutingIndex.RouteEncodingRule\022>\n\troo" + - "tBoxes\030\003 \003(\0132+.OsmAnd.OBF.OsmAndRoutingI" + - "ndex.RouteDataBox\022A\n\014basemapBoxes\030\004 \003(\0132" + - "+.OsmAnd.OBF.OsmAndRoutingIndex.RouteDat" + - "aBox\022=\n\006blocks\030\005 \003(\0132-.OsmAnd.OBF.OsmAnd" + - "RoutingIndex.RouteDataBlock\032;\n\021RouteEnco" + - "dingRule\022\013\n\003tag\030\003 \002(\t\022\r\n\005value\030\005 \002(\t\022\n\n\002" + - "id\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.RouteDat" + - "aBox\032\303\001\n\016RouteDataBlock\022$\n\007idTable\030\005 \001(\013" + - "2\023.OsmAnd.OBF.IdTable\022*\n\013dataObjects\030\006 \003" + - "(\0132\025.OsmAnd.OBF.RouteData\0221\n\014restriction" + - "s\030\007 \003(\0132\033.OsmAnd.OBF.RestrictionData\022,\n\013" + - "stringTable\030\010 \001(\0132\027.OsmAnd.OBF.StringTab" + - "leB\036\n\021net.osmand.binaryB\tOsmandOdb" + "e\030\006 \002(\r\022\017\n\007name_en\030\007 \001(\r\"\255\001\n\rTransportSt" + + "op\022\n\n\002dx\030\001 \002(\021\022\n\n\002dy\030\002 \002(\021\022\n\n\002id\030\005 \002(\022\022\014" + + "\n\004name\030\006 \002(\r\022\017\n\007name_en\030\007 \001(\r\022\033\n\023additio" + + "nalNamePairs\030\010 \001(\014\022,\n\005exits\030\t \003(\0132\035.OsmA" + + "nd.OBF.TransportStopExit\022\016\n\006routes\030\020 \003(\r" + + "\"8\n\021TransportStopExit\022\n\n\002dx\030\001 \002(\021\022\n\n\002dy\030", + "\002 \002(\021\022\013\n\003ref\030\003 \002(\r\"\272\001\n\022TransportStopsTre" + + "e\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\0220\n\010subtrees\030\007 \003(\0132\036." + + "OsmAnd.OBF.TransportStopsTree\022(\n\005leafs\030\010" + + " \003(\0132\031.OsmAnd.OBF.TransportStop\022\016\n\006baseI" + + "d\030\020 \001(\004\"\256\001\n\024OsmAndTransportIndex\022\014\n\004name" + + "\030\001 \001(\t\022+\n\006routes\030\003 \001(\0132\033.OsmAnd.OBF.Tran" + + "sportRoutes\022-\n\005stops\030\006 \001(\0132\036.OsmAnd.OBF." + + "TransportStopsTree\022,\n\013stringTable\030\t \002(\0132" + + "\027.OsmAnd.OBF.StringTable\"\312\002\n\016OsmAndPoiIn", + "dex\022\014\n\004name\030\001 \002(\t\022-\n\nboundaries\030\002 \002(\0132\031." + + "OsmAnd.OBF.OsmAndTileBox\0228\n\017categoriesTa" + + "ble\030\003 \003(\0132\037.OsmAnd.OBF.OsmAndCategoryTab" + + "le\0221\n\tnameIndex\030\004 \001(\0132\036.OsmAnd.OBF.OsmAn" + + "dPoiNameIndex\0226\n\rsubtypesTable\030\005 \001(\0132\037.O" + + "smAnd.OBF.OsmAndSubtypesTable\022\'\n\005boxes\030\006" + + " \003(\0132\030.OsmAnd.OBF.OsmAndPoiBox\022-\n\007poiDat" + + "a\030\t \003(\0132\034.OsmAnd.OBF.OsmAndPoiBoxData\"\331\001" + + "\n\022OsmAndPoiNameIndex\022-\n\005table\030\003 \002(\0132\036.Os" + + "mAnd.OBF.IndexedStringTable\022C\n\004data\030\005 \003(", + "\01325.OsmAnd.OBF.OsmAndPoiNameIndex.OsmAnd" + + "PoiNameIndexData\032O\n\026OsmAndPoiNameIndexDa" + + "ta\0225\n\005atoms\030\003 \003(\0132&.OsmAnd.OBF.OsmAndPoi" + + "NameIndexDataAtom\"Q\n\032OsmAndPoiNameIndexD" + + "ataAtom\022\014\n\004zoom\030\002 \001(\r\022\t\n\001x\030\003 \001(\r\022\t\n\001y\030\004 " + + "\001(\r\022\017\n\007shiftTo\030\016 \001(\007\">\n\023OsmAndCategoryTa" + + "ble\022\020\n\010category\030\001 \002(\t\022\025\n\rsubcategories\030\003" + + " \003(\t\"E\n\023OsmAndSubtypesTable\022.\n\010subtypes\030" + + "\004 \003(\0132\034.OsmAnd.OBF.OsmAndPoiSubtype\"\205\001\n\020" + + "OsmAndPoiSubtype\022\014\n\004name\030\001 \002(\t\022\017\n\007tagnam", + "e\030\002 \001(\t\022\016\n\006isText\030\003 \002(\010\022\021\n\tfrequency\030\005 \001" + + "(\r\022\031\n\021subtypeValuesSize\030\006 \001(\r\022\024\n\014subtype" + + "Value\030\010 \003(\t\"\255\001\n\014OsmAndPoiBox\022\014\n\004zoom\030\001 \002" + + "(\r\022\014\n\004left\030\002 \002(\021\022\013\n\003top\030\003 \002(\021\0223\n\ncategor" + + "ies\030\004 \001(\0132\037.OsmAnd.OBF.OsmAndPoiCategori" + + "es\022*\n\010subBoxes\030\n \003(\0132\030.OsmAnd.OBF.OsmAnd" + + "PoiBox\022\023\n\013shiftToData\030\016 \001(\007\"@\n\023OsmAndPoi" + + "Categories\022\022\n\ncategories\030\003 \003(\r\022\025\n\rsubcat" + + "egories\030\005 \003(\r\"i\n\020OsmAndPoiBoxData\022\014\n\004zoo" + + "m\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\024OsmAndPoiBoxDataAtom\022\n\n\002dx\030\002 \002(\021\022\n\n" + + "\002dy\030\003 \002(\021\022\022\n\ncategories\030\004 \003(\r\022\025\n\rsubcate" + + "gories\030\005 \003(\r\022\014\n\004name\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\004" + + "site\030\013 \001(\t\022\r\n\005phone\030\014 \001(\t\022\014\n\004note\030\r \001(\t\022" + + "\026\n\016textCategories\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\017Restr" + + "ictionData\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\tRouteData\022\016\n\006", + "points\030\001 \002(\014\022\022\n\npointTypes\030\004 \001(\014\022\022\n\npoin" + + "tNames\030\005 \001(\014\022\r\n\005types\030\007 \002(\014\022\017\n\007routeId\030\014" + + " \002(\005\022\023\n\013stringNames\030\016 \001(\014\"\304\005\n\022OsmAndRout" + + "ingIndex\022\014\n\004name\030\001 \002(\t\022?\n\005rules\030\002 \003(\01320." + + "OsmAnd.OBF.OsmAndRoutingIndex.RouteEncod" + + "ingRule\022>\n\trootBoxes\030\003 \003(\0132+.OsmAnd.OBF." + + "OsmAndRoutingIndex.RouteDataBox\022A\n\014basem" + + "apBoxes\030\004 \003(\0132+.OsmAnd.OBF.OsmAndRouting" + + "Index.RouteDataBox\022=\n\006blocks\030\005 \003(\0132-.Osm" + + "And.OBF.OsmAndRoutingIndex.RouteDataBloc", + "k\032;\n\021RouteEncodingRule\022\013\n\003tag\030\003 \002(\t\022\r\n\005v" + + "alue\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.OsmAndRouting" + + "Index.RouteDataBox\032\303\001\n\016RouteDataBlock\022$\n" + + "\007idTable\030\005 \001(\0132\023.OsmAnd.OBF.IdTable\022*\n\013d" + + "ataObjects\030\006 \003(\0132\025.OsmAnd.OBF.RouteData\022" + + "1\n\014restrictions\030\007 \003(\0132\033.OsmAnd.OBF.Restr" + + "ictionData\022,\n\013stringTable\030\010 \001(\0132\027.OsmAnd", + ".OBF.StringTableB\036\n\021net.osmand.binaryB\tO" + + "smandOdb" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -61475,27 +62620,33 @@ public final class OsmandOdb { internal_static_OsmAnd_OBF_TransportStop_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_TransportStop_descriptor, - new java.lang.String[] { "Dx", "Dy", "Id", "Name", "NameEn", "Routes", }); - internal_static_OsmAnd_OBF_TransportStopsTree_descriptor = + new java.lang.String[] { "Dx", "Dy", "Id", "Name", "NameEn", "AdditionalNamePairs", "Exits", "Routes", }); + internal_static_OsmAnd_OBF_TransportStopExit_descriptor = getDescriptor().getMessageTypes().get(21); + internal_static_OsmAnd_OBF_TransportStopExit_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_OsmAnd_OBF_TransportStopExit_descriptor, + new java.lang.String[] { "Dx", "Dy", "Ref", }); + internal_static_OsmAnd_OBF_TransportStopsTree_descriptor = + getDescriptor().getMessageTypes().get(22); internal_static_OsmAnd_OBF_TransportStopsTree_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_TransportStopsTree_descriptor, new java.lang.String[] { "Left", "Right", "Top", "Bottom", "Subtrees", "Leafs", "BaseId", }); internal_static_OsmAnd_OBF_OsmAndTransportIndex_descriptor = - getDescriptor().getMessageTypes().get(22); + getDescriptor().getMessageTypes().get(23); internal_static_OsmAnd_OBF_OsmAndTransportIndex_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_OsmAndTransportIndex_descriptor, new java.lang.String[] { "Name", "Routes", "Stops", "StringTable", }); internal_static_OsmAnd_OBF_OsmAndPoiIndex_descriptor = - getDescriptor().getMessageTypes().get(23); + getDescriptor().getMessageTypes().get(24); internal_static_OsmAnd_OBF_OsmAndPoiIndex_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_OsmAndPoiIndex_descriptor, new java.lang.String[] { "Name", "Boundaries", "CategoriesTable", "NameIndex", "SubtypesTable", "Boxes", "PoiData", }); internal_static_OsmAnd_OBF_OsmAndPoiNameIndex_descriptor = - getDescriptor().getMessageTypes().get(24); + getDescriptor().getMessageTypes().get(25); internal_static_OsmAnd_OBF_OsmAndPoiNameIndex_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_OsmAndPoiNameIndex_descriptor, @@ -61507,73 +62658,73 @@ public final class OsmandOdb { internal_static_OsmAnd_OBF_OsmAndPoiNameIndex_OsmAndPoiNameIndexData_descriptor, new java.lang.String[] { "Atoms", }); internal_static_OsmAnd_OBF_OsmAndPoiNameIndexDataAtom_descriptor = - getDescriptor().getMessageTypes().get(25); + getDescriptor().getMessageTypes().get(26); internal_static_OsmAnd_OBF_OsmAndPoiNameIndexDataAtom_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_OsmAndPoiNameIndexDataAtom_descriptor, new java.lang.String[] { "Zoom", "X", "Y", "ShiftTo", }); internal_static_OsmAnd_OBF_OsmAndCategoryTable_descriptor = - getDescriptor().getMessageTypes().get(26); + getDescriptor().getMessageTypes().get(27); internal_static_OsmAnd_OBF_OsmAndCategoryTable_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_OsmAndCategoryTable_descriptor, new java.lang.String[] { "Category", "Subcategories", }); internal_static_OsmAnd_OBF_OsmAndSubtypesTable_descriptor = - getDescriptor().getMessageTypes().get(27); + getDescriptor().getMessageTypes().get(28); internal_static_OsmAnd_OBF_OsmAndSubtypesTable_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_OsmAndSubtypesTable_descriptor, new java.lang.String[] { "Subtypes", }); internal_static_OsmAnd_OBF_OsmAndPoiSubtype_descriptor = - getDescriptor().getMessageTypes().get(28); + getDescriptor().getMessageTypes().get(29); internal_static_OsmAnd_OBF_OsmAndPoiSubtype_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_OsmAndPoiSubtype_descriptor, new java.lang.String[] { "Name", "Tagname", "IsText", "Frequency", "SubtypeValuesSize", "SubtypeValue", }); internal_static_OsmAnd_OBF_OsmAndPoiBox_descriptor = - getDescriptor().getMessageTypes().get(29); + getDescriptor().getMessageTypes().get(30); internal_static_OsmAnd_OBF_OsmAndPoiBox_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_OsmAndPoiBox_descriptor, new java.lang.String[] { "Zoom", "Left", "Top", "Categories", "SubBoxes", "ShiftToData", }); internal_static_OsmAnd_OBF_OsmAndPoiCategories_descriptor = - getDescriptor().getMessageTypes().get(30); + getDescriptor().getMessageTypes().get(31); internal_static_OsmAnd_OBF_OsmAndPoiCategories_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_OsmAndPoiCategories_descriptor, new java.lang.String[] { "Categories", "Subcategories", }); internal_static_OsmAnd_OBF_OsmAndPoiBoxData_descriptor = - getDescriptor().getMessageTypes().get(31); + getDescriptor().getMessageTypes().get(32); internal_static_OsmAnd_OBF_OsmAndPoiBoxData_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_OsmAndPoiBoxData_descriptor, new java.lang.String[] { "Zoom", "X", "Y", "PoiData", }); internal_static_OsmAnd_OBF_OsmAndPoiBoxDataAtom_descriptor = - getDescriptor().getMessageTypes().get(32); + getDescriptor().getMessageTypes().get(33); 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", }); internal_static_OsmAnd_OBF_IdTable_descriptor = - getDescriptor().getMessageTypes().get(33); + getDescriptor().getMessageTypes().get(34); internal_static_OsmAnd_OBF_IdTable_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_IdTable_descriptor, new java.lang.String[] { "RouteId", }); internal_static_OsmAnd_OBF_RestrictionData_descriptor = - getDescriptor().getMessageTypes().get(34); + getDescriptor().getMessageTypes().get(35); internal_static_OsmAnd_OBF_RestrictionData_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_RestrictionData_descriptor, new java.lang.String[] { "Type", "From", "To", "Via", }); internal_static_OsmAnd_OBF_RouteData_descriptor = - getDescriptor().getMessageTypes().get(35); + getDescriptor().getMessageTypes().get(36); internal_static_OsmAnd_OBF_RouteData_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_RouteData_descriptor, new java.lang.String[] { "Points", "PointTypes", "PointNames", "Types", "RouteId", "StringNames", }); internal_static_OsmAnd_OBF_OsmAndRoutingIndex_descriptor = - getDescriptor().getMessageTypes().get(36); + getDescriptor().getMessageTypes().get(37); internal_static_OsmAnd_OBF_OsmAndRoutingIndex_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OsmAnd_OBF_OsmAndRoutingIndex_descriptor, diff --git a/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java b/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java index 6df662561e..f59fb006ab 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java @@ -77,6 +77,15 @@ public abstract class MapObject implements Comparable { } } + public void setNames(Map name) { + if (name != null) { + if (names == null) { + names = new HashMap(); + } + names.putAll(name); + } + } + public Map getNamesMap(boolean includeEn) { if (!includeEn || Algorithms.isEmpty(enName)) { if (names == null) { diff --git a/OsmAnd-java/src/main/java/net/osmand/data/TransportStop.java b/OsmAnd-java/src/main/java/net/osmand/data/TransportStop.java index 981b182a63..eeaad172cf 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/TransportStop.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/TransportStop.java @@ -2,13 +2,20 @@ package net.osmand.data; import net.osmand.util.MapUtils; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + public class TransportStop extends MapObject { private int[] referencesToRoutes = null; private Amenity amenity; public int distance; public int x31; public int y31; - + private List exits; + private HashMap names; public TransportStop(){ } @@ -39,4 +46,35 @@ public class TransportStop extends MapObject { y31 = dy << (31 - zoom); setLocation(MapUtils.getLatitudeFromTile(zoom, dy), MapUtils.getLongitudeFromTile(zoom, dx)); } + + public void addExit(TransportStopExit transportStopExit) { + if (exits == null) { + exits = new ArrayList<>(); + } + exits.add(transportStopExit); + } + + public List getExits () { + if (exits == null) { + return Collections.emptyList(); + } + return this.exits; + } + + public String getExitsString () { + String exitsString = ""; + String refString = ""; + if (this.exits != null) { + int i = 1; + exitsString = exitsString + " Exits: ["; + for (TransportStopExit e : this.exits ) { + if (e.getRef() != null) { + refString = " [ref:" + e.getRef() + "] "; + } + exitsString = exitsString + " " + i + ")" + refString + e.getName() + " " + e.getLocation() + " ]"; + i++; + } + } + return exitsString; + } } diff --git a/OsmAnd-java/src/main/java/net/osmand/data/TransportStopExit.java b/OsmAnd-java/src/main/java/net/osmand/data/TransportStopExit.java new file mode 100644 index 0000000000..2c4d6a2a68 --- /dev/null +++ b/OsmAnd-java/src/main/java/net/osmand/data/TransportStopExit.java @@ -0,0 +1,27 @@ +package net.osmand.data; + +import net.osmand.util.MapUtils; + +public class TransportStopExit extends MapObject { + public int x31; + public int y31; + public String ref = null; + @Override + public void setLocation(double latitude, double longitude) { + super.setLocation(latitude, longitude); + } + public void setLocation(int zoom, int dx, int dy) { + x31 = dx << (31 - zoom); + y31 = dy << (31 - zoom); + setLocation(MapUtils.getLatitudeFromTile(zoom, dy), MapUtils.getLongitudeFromTile(zoom, dx)); + } + public void setRef (String ref) { + this.ref = ref; + } + public String getRef() { + if (ref != null) { + return ref; + } + return ""; + } +} diff --git a/OsmAnd-java/src/main/java/net/osmand/osm/edit/OSMSettings.java b/OsmAnd-java/src/main/java/net/osmand/osm/edit/OSMSettings.java index fd506c7291..87b9c755a1 100644 --- a/OsmAnd-java/src/main/java/net/osmand/osm/edit/OSMSettings.java +++ b/OsmAnd-java/src/main/java/net/osmand/osm/edit/OSMSettings.java @@ -13,6 +13,7 @@ public class OSMSettings { BOUNDARY("boundary"), //$NON-NLS-1$ POSTAL_CODE("postal_code"), //$NON-NLS-1$ RAILWAY("railway"), //$NON-NLS-1$ + STATION("subway"), //$NON-NLS-1$ ONEWAY("oneway"), //$NON-NLS-1$ LAYER("layer"), //$NON-NLS-1$ BRIDGE("bridge"), //$NON-NLS-1$ diff --git a/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java b/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java index d93665ffca..91abce36d8 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java @@ -28,7 +28,7 @@ public class TransportRoutePlanner { - public List buildRoute(TransportRoutingContext ctx, LatLon start, LatLon end) throws IOException { + public List buildRoute(TransportRoutingContext ctx, LatLon start, LatLon end) throws IOException, InterruptedException { ctx.startCalcTime = System.currentTimeMillis(); List startStops = ctx.getTransportStops(start); List endStops = ctx.getTransportStops(end); @@ -45,7 +45,7 @@ public class TransportRoutePlanner { } double finishTime = ctx.cfg.maxRouteTime; List results = new ArrayList(); - + initProgressBar(ctx, start, end); while (!queue.isEmpty()) { TransportRouteSegment segment = queue.poll(); TransportRouteSegment ex = ctx.visitedSegments.get(segment.getId()); @@ -56,7 +56,6 @@ public class TransportRoutePlanner { continue; } ctx.visitedRoutesCount++; - System.out.println(segment); ctx.visitedSegments.put(segment.getId(), segment); if (segment.getDepth() > ctx.cfg.maxNumberOfChanges) { continue; @@ -137,10 +136,36 @@ public class TransportRoutePlanner { results.add(finish); } } + + if (ctx.calculationProgress != null && ctx.calculationProgress.isCancelled) { + throw new InterruptedException("Route calculation interrupted"); + } + updateCalculationProgress(ctx, queue); + } return prepareResults(ctx, results); } + + private void initProgressBar(TransportRoutingContext ctx, LatLon start, LatLon end) { + ctx.calculationProgress.distanceFromEnd = 0; + ctx.calculationProgress.reverseSegmentQueueSize = 0; + ctx.calculationProgress.directSegmentQueueSize = 0; + float speed = (float) ctx.cfg.travelSpeed + 1; // assume + ctx.calculationProgress.totalEstimatedDistance = (float) (MapUtils.getDistance(start, end)/ speed); + } + + private void updateCalculationProgress(TransportRoutingContext ctx, PriorityQueue queue) { + if (ctx.calculationProgress != null) { + ctx.calculationProgress.directSegmentQueueSize = queue.size(); + if (queue.size() > 0) { + TransportRouteSegment peek = queue.peek(); + ctx.calculationProgress.distanceFromBegin = (float) Math.max(peek.distFromStart, + ctx.calculationProgress.distanceFromBegin); + } + } + } + private List prepareResults(TransportRoutingContext ctx, List results) { Collections.sort(results, new SegmentsComparator(ctx)); diff --git a/OsmAnd-telegram/build.gradle b/OsmAnd-telegram/build.gradle index 57141c4238..f7976040af 100644 --- a/OsmAnd-telegram/build.gradle +++ b/OsmAnd-telegram/build.gradle @@ -146,4 +146,6 @@ dependencies { implementation("com.github.HITGIF:TextFieldBoxes:1.4.4") { exclude group: 'com.android.support' } + + implementation 'net.sf.kxml:kxml2:2.1.8' } diff --git a/OsmAnd-telegram/res/values-zh-rTW/strings.xml b/OsmAnd-telegram/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000000..a6b3daec93 --- /dev/null +++ b/OsmAnd-telegram/res/values-zh-rTW/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/PlatformUtil.java b/OsmAnd-telegram/src/net/osmand/PlatformUtil.java index 9ec83421ae..26a8b13c67 100644 --- a/OsmAnd-telegram/src/net/osmand/PlatformUtil.java +++ b/OsmAnd-telegram/src/net/osmand/PlatformUtil.java @@ -1,6 +1,9 @@ package net.osmand; import org.apache.commons.logging.Log; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; public class PlatformUtil { @@ -143,4 +146,11 @@ public class PlatformUtil { return getLog(cl.getName()); } + public static XmlPullParser newXMLPullParser() throws XmlPullParserException { + return new org.kxml2.io.KXmlParser(); + } + + public static XmlSerializer newSerializer() { + return new org.kxml2.io.KXmlSerializer(); + } } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt index 45e78851e2..2addc53573 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt @@ -25,6 +25,7 @@ class TelegramApplication : Application(), OsmandHelperListener { lateinit var osmandAidlHelper: OsmandAidlHelper private set lateinit var locationProvider: TelegramLocationProvider private set lateinit var messagesDbHelper: MessagesDbHelper private set + lateinit var savingTracksDbHelper: SavingTracksDbHelper private set var telegramService: TelegramService? = null @@ -68,6 +69,7 @@ class TelegramApplication : Application(), OsmandHelperListener { notificationHelper = NotificationHelper(this) locationProvider = TelegramLocationProvider(this) messagesDbHelper = MessagesDbHelper(this) + savingTracksDbHelper = SavingTracksDbHelper(this) if (settings.hasAnyChatToShareLocation() && AndroidUtils.isLocationPermissionAvailable(this)) { shareLocationHelper.startSharingLocation() diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt index 94aec34d9e..a08e5763ee 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt @@ -105,7 +105,7 @@ class TelegramSettings(private val app: TelegramApplication) { var appToConnectPackage = "" private set - var liveNowSortType = LiveNowSortType.SORT_BY_GROUP + var liveNowSortType = LiveNowSortType.SORT_BY_DISTANCE val gpsAndLocPrefs = listOf(SendMyLocPref(), StaleLocPref(), LocHistoryPref(), ShareTypePref()) @@ -487,7 +487,7 @@ class TelegramSettings(private val app: TelegramApplication) { appToConnectPackage = prefs.getString(APP_TO_CONNECT_PACKAGE_KEY, "") liveNowSortType = LiveNowSortType.valueOf( - prefs.getString(LIVE_NOW_SORT_TYPE_KEY, LiveNowSortType.SORT_BY_GROUP.name) + prefs.getString(LIVE_NOW_SORT_TYPE_KEY, LiveNowSortType.SORT_BY_DISTANCE.name) ) batteryOptimisationAsked = prefs.getBoolean(BATTERY_OPTIMISATION_ASKED,false) diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/SavingTracksDbHelper.java b/OsmAnd-telegram/src/net/osmand/telegram/helpers/SavingTracksDbHelper.java new file mode 100644 index 0000000000..6664f8ee69 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/SavingTracksDbHelper.java @@ -0,0 +1,311 @@ +package net.osmand.telegram.helpers; + +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.os.AsyncTask; + +import net.osmand.PlatformUtil; +import net.osmand.telegram.TelegramApplication; +import net.osmand.telegram.ui.LiveNowTabFragment; +import net.osmand.telegram.utils.GPXUtilities; +import net.osmand.telegram.utils.GPXUtilities.GPXFile; +import net.osmand.telegram.utils.GPXUtilities.Track; +import net.osmand.telegram.utils.GPXUtilities.TrkSegment; +import net.osmand.telegram.utils.GPXUtilities.WptPt; + +import org.apache.commons.logging.Log; +import org.drinkless.td.libcore.telegram.TdApi; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.lang.ref.WeakReference; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +public class SavingTracksDbHelper extends SQLiteOpenHelper { + + private final static String DATABASE_NAME = "tracks"; + private final static int DATABASE_VERSION = 3; + + private final static String TRACK_NAME = "track"; //$NON-NLS-1$ + private final static String TRACK_COL_USER_ID = "user_id"; //$NON-NLS-1$ + private final static String TRACK_COL_CHAT_ID = "chat_id"; //$NON-NLS-1$ + private final static String TRACK_COL_DATE = "date"; //$NON-NLS-1$ + private final static String TRACK_COL_LAT = "lat"; //$NON-NLS-1$ + private final static String TRACK_COL_LON = "lon"; //$NON-NLS-1$ + private final static String TRACK_COL_ALTITUDE = "altitude"; //$NON-NLS-1$ + private final static String TRACK_COL_SPEED = "speed"; //$NON-NLS-1$ + private final static String TRACK_COL_HDOP = "hdop"; //$NON-NLS-1$ + private final static String TRACK_COL_TEXT_INFO = "text_info"; // 1 = true, 0 = false //$NON-NLS-1$ + + private final static String INSERT_SCRIPT = "INSERT INTO " + TRACK_NAME + " (" + TRACK_COL_USER_ID + ", " + TRACK_COL_CHAT_ID + ", " + TRACK_COL_LAT + ", " + TRACK_COL_LON + ", " + + TRACK_COL_ALTITUDE + ", " + TRACK_COL_SPEED + ", " + TRACK_COL_HDOP + ", " + TRACK_COL_DATE + ", " + TRACK_COL_TEXT_INFO + ")" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + private final static String CREATE_SCRIPT = "CREATE TABLE " + TRACK_NAME + " (" + TRACK_COL_USER_ID + " long," + TRACK_COL_CHAT_ID + " long," + TRACK_COL_LAT + " double, " + TRACK_COL_LON + " double, " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$//$NON-NLS-5$ + + TRACK_COL_ALTITUDE + " double, " + TRACK_COL_SPEED + " double, " //$NON-NLS-1$ //$NON-NLS-2$ + + TRACK_COL_HDOP + " double, " + TRACK_COL_DATE + " long, " + TRACK_COL_TEXT_INFO + " int )"; + + public final static Log log = PlatformUtil.getLog(SavingTracksDbHelper.class); + + private final TelegramApplication app; + + public SavingTracksDbHelper(TelegramApplication app) { + super(app, DATABASE_NAME, null, DATABASE_VERSION); + this.app = app; + + app.getTelegramHelper().addIncomingMessagesListener(new TelegramHelper.TelegramIncomingMessagesListener() { + + @Override + public void onReceiveChatLocationMessages(long chatId, @NotNull TdApi.Message... messages) { + for (TdApi.Message message : messages) { + updateLocationMessage(message); + } + } + + @Override + public void onDeleteChatLocationMessages(long chatId, @NotNull List messages) { + + } + + @Override + public void updateLocationMessages() { + + } + }); + app.getTelegramHelper().addOutgoingMessagesListener(new TelegramHelper.TelegramOutgoingMessagesListener() { + + @Override + public void onUpdateMessages(@NotNull List messages) { + for (TdApi.Message message : messages) { + updateLocationMessage(message); + } + } + + @Override + public void onDeleteMessages(long chatId, @NotNull List messages) { + + } + + @Override + public void onSendLiveLocationError(int code, @NotNull String message) { + + } + }); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL(CREATE_SCRIPT); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion < 3) { + db.execSQL("ALTER TABLE " + TRACK_NAME + " ADD " + TRACK_COL_TEXT_INFO + " int"); + } + } + + public void saveAsyncUserDataToGpx(LiveNowTabFragment fragment, File dir, int userId, long interval) { + GPXFile gpxFile = app.getSavingTracksDbHelper().collectRecordedDataForUser(userId, interval); + if (gpxFile != null && !gpxFile.isEmpty()) { + LiveUpdatesPurchaseTask task = new LiveUpdatesPurchaseTask(fragment, gpxFile, dir, userId); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + private void updateLocationMessage(TdApi.Message message) { + TdApi.MessageContent content = message.content; + if (content instanceof TdApi.MessageLocation) { + long lastTextMessageUpdate = getLastTextTrackPointTimeForUser(message.senderUserId); + long currentTime = System.currentTimeMillis(); + if (lastTextMessageUpdate == 0 || currentTime - lastTextMessageUpdate < 10 * 1000) { + log.debug("Add map message" + message.senderUserId); + TdApi.MessageLocation messageLocation = (TdApi.MessageLocation) content; + insertData(message.senderUserId, message.chatId, messageLocation.location.latitude, + messageLocation.location.longitude, 0.0, 0.0, 0.0, + Math.max(message.date, message.editDate), 0); + } else { + log.debug("Skip map message"); + } + } else if (content instanceof TelegramHelper.MessageLocation) { + log.debug("Add text message " + message.senderUserId); + TelegramHelper.MessageLocation messageLocation = (TelegramHelper.MessageLocation) content; + insertData(message.senderUserId, message.chatId, messageLocation.getLat(), messageLocation.getLon(), + messageLocation.getAltitude(), messageLocation.getSpeed(), messageLocation.getHdop(), + messageLocation.getLastUpdated() * 1000L, 1); + } + } + + private void insertData(int userId, long chatId, double lat, double lon, double alt, double speed, double hdop, long time, int textMessage) { + execWithClose(INSERT_SCRIPT, new Object[]{userId, chatId, lat, lon, alt, speed, hdop, time, textMessage}); + } + + private synchronized void execWithClose(String script, Object[] objects) { + SQLiteDatabase db = getWritableDatabase(); + try { + if (db != null) { + db.execSQL(script, objects); + } + } catch (RuntimeException e) { + log.error(e.getMessage(), e); + } finally { + if (db != null) { + db.close(); + } + } + } + + private long getLastTextTrackPointTimeForUser(int userId) { + long res = 0; + try { + SQLiteDatabase db = getWritableDatabase(); + if (db != null) { + try { + Cursor query = db.rawQuery("SELECT " + TRACK_COL_DATE + " FROM " + TRACK_NAME + " WHERE " + TRACK_COL_USER_ID + " = ? AND " + + TRACK_COL_TEXT_INFO + " = ?" + " ORDER BY " + TRACK_COL_DATE + " ASC ", new String[]{String.valueOf(userId), String.valueOf(1)}); + if (query.moveToFirst()) { + res = query.getLong(0); + } + query.close(); + } finally { + db.close(); + } + } + } catch (RuntimeException e) { + } + return res; + } + + private GPXFile collectRecordedDataForUser(int userId, long interval) { + GPXFile gpxFile = null; + SQLiteDatabase db = getReadableDatabase(); + if (db != null && db.isOpen()) { + try { + gpxFile = collectDBTracksForUser(db, userId, interval); + } finally { + db.close(); + } + } + return gpxFile; + } + + private GPXFile collectDBTracksForUser(SQLiteDatabase db, int userId, long interval) { + Cursor query = db.rawQuery("SELECT " + TRACK_COL_USER_ID + "," + TRACK_COL_CHAT_ID + "," + TRACK_COL_LAT + "," + TRACK_COL_LON + "," + TRACK_COL_ALTITUDE + "," //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ + + TRACK_COL_SPEED + "," + TRACK_COL_HDOP + "," + TRACK_COL_DATE + " FROM " + TRACK_NAME + + " WHERE " + TRACK_COL_USER_ID + " = ? ORDER BY " + TRACK_COL_DATE + " ASC ", new String[]{String.valueOf(userId)}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + GPXFile gpxFile = new GPXFile(); + long previousTime = 0; + TrkSegment segment = null; + Track track = null; + if (query.moveToFirst()) { + do { + long time = query.getLong(7); + long curTime = System.currentTimeMillis(); + if (curTime - time > interval) { + continue; + } + WptPt pt = new WptPt(); + pt.userId = query.getInt(0); + pt.chatId = query.getLong(1); + pt.lat = query.getDouble(2); + pt.lon = query.getDouble(3); + pt.ele = query.getDouble(4); + pt.speed = query.getDouble(5); + pt.hdop = query.getDouble(6); + pt.time = time; + long currentInterval = Math.abs(time - previousTime); + + if (track != null) { + if (currentInterval < 30 * 60 * 1000) { + // 30 minute - same segment + segment.points.add(pt); + } else { + segment = new TrkSegment(); + segment.points.add(pt); + track.segments.add(segment); + } + } else { + track = new Track(); + segment = new TrkSegment(); + track.segments.add(segment); + segment.points.add(pt); + + gpxFile.tracks.add(track); + } + previousTime = time; + } while (query.moveToNext()); + } + query.close(); + return gpxFile; + } + + private static class LiveUpdatesPurchaseTask extends AsyncTask> { + + private TelegramApplication app; + private WeakReference fragmentRef; + + private final GPXFile gpxFile; + private File dir; + private int userId; + + LiveUpdatesPurchaseTask(LiveNowTabFragment fragment, GPXFile gpxFile, File dir, int userId) { + this.gpxFile = gpxFile; + this.fragmentRef = new WeakReference<>(fragment); + this.app = (TelegramApplication) fragment.getActivity().getApplication(); + this.dir = dir; + this.userId = userId; + } + + @Override + protected List doInBackground(Void... params) { + List warnings = new ArrayList(); + dir.mkdirs(); + if (dir.getParentFile().canWrite()) { + if (dir.exists()) { + + // save file + File fout = new File(dir, userId + ".gpx"); //$NON-NLS-1$ + if (!gpxFile.isEmpty()) { + WptPt pt = gpxFile.findPointToShow(); + + TdApi.User user = app.getTelegramHelper().getUser(pt.userId); + String fileName; + if (user != null) { + fileName = TelegramUiHelper.INSTANCE.getUserName(user) + + "_" + new SimpleDateFormat("yyyy-MM-dd_HH-mm_EEE", Locale.US).format(new Date(pt.time)); //$NON-NLS-1$ + } else { + fileName = userId + "_" + new SimpleDateFormat("yyyy-MM-dd_HH-mm_EEE", Locale.US).format(new Date(pt.time)); //$NON-NLS-1$ + } + fout = new File(dir, fileName + ".gpx"); //$NON-NLS-1$ + int ind = 1; + while (fout.exists()) { + fout = new File(dir, fileName + "_" + (++ind) + ".gpx"); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + String warn = GPXUtilities.writeGpxFile(fout, gpxFile, app); + if (warn != null) { + warnings.add(warn); + return warnings; + } + } + } + + return warnings; + } + + @Override + protected void onPostExecute(List warnings) { + if (warnings != null && warnings.isEmpty()) { + LiveNowTabFragment fragment = fragmentRef.get(); + if (fragment != null && fragment.isResumed()) { + fragment.shareGpx(gpxFile.path); + } + } + } + } +} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt index 317e88b35d..251447062e 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt @@ -221,10 +221,10 @@ class TelegramHelper private constructor() { is MessageUserTextLocation -> content.lastUpdated else -> Math.max(message.editDate, message.date) } - } + } fun isPrivateChat(chat: TdApi.Chat): Boolean = chat.type is TdApi.ChatTypePrivate - + fun isSecretChat(chat: TdApi.Chat): Boolean = chat.type is TdApi.ChatTypeSecret private fun isChannel(chat: TdApi.Chat): Boolean { @@ -384,7 +384,7 @@ class TelegramHelper private constructor() { is TdApi.ChatTypeSecret -> type.userId else -> 0 } - + fun isOsmAndBot(userId: Int) = users[userId]?.username == OSMAND_BOT_USERNAME fun isBot(userId: Int) = users[userId]?.type is TdApi.UserTypeBot @@ -685,10 +685,16 @@ class TelegramHelper private constructor() { } else if (oldContent is TdApi.MessageLocation && (fromBot || viaBot)) { message.content = parseOsmAndBotLocation(message) } - removeOldMessages(message, fromBot, viaBot) - usersLocationMessages[message.id] = message - incomingMessagesListeners.forEach { - it.onReceiveChatLocationMessages(message.chatId, message) + if (message.isOutgoing) { + outgoingMessagesListeners.forEach { + it.onUpdateMessages(listOf(message)) + } + } else { + removeOldMessages(message, fromBot, viaBot) + usersLocationMessages[message.id] = message + incomingMessagesListeners.forEach { + it.onReceiveChatLocationMessages(message.chatId, message) + } } } } @@ -753,7 +759,7 @@ class TelegramHelper private constructor() { stopSendingLiveLocationToChat(chatInfo) } } - + fun getActiveLiveLocationMessages(onComplete: (() -> Unit)?) { requestingActiveLiveLocationMessages = true client?.send(TdApi.GetActiveLiveLocationMessages()) { obj -> @@ -1145,19 +1151,20 @@ class TelegramHelper private constructor() { if (isChannelPost) { return false } + val content = content + val isUserTextLocation = (content is TdApi.MessageText) && content.text.text.startsWith(USER_TEXT_LOCATION_TITLE) val isOsmAndBot = isOsmAndBot(senderUserId) || isOsmAndBot(viaBotUserId) - if (isOutgoing && !isOsmAndBot) { + if (!(isUserTextLocation || content is TdApi.MessageLocation || isOsmAndBot)) { return false } val lastEdited = Math.max(date, editDate) if (TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) - lastEdited > messageActiveTimeSec) { return false } - val content = content - val isUserTextLocation = (content is TdApi.MessageText) && content.text.text.startsWith(USER_TEXT_LOCATION_TITLE) + return when (content) { is TdApi.MessageLocation -> true - is TdApi.MessageText -> (isOsmAndBot) && content.text.text.startsWith(DEVICE_PREFIX) || (isUserTextLocation && senderUserId != currentUser?.id) + is TdApi.MessageText -> (isOsmAndBot) && content.text.text.startsWith(DEVICE_PREFIX) || isUserTextLocation else -> false } } @@ -1234,6 +1241,33 @@ class TelegramHelper private constructor() { } } } + s.startsWith(ALTITUDE_PREFIX) -> { + val altStr = s.removePrefix(ALTITUDE_PREFIX) + try { + val alt = altStr.split(" ").first() + res.altitude = alt.toDouble() + } catch (e: Exception) { + e.printStackTrace() + } + } + s.startsWith(SPEED_PREFIX) -> { + val altStr = s.removePrefix(SPEED_PREFIX) + try { + val alt = altStr.split(" ").first() + res.speed = alt.toDouble() + } catch (e: Exception) { + e.printStackTrace() + } + } + s.startsWith(HDOP_PREFIX) -> { + val altStr = s.removePrefix(HDOP_PREFIX) + try { + val alt = altStr.split(" ").first() + res.hdop = alt.toDouble() + } catch (e: Exception) { + e.printStackTrace() + } + } s.startsWith(UPDATED_PREFIX) -> { if (res.lastUpdated == 0) { val updatedStr = s.removePrefix(UPDATED_PREFIX) @@ -1291,6 +1325,12 @@ class TelegramHelper private constructor() { internal set var lastUpdated: Int = 0 internal set + var speed: Double = 0.0 + internal set + var altitude: Double = 0.0 + internal set + var hdop: Double = 0.0 + internal set override fun getConstructor() = -1 @@ -1510,7 +1550,7 @@ class TelegramHelper private constructor() { lastTelegramUpdateTime = Math.max(message.date, message.editDate) } incomingMessagesListeners.forEach { - it.onReceiveChatLocationMessages(message.chatId, message) + it.updateLocationMessages() } } } @@ -1536,6 +1576,7 @@ class TelegramHelper private constructor() { newContent } } + log.debug("UpdateMessageContent " + message.senderUserId) incomingMessagesListeners.forEach { it.onReceiveChatLocationMessages(message.chatId, message) } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/LiveNowTabFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/LiveNowTabFragment.kt index f962a1f3f8..bd85fa36c0 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/LiveNowTabFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/LiveNowTabFragment.kt @@ -33,6 +33,7 @@ import net.osmand.telegram.utils.OsmandFormatter import net.osmand.telegram.utils.UiUtils.UpdateLocationViewCache import net.osmand.util.MapUtils import org.drinkless.td.libcore.telegram.TdApi +import java.io.File private const val CHAT_VIEW_TYPE = 0 private const val LOCATION_ITEM_VIEW_TYPE = 1 @@ -232,6 +233,15 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage stopLocationUpdate() } + fun shareGpx(path: String) { + val fileUri = AndroidUtils.getUriForFile(app, File(path)) + val sendIntent = Intent(Intent.ACTION_SEND) + sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri) + sendIntent.type = "application/gpx+xml" + sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + startActivity(sendIntent) + } + private fun chooseOsmAnd() { val ctx = context ?: return val installedApps = TelegramSettings.AppConnect.getInstalledApps(ctx) @@ -446,6 +456,15 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage app.showLocationHelper.showLocationOnMap(item, staleLocation) } } + openOnMapView?.setOnLongClickListener { + app.savingTracksDbHelper.saveAsyncUserDataToGpx( + this@LiveNowTabFragment, + app.getExternalFilesDir(null), + item.userId, + 60 * 60 * 6 * 1000 + ) + true + } } else { openOnMapView?.setOnClickListener(null) } @@ -466,6 +485,17 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage if (lastItem) { holder.lastTelegramUpdateTime?.visibility = View.VISIBLE holder.lastTelegramUpdateTime?.text = OsmandFormatter.getListItemLiveTimeDescr(app, telegramHelper.lastTelegramUpdateTime, lastTelegramUpdateStr) + holder.lastTelegramUpdateTime?.setOnClickListener { + val currentUserId = telegramHelper.getCurrentUser()?.id + if (currentUserId != null) { + app.savingTracksDbHelper.saveAsyncUserDataToGpx( + this@LiveNowTabFragment, + app.getExternalFilesDir(null), + currentUserId, + 60 * 60 * 6 * 1000 + ) + } + } } else { holder.lastTelegramUpdateTime?.visibility = View.GONE } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/MyLocationTabFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/MyLocationTabFragment.kt index 9a8fc9cebc..8ce2e7a4f5 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/MyLocationTabFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/MyLocationTabFragment.kt @@ -77,7 +77,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener { private var actionButtonsListener: ActionButtonsListener? = null private var sharingMode = false - + private var updateEnable: Boolean = false override fun onCreateView( @@ -96,7 +96,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener { searchBoxSidesMargin = resources.getDimensionPixelSize(R.dimen.content_padding_half) sharingMode = settings.hasAnyChatToShareLocation() - + savedInstanceState?.apply { val chatsArray = getLongArray(SELECTED_CHATS_KEY) val usersArray = getLongArray(SELECTED_CHATS_KEY) @@ -137,7 +137,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener { setupOptionsBtn(optionsBtn) setupOptionsBtn(mainView.findViewById(R.id.options_title)) } - + imageContainer = mainView.findViewById(R.id.image_container) titleContainer = mainView.findViewById(R.id.title_container).apply { AndroidUtils.addStatusBarPadding19v(context, this) @@ -146,8 +146,10 @@ class MyLocationTabFragment : Fragment(), TelegramListener { mainView.findViewById(R.id.status_title).apply { val sharingStatus = getString(R.string.sharing_enabled) val spannable = SpannableString(sharingStatus) - spannable.setSpan(ForegroundColorSpan(app.uiUtils.getActiveColor()), - sharingStatus.indexOf(" "), sharingStatus.length, 0) + spannable.setSpan( + ForegroundColorSpan(app.uiUtils.getActiveColor()), + sharingStatus.indexOf(" "), sharingStatus.length, 0 + ) text = spannable } @@ -222,7 +224,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener { updateContent() } } - + return mainView } @@ -239,7 +241,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener { super.onPause() updateEnable = false } - + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putLongArray(SELECTED_CHATS_KEY, selectedChats.toLongArray()) @@ -349,7 +351,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener { } }, ADAPTER_UPDATE_INTERVAL_MIL) } - + private fun animateStartSharingBtn(show: Boolean) { if (startSharingBtn.visibility == View.VISIBLE) { val scale = if (show) 1f else 0f @@ -361,7 +363,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener { .start() } } - + private fun clearSelection() { selectedChats.clear() selectedUsers.clear() @@ -450,7 +452,8 @@ class MyLocationTabFragment : Fragment(), TelegramListener { textContainer.visibility = if (sharingMode) View.GONE else View.VISIBLE titleContainer.visibility = if (sharingMode) View.VISIBLE else View.GONE startSharingBtn.visibility = if (sharingMode) View.VISIBLE else View.GONE - headerParams.scrollFlags = if (sharingMode) 0 else AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL + headerParams.scrollFlags = + if (sharingMode) 0 else AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL stopSharingSwitcher.isChecked = true appBarScrollRange = -1 } @@ -460,7 +463,12 @@ class MyLocationTabFragment : Fragment(), TelegramListener { settings.updateSharingStatusHistory() val sharingStatus = settings.sharingStatusChanges.last() sharingStatusTitle.text = sharingStatus.getTitle(app) - sharingStatusIcon.setImageDrawable(app.uiUtils.getIcon(sharingStatus.statusType.iconId, sharingStatus.statusType.iconColorRes)) + sharingStatusIcon.setImageDrawable( + app.uiUtils.getIcon( + sharingStatus.statusType.iconId, + sharingStatus.statusType.iconColorRes + ) + ) } } @@ -490,7 +498,8 @@ class MyLocationTabFragment : Fragment(), TelegramListener { items.addAll(chats) if (!sharingMode) { for (user in contacts.values) { - val containsInChats = chats.any { telegramHelper.getUserIdFromChatType(it.type) == user.id } + val containsInChats = + chats.any { telegramHelper.getUserIdFromChatType(it.type) == user.id } if ((!sharingMode && settings.isSharingLocationToUser(user.id)) || user.id == currentUser?.id || containsInChats) { continue } @@ -520,8 +529,9 @@ class MyLocationTabFragment : Fragment(), TelegramListener { }) return list } - - inner class MyLocationListAdapter : RecyclerView.Adapter() { + + inner class MyLocationListAdapter : + RecyclerView.Adapter() { var items = mutableListOf() set(value) { field = value @@ -569,7 +579,8 @@ class MyLocationTabFragment : Fragment(), TelegramListener { } val lastItem = position == itemCount - 1 - val placeholderId = if (isChat && telegramHelper.isGroup(item as TdApi.Chat)) R.drawable.img_group_picture else R.drawable.img_user_picture + val placeholderId = + if (isChat && telegramHelper.isGroup(item as TdApi.Chat)) R.drawable.img_group_picture else R.drawable.img_user_picture val live = (isChat && settings.isSharingLocationToChat(itemId)) val shareInfo = if (isChat) settings.getChatsShareInfo()[itemId] else null @@ -588,7 +599,12 @@ class MyLocationTabFragment : Fragment(), TelegramListener { } holder.title?.text = title - + holder.icon?.setOnClickListener { + app.forceUpdateMyLocation() + val curUser = telegramHelper.getCurrentUser() + val text = "${curUser?.id} ${curUser?.firstName} ${curUser?.lastName}" + Toast.makeText(app, text, Toast.LENGTH_LONG).show() + } if (holder is ChatViewHolder) { holder.description?.visibility = View.GONE if (live) { @@ -648,7 +664,8 @@ class MyLocationTabFragment : Fragment(), TelegramListener { val duration = shareInfo?.userSetLivePeriod if (duration != null && duration > 0) { - holder.descriptionDuration?.text = OsmandFormatter.getFormattedDuration(context!!, duration) + holder.descriptionDuration?.text = + OsmandFormatter.getFormattedDuration(context!!, duration) holder.description?.apply { visibility = View.VISIBLE text = "${getText(R.string.sharing_time)}:" @@ -656,19 +673,31 @@ class MyLocationTabFragment : Fragment(), TelegramListener { } val expiresIn = shareInfo?.getChatLiveMessageExpireTime() ?: 0 - + holder.textInArea?.apply { - val time = shareInfo?.additionalActiveTime ?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0] + val time = + shareInfo?.additionalActiveTime ?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0] visibility = View.VISIBLE text = "+ ${OsmandFormatter.getFormattedDuration(context!!, time)}" setOnClickListener { val expireTime = shareInfo?.getChatLiveMessageExpireTime() ?: 0 - val newLivePeriod = expireTime + (shareInfo?.additionalActiveTime ?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0]) - val nextAdditionalActiveTime = shareInfo?.getNextAdditionalActiveTime() ?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[1] + val newLivePeriod = expireTime + (shareInfo?.additionalActiveTime + ?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0]) + val nextAdditionalActiveTime = shareInfo?.getNextAdditionalActiveTime() + ?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[1] if (isChat) { - settings.shareLocationToChat(itemId, true, newLivePeriod, nextAdditionalActiveTime) + settings.shareLocationToChat( + itemId, + true, + newLivePeriod, + nextAdditionalActiveTime + ) } else { - settings.shareLocationToUser(itemId.toInt(), newLivePeriod, nextAdditionalActiveTime) + settings.shareLocationToUser( + itemId.toInt(), + newLivePeriod, + nextAdditionalActiveTime + ) } notifyItemChanged(position) } @@ -686,13 +715,16 @@ class MyLocationTabFragment : Fragment(), TelegramListener { holder.stopSharingSecondPart?.apply { visibility = getStopSharingVisibility(expiresIn) - text = "(${getString(R.string.in_time, - OsmandFormatter.getFormattedDuration(context!!, expiresIn, true))})" + text = "(${getString( + R.string.in_time, + OsmandFormatter.getFormattedDuration(context!!, expiresIn, true) + )})" } } } - private fun getStopSharingVisibility(expiresIn: Long) = if (expiresIn > 0) View.VISIBLE else View.INVISIBLE + private fun getStopSharingVisibility(expiresIn: Long) = + if (expiresIn > 0) View.VISIBLE else View.INVISIBLE private fun removeItem(chat: TdApi.Object) { items.remove(chat) diff --git a/OsmAnd-telegram/src/net/osmand/telegram/utils/GPXUtilities.java b/OsmAnd-telegram/src/net/osmand/telegram/utils/GPXUtilities.java new file mode 100644 index 0000000000..5d2ba74d8f --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/telegram/utils/GPXUtilities.java @@ -0,0 +1,1754 @@ +package net.osmand.telegram.utils; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.support.annotation.ColorInt; +import android.support.annotation.Nullable; +import android.text.TextUtils; + +import net.osmand.Location; +import net.osmand.PlatformUtil; +import net.osmand.data.RotatedTileBox; +import net.osmand.telegram.TelegramApplication; +import net.osmand.util.Algorithms; + +import org.apache.commons.logging.Log; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.StringWriter; +import java.io.Writer; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.Stack; +import java.util.TimeZone; + +// copy from net.osmand.plus.GPXUtilities and changes done to WptPt (userId,chatId) + +public class GPXUtilities { + public final static Log log = PlatformUtil.getLog(GPXUtilities.class); + + private final static String GPX_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; //$NON-NLS-1$ + private final static String GPX_TIME_FORMAT_MILLIS = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; //$NON-NLS-1$ + + private final static NumberFormat latLonFormat = new DecimalFormat("0.00#####", new DecimalFormatSymbols( + new Locale("EN", "US"))); + private final static NumberFormat decimalFormat = new DecimalFormat("#.###", new DecimalFormatSymbols( + new Locale("EN", "US"))); + + public static class GPXExtensions { + Map extensions = null; + + public Map getExtensionsToRead() { + if (extensions == null) { + return Collections.emptyMap(); + } + return extensions; + } + + @ColorInt + public int getColor(@ColorInt int defColor) { + String clrValue = null; + if (extensions != null) { + clrValue = extensions.get("color"); + if (clrValue == null) { + clrValue = extensions.get("colour"); + } + if (clrValue == null) { + clrValue = extensions.get("displaycolor"); + } + } + if (clrValue != null && clrValue.length() > 0) { + try { + return Color.parseColor(clrValue.toUpperCase()); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + } + return defColor; + } + + public void setColor(int color) { + getExtensionsToWrite().put("color", Algorithms.colorToString(color)); + } + + public void removeColor() { + getExtensionsToWrite().remove("color"); + } + + public Map getExtensionsToWrite() { + if (extensions == null) { + extensions = new LinkedHashMap<>(); + } + return extensions; + } + + } + + public static class Elevation { + public float distance; + public int time; + public float elevation; + } + + public static class Speed { + public float distance; + public int time; + public float speed; + } + + public static class WptPt extends GPXExtensions implements LocationPoint { + public boolean firstPoint = false; + public boolean lastPoint = false; + public int userId; + public long chatId; + public double lat; + public double lon; + public String name = null; + public String link = null; + // previous undocumented feature 'category' ,now 'type' + public String category = null; + public String desc = null; + public String comment = null; + // by default + public long time = 0; + public double ele = Double.NaN; + public double speed = 0; + public double hdop = Double.NaN; + public boolean deleted = false; + public int colourARGB = 0; // point colour (used for altitude/speed colouring) + public double distance = 0.0; // cumulative distance, if in a track + + public WptPt() { + } + + public WptPt(WptPt wptPt) { + this.lat = wptPt.lat; + this.lon = wptPt.lon; + this.name = wptPt.name; + this.link = wptPt.link; + + this.category = wptPt.category; + this.desc = wptPt.desc; + this.comment = wptPt.comment; + + this.time = wptPt.time; + this.ele = wptPt.ele; + this.speed = wptPt.speed; + this.hdop = wptPt.hdop; + this.deleted = wptPt.deleted; + this.colourARGB = wptPt.colourARGB; + this.distance = wptPt.distance; + } + + public void setDistance(double dist) { + distance = dist; + } + + public double getDistance() { + return distance; + } + + @Override + public int getColor() { + return getColor(0); + } + + @Override + public double getLatitude() { + return lat; + } + + @Override + public double getLongitude() { + return lon; + } + + +// @Override +// public PointDescription getPointDescription(Context ctx) { +// return new PointDescription(PointDescription.POINT_TYPE_WPT, name); +// } + + public WptPt(double lat, double lon, long time, double ele, double speed, double hdop) { + this.lat = lat; + this.lon = lon; + this.time = time; + this.ele = ele; + this.speed = speed; + this.hdop = hdop; + } + + @Override + public boolean isVisible() { + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((category == null) ? 0 : category.hashCode()); + result = prime * result + ((desc == null) ? 0 : desc.hashCode()); + result = prime * result + ((comment == null) ? 0 : comment.hashCode()); + result = prime * result + ((lat == 0) ? 0 : Double.valueOf(lat).hashCode()); + result = prime * result + ((lon == 0) ? 0 : Double.valueOf(lon).hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + WptPt other = (WptPt) obj; + return Algorithms.objectEquals(other.name, name) + && Algorithms.objectEquals(other.category, category) + && Algorithms.objectEquals(other.lat, lat) + && Algorithms.objectEquals(other.lon, lon) + && Algorithms.objectEquals(other.desc, desc); + } + } + + public static class TrkSegment extends GPXExtensions { + public boolean generalSegment = false; + + public List points = new ArrayList<>(); + +// public List renders = new ArrayList<>(); + + public List splitByDistance(double meters) { + return split(getDistanceMetric(), getTimeSplit(), meters); + } + + public List splitByTime(int seconds) { + return split(getTimeSplit(), getDistanceMetric(), seconds); + } + + private List split(SplitMetric metric, SplitMetric secondaryMetric, double metricLimit) { + List splitSegments = new ArrayList<>(); + splitSegment(metric, secondaryMetric, metricLimit, splitSegments, this); + return convert(splitSegments); + } + +// public void drawRenderers(double zoom, Paint p, Canvas c, RotatedTileBox tb) { +// for (Renderable.RenderableSegment rs : renders) { +// rs.drawSegment(zoom, p, c, tb); +// } +// } + } + + public static class Track extends GPXExtensions { + public String name = null; + public String desc = null; + public List segments = new ArrayList<>(); + public boolean generalTrack = false; + + } + + public static class Route extends GPXExtensions { + public String name = null; + public String desc = null; + public List points = new ArrayList<>(); + + } + + public static class Metadata extends GPXExtensions { + public String desc; + + @Nullable + public String getArticleTitle() { + return getExtensionsToRead().get("article_title"); + } + + @Nullable + public String getArticleLang() { + return getExtensionsToRead().get("article_lang"); + } + } + + public static class GPXTrackAnalysis { + public float totalDistance = 0; + public int totalTracks = 0; + public long startTime = Long.MAX_VALUE; + public long endTime = Long.MIN_VALUE; + public long timeSpan = 0; + //Next few lines for Issue 3222 heuristic testing only + //public long timeMoving0 = 0; + //public float totalDistanceMoving0 = 0; + public long timeMoving = 0; + public float totalDistanceMoving = 0; + + public double diffElevationUp = 0; + public double diffElevationDown = 0; + public double avgElevation = 0; + public double minElevation = 99999; + public double maxElevation = -100; + + public float minSpeed = Float.MAX_VALUE; + public float maxSpeed = 0; + public float avgSpeed; + + public int points; + public int wptPoints = 0; + + public Set wptCategoryNames; + + public double metricEnd; + public double secondaryMetricEnd; + public WptPt locationStart; + public WptPt locationEnd; + + public double left = 0; + public double right = 0; + public double top = 0; + public double bottom = 0; + + public boolean isTimeSpecified() { + return startTime != Long.MAX_VALUE && startTime != 0; + } + + public boolean isTimeMoving() { + return timeMoving != 0; + } + + public boolean isElevationSpecified() { + return maxElevation != -100; + } + + public boolean hasSpeedInTrack() { + return hasSpeedInTrack; + } + + public boolean isBoundsCalculated() { + return left != 0 && right != 0 && top != 0 && bottom != 0; + } + + public List elevationData; + public List speedData; + + public boolean hasElevationData; + public boolean hasSpeedData; + public boolean hasSpeedInTrack = false; + + public boolean isSpeedSpecified() { + return avgSpeed > 0; + } + + + public static GPXTrackAnalysis segment(long filetimestamp, TrkSegment segment) { + return new GPXTrackAnalysis().prepareInformation(filetimestamp, new SplitSegment(segment)); + } + + public GPXTrackAnalysis prepareInformation(long filestamp, SplitSegment... splitSegments) { + float[] calculations = new float[1]; + + long startTimeOfSingleSegment = 0; + long endTimeOfSingleSegment = 0; + + float totalElevation = 0; + int elevationPoints = 0; + int speedCount = 0; + int timeDiff = 0; + double totalSpeedSum = 0; + points = 0; + + double channelThresMin = 10; // Minimum oscillation amplitude considered as relevant or as above noise for accumulated Ascent/Descent analysis + double channelThres = channelThresMin; // Actual oscillation amplitude considered as above noise (dynamic channel adjustment, accomodates depedency on current VDOP/getAccuracy if desired) + double channelBase; + double channelTop; + double channelBottom; + boolean climb = false; + + elevationData = new ArrayList<>(); + speedData = new ArrayList<>(); + + for (SplitSegment s : splitSegments) { + final int numberOfPoints = s.getNumberOfPoints(); + + channelBase = 99999; + channelTop = channelBase; + channelBottom = channelBase; + //channelThres = channelThresMin; //only for dynamic channel adjustment + + float segmentDistance = 0f; + metricEnd += s.metricEnd; + secondaryMetricEnd += s.secondaryMetricEnd; + points += numberOfPoints; + for (int j = 0; j < numberOfPoints; j++) { + WptPt point = s.get(j); + if (j == 0 && locationStart == null) { + locationStart = point; + } + if (j == numberOfPoints - 1) { + locationEnd = point; + } + long time = point.time; + if (time != 0) { + if (s.metricEnd == 0) { + if (s.segment.generalSegment) { + if (point.firstPoint) { + startTimeOfSingleSegment = time; + } else if (point.lastPoint) { + endTimeOfSingleSegment = time; + } + if (startTimeOfSingleSegment != 0 && endTimeOfSingleSegment != 0) { + timeSpan += endTimeOfSingleSegment - startTimeOfSingleSegment; + startTimeOfSingleSegment = 0; + endTimeOfSingleSegment = 0; + } + } + } + startTime = Math.min(startTime, time); + endTime = Math.max(endTime, time); + } + + if (left == 0 && right == 0) { + left = point.getLongitude(); + right = point.getLongitude(); + top = point.getLatitude(); + bottom = point.getLatitude(); + } else { + left = Math.min(left, point.getLongitude()); + right = Math.max(right, point.getLongitude()); + top = Math.max(top, point.getLatitude()); + bottom = Math.min(bottom, point.getLatitude()); + } + + double elevation = point.ele; + Elevation elevation1 = new Elevation(); + if (!Double.isNaN(elevation)) { + totalElevation += elevation; + elevationPoints++; + minElevation = Math.min(elevation, minElevation); + maxElevation = Math.max(elevation, maxElevation); + + elevation1.elevation = (float) elevation; + } else { + elevation1.elevation = Float.NaN; + } + + float speed = (float) point.speed; + if (speed > 0) { + hasSpeedInTrack = true; + } + + // Trend channel analysis for elevation gain/loss, Hardy 2015-09-22, LPF filtering added 2017-10-26: + // - Detect the consecutive elevation trend channels: Only use the net elevation changes of each trend channel (i.e. between the turnarounds) to accumulate the Ascent/Descent values. + // - Perform the channel evaluation on Low Pass Filter (LPF) smoothed ele data instead of on the raw ele data + // Parameters: + // - channelThresMin (in meters): defines the channel turnaround detection, i.e. oscillations smaller than this are ignored as irrelevant or noise. + // - smoothWindow (number of points): is the LPF window + // NOW REMOVED, as no relevant examples found: Dynamic channel adjustment: To suppress unreliable measurement points, could relax the turnaround detection from the constant channelThresMin to channelThres which is e.g. based on the maximum VDOP of any point which contributed to the current trend. (Good assumption is VDOP=2*HDOP, which accounts for invisibility of lower hemisphere satellites.) + + // LPF smooting of ele data, usually smooth over odd number of values like 5 + final int smoothWindow = 5; + double eleSmoothed = Double.NaN; + int j2 = 0; + for (int j1 = -smoothWindow + 1; j1 <= 0; j1++) { + if ((j + j1 >= 0) && !Double.isNaN(s.get(j + j1).ele)) { + j2++; + if (!Double.isNaN(eleSmoothed)) { + eleSmoothed = eleSmoothed + s.get(j + j1).ele; + } else { + eleSmoothed = s.get(j + j1).ele; + } + } + } + if (!Double.isNaN(eleSmoothed)) { + eleSmoothed = eleSmoothed / j2; + } + + if (!Double.isNaN(eleSmoothed)) { + // Init channel + if (channelBase == 99999) { + channelBase = eleSmoothed; + channelTop = channelBase; + channelBottom = channelBase; + //channelThres = channelThresMin; //only for dynamic channel adjustment + } + // Channel maintenance + if (eleSmoothed > channelTop) { + channelTop = eleSmoothed; + //if (!Double.isNaN(point.hdop)) { + // channelThres = Math.max(channelThres, 2.0 * point.hdop); //only for dynamic channel adjustment + //} + } else if (eleSmoothed < channelBottom) { + channelBottom = eleSmoothed; + //if (!Double.isNaN(point.hdop)) { + // channelThres = Math.max(channelThres, 2.0 * point.hdop); //only for dynamic channel adjustment + //} + } + // Turnaround (breakout) detection + if ((eleSmoothed <= (channelTop - channelThres)) && (climb == true)) { + if ((channelTop - channelBase) >= channelThres) { + diffElevationUp += channelTop - channelBase; + } + channelBase = channelTop; + channelBottom = eleSmoothed; + climb = false; + //channelThres = channelThresMin; //only for dynamic channel adjustment + } else if ((eleSmoothed >= (channelBottom + channelThres)) && (climb == false)) { + if ((channelBase - channelBottom) >= channelThres) { + diffElevationDown += channelBase - channelBottom; + } + channelBase = channelBottom; + channelTop = eleSmoothed; + climb = true; + //channelThres = channelThresMin; //only for dynamic channel adjustment + } + // End detection without breakout + if (j == (numberOfPoints - 1)) { + if ((channelTop - channelBase) >= channelThres) { + diffElevationUp += channelTop - channelBase; + } + if ((channelBase - channelBottom) >= channelThres) { + diffElevationDown += channelBase - channelBottom; + } + } + } + + if (j > 0) { + WptPt prev = s.get(j - 1); + + // Old complete summation approach for elevation gain/loss + //if (!Double.isNaN(point.ele) && !Double.isNaN(prev.ele)) { + // double diff = point.ele - prev.ele; + // if (diff > 0) { + // diffElevationUp += diff; + // } else { + // diffElevationDown -= diff; + // } + //} + + // totalDistance += MapUtils.getDistance(prev.lat, prev.lon, point.lat, point.lon); + // using ellipsoidal 'distanceBetween' instead of spherical haversine (MapUtils.getDistance) is + // a little more exact, also seems slightly faster: + net.osmand.Location.distanceBetween(prev.lat, prev.lon, point.lat, point.lon, calculations); + totalDistance += calculations[0]; + segmentDistance += calculations[0]; + point.distance = segmentDistance; + timeDiff = (int) ((point.time - prev.time) / 1000); + + //Last resort: Derive speed values from displacement if track does not originally contain speed + if (!hasSpeedInTrack && speed == 0 && timeDiff > 0) { + speed = calculations[0] / timeDiff; + } + + // Motion detection: + // speed > 0 uses GPS chipset's motion detection + // calculations[0] > minDisplacment * time is heuristic needed because tracks may be filtered at recording time, so points at rest may not be present in file at all + if ((speed > 0) && (calculations[0] > 0.1 / 1000f * (point.time - prev.time)) && point.time != 0 && prev.time != 0) { + timeMoving = timeMoving + (point.time - prev.time); + totalDistanceMoving += calculations[0]; + } + + //Next few lines for Issue 3222 heuristic testing only + // if (speed > 0 && point.time != 0 && prev.time != 0) { + // timeMoving0 = timeMoving0 + (point.time - prev.time); + // totalDistanceMoving0 += calculations[0]; + // } + } + + elevation1.time = timeDiff; + elevation1.distance = (j > 0) ? calculations[0] : 0; + elevationData.add(elevation1); + if (!hasElevationData && !Float.isNaN(elevation1.elevation) && totalDistance > 0) { + hasElevationData = true; + } + + minSpeed = Math.min(speed, minSpeed); + if (speed > 0) { + totalSpeedSum += speed; + maxSpeed = Math.max(speed, maxSpeed); + speedCount++; + } + + Speed speed1 = new Speed(); + speed1.speed = speed; + speed1.time = timeDiff; + speed1.distance = elevation1.distance; + speedData.add(speed1); + if (!hasSpeedData && speed1.speed > 0 && totalDistance > 0) { + hasSpeedData = true; + } + } + } + if (totalDistance < 0) { + hasElevationData = false; + hasSpeedData = false; + } + if (!isTimeSpecified()) { + startTime = filestamp; + endTime = filestamp; + } + + // OUTPUT: + // 1. Total distance, Start time, End time + // 2. Time span + if (timeSpan == 0) { + timeSpan = endTime - startTime; + } + + // 3. Time moving, if any + // 4. Elevation, eleUp, eleDown, if recorded + if (elevationPoints > 0) { + avgElevation = totalElevation / elevationPoints; + } + + + // 5. Max speed and Average speed, if any. Average speed is NOT overall (effective) speed, but only calculated for "moving" periods. + // Averaging speed values is less precise than totalDistanceMoving/timeMoving + if (speedCount > 0) { + if (timeMoving > 0) { + avgSpeed = (float) totalDistanceMoving / (float) timeMoving * 1000f; + } else { + avgSpeed = (float) totalSpeedSum / (float) speedCount; + } + } else { + avgSpeed = -1; + } + return this; + } + + } + + private static class SplitSegment { + TrkSegment segment; + double startCoeff = 0; + int startPointInd; + double endCoeff = 0; + int endPointInd; + double metricEnd; + double secondaryMetricEnd; + + public SplitSegment(TrkSegment s) { + startPointInd = 0; + startCoeff = 0; + endPointInd = s.points.size() - 2; + endCoeff = 1; + this.segment = s; + } + + public SplitSegment(TrkSegment s, int pointInd, double cf) { + this.segment = s; + this.startPointInd = pointInd; + this.startCoeff = cf; + } + + + public int getNumberOfPoints() { + return endPointInd - startPointInd + 2; + } + + public WptPt get(int j) { + final int ind = j + startPointInd; + if (j == 0) { + if (startCoeff == 0) { + return segment.points.get(ind); + } + return approx(segment.points.get(ind), segment.points.get(ind + 1), startCoeff); + } + if (j == getNumberOfPoints() - 1) { + if (endCoeff == 1) { + return segment.points.get(ind); + } + return approx(segment.points.get(ind - 1), segment.points.get(ind), endCoeff); + } + return segment.points.get(ind); + } + + + private WptPt approx(WptPt w1, WptPt w2, double cf) { + long time = value(w1.time, w2.time, 0, cf); + double speed = value(w1.speed, w2.speed, 0, cf); + double ele = value(w1.ele, w2.ele, 0, cf); + double hdop = value(w1.hdop, w2.hdop, 0, cf); + double lat = value(w1.lat, w2.lat, -360, cf); + double lon = value(w1.lon, w2.lon, -360, cf); + return new WptPt(lat, lon, time, ele, speed, hdop); + } + + private double value(double vl, double vl2, double none, double cf) { + if (vl == none || Double.isNaN(vl)) { + return vl2; + } else if (vl2 == none || Double.isNaN(vl2)) { + return vl; + } + return vl + cf * (vl2 - vl); + } + + private long value(long vl, long vl2, long none, double cf) { + if (vl == none) { + return vl2; + } else if (vl2 == none) { + return vl; + } + return vl + ((long) (cf * (vl2 - vl))); + } + + + public double setLastPoint(int pointInd, double endCf) { + endCoeff = endCf; + endPointInd = pointInd; + return endCoeff; + } + + } + + private static SplitMetric getDistanceMetric() { + return new SplitMetric() { + + private float[] calculations = new float[1]; + + @Override + public double metric(WptPt p1, WptPt p2) { + net.osmand.Location.distanceBetween(p1.lat, p1.lon, p2.lat, p2.lon, calculations); + return calculations[0]; + } + }; + } + + private static SplitMetric getTimeSplit() { + return new SplitMetric() { + + @Override + public double metric(WptPt p1, WptPt p2) { + if (p1.time != 0 && p2.time != 0) { + return (int) Math.abs((p2.time - p1.time) / 1000l); + } + return 0; + } + }; + } + + private abstract static class SplitMetric { + + public abstract double metric(WptPt p1, WptPt p2); + + } + + private static void splitSegment(SplitMetric metric, SplitMetric secondaryMetric, + double metricLimit, List splitSegments, + TrkSegment segment) { + double currentMetricEnd = metricLimit; + double secondaryMetricEnd = 0; + SplitSegment sp = new SplitSegment(segment, 0, 0); + double total = 0; + WptPt prev = null; + for (int k = 0; k < segment.points.size(); k++) { + WptPt point = segment.points.get(k); + if (k > 0) { + double currentSegment = metric.metric(prev, point); + secondaryMetricEnd += secondaryMetric.metric(prev, point); + while (total + currentSegment > currentMetricEnd) { + double p = currentMetricEnd - total; + double cf = (p / currentSegment); + sp.setLastPoint(k - 1, cf); + sp.metricEnd = currentMetricEnd; + sp.secondaryMetricEnd = secondaryMetricEnd; + splitSegments.add(sp); + + sp = new SplitSegment(segment, k - 1, cf); + currentMetricEnd += metricLimit; + prev = sp.get(0); + } + total += currentSegment; + } + prev = point; + } + if (segment.points.size() > 0 + && !(sp.endPointInd == segment.points.size() - 1 && sp.startCoeff == 1)) { + sp.metricEnd = total; + sp.secondaryMetricEnd = secondaryMetricEnd; + sp.setLastPoint(segment.points.size() - 2, 1); + splitSegments.add(sp); + } + } + + private static List convert(List splitSegments) { + List ls = new ArrayList<>(); + for (SplitSegment s : splitSegments) { + GPXTrackAnalysis a = new GPXTrackAnalysis(); + a.prepareInformation(0, s); + ls.add(a); + } + return ls; + } + + public static class GPXFile extends GPXExtensions { + public String author; + public Metadata metadata; + public List tracks = new ArrayList<>(); + private List points = new ArrayList<>(); + public List routes = new ArrayList<>(); + + public String warning = null; + public String path = ""; + public boolean showCurrentTrack; + public long modifiedTime = 0; + + private Track generalTrack; + private TrkSegment generalSegment; + + public List getPoints() { + return Collections.unmodifiableList(points); + } + + public Map> getPointsByCategories() { + Map> res = new HashMap<>(); + for (WptPt pt : points) { + String category = pt.category == null ? "" : pt.category; + List list = res.get(category); + if (list != null) { + list.add(pt); + } else { + list = new ArrayList<>(); + list.add(pt); + res.put(category, list); + } + } + return res; + } + + public boolean isPointsEmpty() { + return points.isEmpty(); + } + + public int getPointsSize() { + return points.size(); + } + + boolean containsPoint(WptPt point) { + return points.contains(point); + } + + void clearPoints() { + points.clear(); + modifiedTime = System.currentTimeMillis(); + } + + public void addPoint(WptPt point) { + points.add(point); + modifiedTime = System.currentTimeMillis(); + } + + public void addPoint(int position, WptPt point) { + points.add(position, point); + modifiedTime = System.currentTimeMillis(); + } + + void addPoints(Collection collection) { + points.addAll(collection); + modifiedTime = System.currentTimeMillis(); + } + + public boolean isCloudmadeRouteFile() { + return "cloudmade".equalsIgnoreCase(author); + } + + public void addGeneralTrack() { + Track generalTrack = getGeneralTrack(); + if (generalTrack != null && !tracks.contains(generalTrack)) { + tracks.add(0, generalTrack); + } + } + + public Track getGeneralTrack() { + TrkSegment generalSegment = getGeneralSegment(); + if (generalTrack == null && generalSegment != null) { + Track track = new Track(); + track.segments = new ArrayList<>(); + track.segments.add(generalSegment); + generalTrack = track; + track.generalTrack = true; + } + return generalTrack; + } + + public TrkSegment getGeneralSegment() { + if (generalSegment == null && getNonEmptySegmentsCount() > 1) { + buildGeneralSegment(); + } + return generalSegment; + } + + private void buildGeneralSegment() { + TrkSegment segment = new TrkSegment(); + for (Track track : tracks) { + for (TrkSegment s : track.segments) { + if (s.points.size() > 0) { + List waypoints = new ArrayList<>(s.points.size()); + for (WptPt wptPt : s.points) { + waypoints.add(new WptPt(wptPt)); + } + waypoints.get(0).firstPoint = true; + waypoints.get(waypoints.size() - 1).lastPoint = true; + segment.points.addAll(waypoints); + } + } + } + if (segment.points.size() > 0) { + segment.generalSegment = true; + generalSegment = segment; + } + } + + public GPXTrackAnalysis getAnalysis(long fileTimestamp) { + GPXTrackAnalysis g = new GPXTrackAnalysis(); + g.wptPoints = points.size(); + g.wptCategoryNames = getWaypointCategories(true); + List splitSegments = new ArrayList(); + for (int i = 0; i < tracks.size(); i++) { + Track subtrack = tracks.get(i); + for (TrkSegment segment : subtrack.segments) { + if (!segment.generalSegment) { + g.totalTracks++; + if (segment.points.size() > 1) { + splitSegments.add(new SplitSegment(segment)); + } + } + } + } + g.prepareInformation(fileTimestamp, splitSegments.toArray(new SplitSegment[splitSegments.size()])); + return g; + } + + public List getRoutePoints() { + List points = new ArrayList<>(); + for (int i = 0; i < routes.size(); i++) { + Route rt = routes.get(i); + points.addAll(rt.points); + } + return points; + } + + public boolean hasRtePt() { + for (Route r : routes) { + if (r.points.size() > 0) { + return true; + } + } + return false; + } + + public boolean hasWptPt() { + return points.size() > 0; + } + + public boolean hasTrkPt() { + for (Track t : tracks) { + for (TrkSegment ts : t.segments) { + if (ts.points.size() > 0) { + return true; + } + } + } + return false; + } + + public WptPt addWptPt(double lat, double lon, long time, String description, String name, String category, int color) { + double latAdjusted = Double.parseDouble(latLonFormat.format(lat)); + double lonAdjusted = Double.parseDouble(latLonFormat.format(lon)); + final WptPt pt = new WptPt(latAdjusted, lonAdjusted, time, Double.NaN, 0, Double.NaN); + pt.name = name; + pt.category = category; + pt.desc = description; + if (color != 0) { + pt.setColor(color); + } + + points.add(pt); + + modifiedTime = System.currentTimeMillis(); + + return pt; + } + + public WptPt addRtePt(double lat, double lon, long time, String description, String name, String category, int color) { + double latAdjusted = Double.parseDouble(latLonFormat.format(lat)); + double lonAdjusted = Double.parseDouble(latLonFormat.format(lon)); + final WptPt pt = new WptPt(latAdjusted, lonAdjusted, time, Double.NaN, 0, Double.NaN); + pt.name = name; + pt.category = category; + pt.desc = description; + if (color != 0) { + pt.setColor(color); + } + + if (routes.size() == 0) { + routes.add(new Route()); + } + Route currentRoute = routes.get(routes.size() - 1); + currentRoute.points.add(pt); + + modifiedTime = System.currentTimeMillis(); + + return pt; + } + + public void addTrkSegment(List points) { + removeGeneralTrackIfExists(); + + TrkSegment segment = new TrkSegment(); + segment.points.addAll(points); + + if (tracks.size() == 0) { + tracks.add(new Track()); + } + Track lastTrack = tracks.get(tracks.size() - 1); + lastTrack.segments.add(segment); + + modifiedTime = System.currentTimeMillis(); + } + + public boolean replaceSegment(TrkSegment oldSegment, TrkSegment newSegment) { + removeGeneralTrackIfExists(); + + for (int i = 0; i < tracks.size(); i++) { + Track currentTrack = tracks.get(i); + for (int j = 0; j < currentTrack.segments.size(); j++) { + int segmentIndex = currentTrack.segments.indexOf(oldSegment); + if (segmentIndex != -1) { + currentTrack.segments.remove(segmentIndex); + currentTrack.segments.add(segmentIndex, newSegment); + addGeneralTrack(); + modifiedTime = System.currentTimeMillis(); + return true; + } + } + } + + addGeneralTrack(); + return false; + } + + public void addRoutePoints(List points) { + if (routes.size() == 0) { + Route route = new Route(); + routes.add(route); + } + + Route lastRoute = routes.get(routes.size() - 1); + lastRoute.points.addAll(points); + modifiedTime = System.currentTimeMillis(); + } + + public void replaceRoutePoints(List points) { + routes.clear(); + routes.add(new Route()); + Route currentRoute = routes.get(routes.size() - 1); + currentRoute.points.addAll(points); + modifiedTime = System.currentTimeMillis(); + } + + public void updateWptPt(WptPt pt, double lat, double lon, long time, String description, String name, String category, int color) { + int index = points.indexOf(pt); + double latAdjusted = Double.parseDouble(latLonFormat.format(lat)); + double lonAdjusted = Double.parseDouble(latLonFormat.format(lon)); + pt.lat = latAdjusted; + pt.lon = lonAdjusted; + pt.time = time; + pt.desc = description; + pt.name = name; + pt.category = category; + if (color != 0) { + pt.setColor(color); + } + + if (index != -1) { + points.set(index, pt); + } + modifiedTime = System.currentTimeMillis(); + } + + private void removeGeneralTrackIfExists() { + if (generalTrack != null) { + tracks.remove(generalTrack); + this.generalTrack = null; + this.generalSegment = null; + } + } + + public boolean removeTrkSegment(TrkSegment segment) { + removeGeneralTrackIfExists(); + + for (int i = 0; i < tracks.size(); i++) { + Track currentTrack = tracks.get(i); + for (int j = 0; j < currentTrack.segments.size(); j++) { + if (currentTrack.segments.remove(segment)) { + addGeneralTrack(); + modifiedTime = System.currentTimeMillis(); + return true; + } + } + } + addGeneralTrack(); + return false; + } + + public boolean deleteWptPt(WptPt pt) { + modifiedTime = System.currentTimeMillis(); + return points.remove(pt); + } + + public boolean deleteRtePt(WptPt pt) { + modifiedTime = System.currentTimeMillis(); + for (Route route : routes) { + if (route.points.remove(pt)) { + return true; + } + } + return false; + } + + public List processRoutePoints() { + List tpoints = new ArrayList(); + if (routes.size() > 0) { + for (Route r : routes) { + int routeColor = r.getColor(getColor(0)); + if (r.points.size() > 0) { + TrkSegment sgmt = new TrkSegment(); + tpoints.add(sgmt); + sgmt.points.addAll(r.points); + sgmt.setColor(routeColor); + } + } + } + return tpoints; + } + + public List proccessPoints() { + List tpoints = new ArrayList(); + for (Track t : tracks) { + int trackColor = t.getColor(getColor(0)); + for (TrkSegment ts : t.segments) { + if (!ts.generalSegment && ts.points.size() > 0) { + TrkSegment sgmt = new TrkSegment(); + tpoints.add(sgmt); + sgmt.points.addAll(ts.points); + sgmt.setColor(trackColor); + } + } + } + return tpoints; + } + + public WptPt getLastPoint() { + if (tracks.size() > 0) { + Track tk = tracks.get(tracks.size() - 1); + if (tk.segments.size() > 0) { + TrkSegment ts = tk.segments.get(tk.segments.size() - 1); + if (ts.points.size() > 0) { + return ts.points.get(ts.points.size() - 1); + } + } + } + return null; + } + + public WptPt findPointToShow() { + for (Track t : tracks) { + for (TrkSegment s : t.segments) { + if (s.points.size() > 0) { + return s.points.get(0); + } + } + } + for (Route s : routes) { + if (s.points.size() > 0) { + return s.points.get(0); + } + } + if (points.size() > 0) { + return points.get(0); + } + return null; + } + + public boolean isEmpty() { + for (Track t : tracks) { + if (t.segments != null) { + for (TrkSegment s : t.segments) { + boolean tracksEmpty = s.points.isEmpty(); + if (!tracksEmpty) { + return false; + } + } + } + } + return points.isEmpty() && routes.isEmpty(); + } + + public int getNonEmptySegmentsCount() { + int count = 0; + for (Track t : tracks) { + for (TrkSegment s : t.segments) { + if (s.points.size() > 0) { + count++; + } + } + } + return count; + } + + public Set getWaypointCategories(boolean withDefaultCategory) { + Set categories = new HashSet<>(); + for (WptPt pt : points) { + String category = pt.category == null ? "" : pt.category; + if (withDefaultCategory || !TextUtils.isEmpty(category)) { + categories.add(category); + } + } + return categories; + } + + public Map getWaypointCategoriesWithColors(boolean withDefaultCategory) { + Map categories = new HashMap<>(); + for (WptPt pt : points) { + String category = pt.category == null ? "" : pt.category; + int color = pt.category == null ? 0 : pt.getColor(); + boolean emptyCategory = TextUtils.isEmpty(category); + if (!emptyCategory) { + Integer existingColor = categories.get(category); + if (existingColor == null || (existingColor == 0 && color != 0)) { + categories.put(category, color); + } + } else if (withDefaultCategory) { + categories.put(category, 0); + } + } + return categories; + } + } + + public static String asString(GPXFile file, TelegramApplication ctx) { + final Writer writer = new StringWriter(); + GPXUtilities.writeGpx(writer, file, ctx); + return writer.toString(); + } + + public static String writeGpxFile(File fout, GPXFile file, TelegramApplication ctx) { + Writer output = null; + try { + if (fout.getParentFile() != null) { + fout.getParentFile().mkdirs(); + } + output = new OutputStreamWriter(new FileOutputStream(fout), "UTF-8"); //$NON-NLS-1$ + if (Algorithms.isEmpty(file.path)) { + file.path = fout.getAbsolutePath(); + } + String msg = writeGpx(output, file, ctx); + return msg; + } catch (IOException e) { + log.error("Error saving gpx", e); //$NON-NLS-1$ + return "Error saving gpx"; + } finally { + if (output != null) { + try { + output.close(); + } catch (IOException ignore) { + // ignore + } + } + } + } + + public static String writeGpx(Writer output, GPXFile file, TelegramApplication ctx) { + try { + SimpleDateFormat format = new SimpleDateFormat(GPX_TIME_FORMAT, Locale.US); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + XmlSerializer serializer = PlatformUtil.newSerializer(); + serializer.setOutput(output); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); //$NON-NLS-1$ + serializer.startDocument("UTF-8", true); //$NON-NLS-1$ + serializer.startTag(null, "gpx"); //$NON-NLS-1$ + serializer.attribute(null, "version", "1.1"); //$NON-NLS-1$ //$NON-NLS-2$ + if (file.author == null) { + serializer.attribute(null, "creator", ctx.getPackageName()); //$NON-NLS-1$ + } else { + serializer.attribute(null, "creator", file.author); //$NON-NLS-1$ + } + serializer.attribute(null, "xmlns", "http://www.topografix.com/GPX/1/1"); //$NON-NLS-1$ //$NON-NLS-2$ + serializer.attribute(null, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); + serializer.attribute(null, "xsi:schemaLocation", + "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"); + + String trackName = getFilename(file.path); + serializer.startTag(null, "metadata"); + writeNotNullText(serializer, "name", trackName); + if (file.metadata != null) { + writeNotNullText(serializer, "desc", file.metadata.desc); + writeExtensions(serializer, file.metadata); + } + serializer.endTag(null, "metadata"); + + + for (Track track : file.tracks) { + if (!track.generalTrack) { + serializer.startTag(null, "trk"); //$NON-NLS-1$ + writeNotNullText(serializer, "name", track.name); + writeNotNullText(serializer, "desc", track.desc); + for (TrkSegment segment : track.segments) { + serializer.startTag(null, "trkseg"); //$NON-NLS-1$ + for (WptPt p : segment.points) { + serializer.startTag(null, "trkpt"); //$NON-NLS-1$ + writeWpt(format, serializer, p); + serializer.endTag(null, "trkpt"); //$NON-NLS-1$ + } + serializer.endTag(null, "trkseg"); //$NON-NLS-1$ + } + writeExtensions(serializer, track); + serializer.endTag(null, "trk"); //$NON-NLS-1$ + } + } + + for (Route track : file.routes) { + serializer.startTag(null, "rte"); //$NON-NLS-1$ + writeNotNullText(serializer, "name", track.name); + writeNotNullText(serializer, "desc", track.desc); + + for (WptPt p : track.points) { + serializer.startTag(null, "rtept"); //$NON-NLS-1$ + writeWpt(format, serializer, p); + serializer.endTag(null, "rtept"); //$NON-NLS-1$ + } + writeExtensions(serializer, track); + serializer.endTag(null, "rte"); //$NON-NLS-1$ + } + + for (WptPt l : file.points) { + serializer.startTag(null, "wpt"); //$NON-NLS-1$ + writeWpt(format, serializer, l); + serializer.endTag(null, "wpt"); //$NON-NLS-1$ + } + + serializer.endTag(null, "gpx"); //$NON-NLS-1$ + serializer.endDocument(); + serializer.flush(); + + + } catch (RuntimeException e) { + log.error("Error saving gpx", e); //$NON-NLS-1$ + return "Error saving gpx"; + } catch (IOException e) { + log.error("Error saving gpx", e); //$NON-NLS-1$ + return "Error saving gpx"; + } + return null; + } + + private static String getFilename(String path) { + if (path != null) { + int i = path.lastIndexOf('/'); + if (i > 0) { + path = path.substring(i + 1); + } + i = path.lastIndexOf('.'); + if (i > 0) { + path = path.substring(0, i); + } + } + return path; + } + + private static void writeNotNullText(XmlSerializer serializer, String tag, String value) throws IOException { + if (value != null) { + serializer.startTag(null, tag); + serializer.text(value); + serializer.endTag(null, tag); + } + } + + private static void writeExtensions(XmlSerializer serializer, GPXExtensions p) throws IOException { + if (!p.getExtensionsToRead().isEmpty()) { + serializer.startTag(null, "extensions"); + for (Map.Entry s : p.getExtensionsToRead().entrySet()) { + writeNotNullText(serializer, s.getKey(), s.getValue()); + } + serializer.endTag(null, "extensions"); + } + } + + private static void writeWpt(SimpleDateFormat format, XmlSerializer serializer, WptPt p) throws IOException { + serializer.attribute(null, "lat", latLonFormat.format(p.lat)); //$NON-NLS-1$ //$NON-NLS-2$ + serializer.attribute(null, "lon", latLonFormat.format(p.lon)); //$NON-NLS-1$ //$NON-NLS-2$ + + if (!Double.isNaN(p.ele)) { + writeNotNullText(serializer, "ele", decimalFormat.format(p.ele)); + } + if (p.time != 0) { + writeNotNullText(serializer, "time", format.format(new Date(p.time))); + } + writeNotNullText(serializer, "name", p.name); + writeNotNullText(serializer, "desc", p.desc); + if (p.link != null) { + serializer.startTag(null, "link"); + serializer.attribute(null, "href", p.link); + serializer.endTag(null, "link"); + } + writeNotNullText(serializer, "type", p.category); + if (p.comment != null) { + writeNotNullText(serializer, "cmt", p.comment); + } + if (!Double.isNaN(p.hdop)) { + writeNotNullText(serializer, "hdop", decimalFormat.format(p.hdop)); + } + if (p.speed > 0) { + p.getExtensionsToWrite().put("speed", decimalFormat.format(p.speed)); + } + writeExtensions(serializer, p); + } + + public static class GPXFileResult { + public ArrayList> locations = new ArrayList>(); + public ArrayList wayPoints = new ArrayList<>(); + // special case for cloudmate gpx : they discourage common schema + // by using waypoint as track points and rtept are not very close to real way + // such as wpt. However they provide additional information into gpx. + public boolean cloudMadeFile; + public String error; + + public Location findFistLocation() { + for (List l : locations) { + for (Location ls : l) { + if (ls != null) { + return ls; + } + } + } + return null; + } + } + + private static String readText(XmlPullParser parser, String key) throws XmlPullParserException, IOException { + int tok; + String text = null; + while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (tok == XmlPullParser.END_TAG && parser.getName().equals(key)) { + break; + } else if (tok == XmlPullParser.TEXT) { + if (text == null) { + text = parser.getText(); + } else { + text += parser.getText(); + } + } + + } + return text; + } + + public static GPXFile loadGPXFile(Context ctx, File f) { + FileInputStream fis = null; + try { + fis = new FileInputStream(f); + GPXFile file = loadGPXFile(ctx, fis); + file.path = f.getAbsolutePath(); + try { + fis.close(); + } catch (IOException e) { + } + return file; + } catch (IOException e) { + GPXFile res = new GPXFile(); + res.path = f.getAbsolutePath(); + log.error("Error reading gpx " + res.path, e); //$NON-NLS-1$ + res.warning = "Error reading gpx "; + return res; + } finally { + try { + if (fis != null) + fis.close(); + } catch (IOException ignore) { + // ignore + } + } + } + + public static GPXFile loadGPXFile(Context ctx, InputStream f) { + GPXFile res = new GPXFile(); + SimpleDateFormat format = new SimpleDateFormat(GPX_TIME_FORMAT, Locale.US); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + SimpleDateFormat formatMillis = new SimpleDateFormat(GPX_TIME_FORMAT_MILLIS, Locale.US); + formatMillis.setTimeZone(TimeZone.getTimeZone("UTC")); + try { + XmlPullParser parser = PlatformUtil.newXMLPullParser(); + parser.setInput(getUTF8Reader(f)); + Stack parserState = new Stack<>(); + boolean extensionReadMode = false; + parserState.push(res); + int tok; + while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (tok == XmlPullParser.START_TAG) { + Object parse = parserState.peek(); + String tag = parser.getName(); + if (extensionReadMode && parse instanceof GPXExtensions) { + String value = readText(parser, tag); + if (value != null) { + ((GPXExtensions) parse).getExtensionsToWrite().put(tag.toLowerCase(), value); + if (tag.equals("speed") && parse instanceof WptPt) { + try { + ((WptPt) parse).speed = Float.parseFloat(value); + } catch (NumberFormatException e) { + } + } + } + + } else if (parse instanceof GPXExtensions && tag.equals("extensions")) { + extensionReadMode = true; + } else { + if (parse instanceof GPXFile) { + if (tag.equals("gpx")) { + ((GPXFile) parse).author = parser.getAttributeValue("", "creator"); + } + if (tag.equals("metadata")) { + Metadata metadata = new Metadata(); + ((GPXFile) parse).metadata = metadata; + parserState.push(metadata); + } + if (tag.equals("trk")) { + Track track = new Track(); + ((GPXFile) parse).tracks.add(track); + parserState.push(track); + } + if (tag.equals("rte")) { + Route route = new Route(); + ((GPXFile) parse).routes.add(route); + parserState.push(route); + } + if (tag.equals("wpt")) { + WptPt wptPt = parseWptAttributes(parser); + ((GPXFile) parse).points.add(wptPt); + parserState.push(wptPt); + } + } else if (parse instanceof Metadata) { + if (tag.equals("desc")) { + ((Metadata) parse).desc = readText(parser, "desc"); + } + } else if (parse instanceof Route) { + if (tag.equals("name")) { + ((Route) parse).name = readText(parser, "name"); + } + if (tag.equals("desc")) { + ((Route) parse).desc = readText(parser, "desc"); + } + if (tag.equals("rtept")) { + WptPt wptPt = parseWptAttributes(parser); + ((Route) parse).points.add(wptPt); + parserState.push(wptPt); + } + } else if (parse instanceof Track) { + if (tag.equals("name")) { + ((Track) parse).name = readText(parser, "name"); + } + if (tag.equals("desc")) { + ((Track) parse).desc = readText(parser, "desc"); + } + if (tag.equals("trkseg")) { + TrkSegment trkSeg = new TrkSegment(); + ((Track) parse).segments.add(trkSeg); + parserState.push(trkSeg); + } + } else if (parse instanceof TrkSegment) { + if (tag.equals("trkpt")) { + WptPt wptPt = parseWptAttributes(parser); + ((TrkSegment) parse).points.add(wptPt); + parserState.push(wptPt); + } + if (tag.equals("csvattributes")) { + String segmentPoints = readText(parser, "csvattributes"); + String[] pointsArr = segmentPoints.split("\n"); + for (int i = 0; i < pointsArr.length; i++) { + String[] pointAttrs = pointsArr[i].split(","); + try { + int arrLength = pointsArr.length; + if (arrLength > 1) { + WptPt wptPt = new WptPt(); + wptPt.lon = Double.parseDouble(pointAttrs[0]); + wptPt.lat = Double.parseDouble(pointAttrs[1]); + ((TrkSegment) parse).points.add(wptPt); + if (arrLength > 2) { + wptPt.ele = Double.parseDouble(pointAttrs[2]); + } + } + } catch (NumberFormatException e) { + } + } + } + // main object to parse + } else if (parse instanceof WptPt) { + if (tag.equals("name")) { + ((WptPt) parse).name = readText(parser, "name"); + } else if (tag.equals("desc")) { + ((WptPt) parse).desc = readText(parser, "desc"); + } else if (tag.equals("cmt")) { + ((WptPt) parse).comment = readText(parser, "cmt"); + } else if (tag.equals("speed")) { + try { + String value = readText(parser, "speed"); + ((WptPt) parse).speed = Float.parseFloat(value); + ((WptPt) parse).getExtensionsToWrite().put("speed", value); + } catch (NumberFormatException e) { + } + } else if (tag.equals("link")) { + ((WptPt) parse).link = parser.getAttributeValue("", "href"); + } else if (tag.equals("category")) { + ((WptPt) parse).category = readText(parser, "category"); + } else if (tag.equals("type")) { + if (((WptPt) parse).category == null) { + ((WptPt) parse).category = readText(parser, "type"); + } + } else if (tag.equals("ele")) { + String text = readText(parser, "ele"); + if (text != null) { + try { + ((WptPt) parse).ele = Float.parseFloat(text); + } catch (NumberFormatException e) { + } + } + } else if (tag.equals("hdop")) { + String text = readText(parser, "hdop"); + if (text != null) { + try { + ((WptPt) parse).hdop = Float.parseFloat(text); + } catch (NumberFormatException e) { + } + } + } else if (tag.equals("time")) { + String text = readText(parser, "time"); + if (text != null) { + try { + ((WptPt) parse).time = format.parse(text).getTime(); + } catch (ParseException e1) { + try { + ((WptPt) parse).time = formatMillis.parse(text).getTime(); + } catch (ParseException e2) { + + } + } + } + } + } + } + + } else if (tok == XmlPullParser.END_TAG) { + Object parse = parserState.peek(); + String tag = parser.getName(); + if (parse instanceof GPXExtensions && tag.equals("extensions")) { + extensionReadMode = false; + } + + if (tag.equals("metadata")) { + Object pop = parserState.pop(); + assert pop instanceof Metadata; + } else if (tag.equals("trkpt")) { + Object pop = parserState.pop(); + assert pop instanceof WptPt; + } else if (tag.equals("wpt")) { + Object pop = parserState.pop(); + assert pop instanceof WptPt; + } else if (tag.equals("rtept")) { + Object pop = parserState.pop(); + assert pop instanceof WptPt; + } else if (tag.equals("trk")) { + Object pop = parserState.pop(); + assert pop instanceof Track; + } else if (tag.equals("rte")) { + Object pop = parserState.pop(); + assert pop instanceof Route; + } else if (tag.equals("trkseg")) { + Object pop = parserState.pop(); + assert pop instanceof TrkSegment; + } + } + } + } catch (RuntimeException e) { + log.error("Error reading gpx", e); //$NON-NLS-1$ + res.warning = "Error reading gpx" + " " + e.getMessage(); + } catch (XmlPullParserException e) { + log.error("Error reading gpx", e); //$NON-NLS-1$ + res.warning = "Error reading gpx" + " " + e.getMessage(); + } catch (IOException e) { + log.error("Error reading gpx", e); //$NON-NLS-1$ + res.warning = "Error reading gpx" + " " + e.getMessage(); + } + + return res; + } + + private static Reader getUTF8Reader(InputStream f) throws IOException { + BufferedInputStream bis = new BufferedInputStream(f); + assert bis.markSupported(); + bis.mark(3); + boolean reset = true; + byte[] t = new byte[3]; + bis.read(t); + if (t[0] == ((byte) 0xef) && t[1] == ((byte) 0xbb) && t[2] == ((byte) 0xbf)) { + reset = false; + } + if (reset) { + bis.reset(); + } + return new InputStreamReader(bis, "UTF-8"); + } + + private static WptPt parseWptAttributes(XmlPullParser parser) { + WptPt wpt = new WptPt(); + try { + wpt.lat = Double.parseDouble(parser.getAttributeValue("", "lat")); //$NON-NLS-1$ //$NON-NLS-2$ + wpt.lon = Double.parseDouble(parser.getAttributeValue("", "lon")); //$NON-NLS-1$ //$NON-NLS-2$ + } catch (NumberFormatException e) { + } + return wpt; + } + + public static void mergeGPXFileInto(GPXFile to, GPXFile from) { + if (from == null) { + return; + } + if (from.showCurrentTrack) { + to.showCurrentTrack = true; + } + if (from.points != null) { + to.points.addAll(from.points); + } + if (from.tracks != null) { + to.tracks.addAll(from.tracks); + } + if (from.routes != null) { + to.routes.addAll(from.routes); + } + if (from.warning != null) { + to.warning = from.warning; + } + } +} diff --git a/OsmAnd-telegram/src/net/osmand/telegram/utils/LocationPoint.java b/OsmAnd-telegram/src/net/osmand/telegram/utils/LocationPoint.java new file mode 100644 index 0000000000..220896e389 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/telegram/utils/LocationPoint.java @@ -0,0 +1,24 @@ +package net.osmand.telegram.utils; + + +import android.content.Context; + +/** + */ +public interface LocationPoint { + + public double getLatitude(); + + public double getLongitude(); + + public int getColor(); + + public boolean isVisible(); + +// public PointDescription getPointDescription(Context ctx); + +// public String getSpeakableName(); + + //public void prepareCommandPlayer(CommandBuilder cmd, String names); + +} \ No newline at end of file diff --git a/OsmAnd/res/values-es-rAR/phrases.xml b/OsmAnd/res/values-es-rAR/phrases.xml index fae56018ab..4e52a02713 100644 --- a/OsmAnd/res/values-es-rAR/phrases.xml +++ b/OsmAnd/res/values-es-rAR/phrases.xml @@ -3850,24 +3850,24 @@ Servicio automotriz -Taller mecánico +Taller para automóviles Cambio de aceite Venta de autos usados Frenos Venta de autos nuevos - Diagnóstico + Diagnósticos Autopartes Baterías Aire acondicionado - Reparación de carrocería + Reparación de carrocerías Eléctrico Ruedas - Vidrio - Taller mecánico para camiones + Vidrios + Taller para camiones Silenciador Alineación Reparación de la transmisión - Motor + Motores Aseguradora Neumáticos diff --git a/OsmAnd/res/values-nb/strings.xml b/OsmAnd/res/values-nb/strings.xml index 1815f2d10f..5f08ac48f7 100644 --- a/OsmAnd/res/values-nb/strings.xml +++ b/OsmAnd/res/values-nb/strings.xml @@ -2370,7 +2370,7 @@ Kartmarkør flyttet til aktive Liste Sist brukt: %1$s - Filtrer bilder etter innsender eller etter dato. Kun aktiv på høyere zoomnivåer. + Filtrer bilder etter innsender, dato eller type. Kun aktivt på nærgående forstørrelsesnivå. Installer Mapillary for å legge til et eller flere bilder til denne kartposisjonen. Foto på gatenivå for alle. Oppdag plasser, samarbeid, fang inn verden. Vis/skjul OSM-notater diff --git a/OsmAnd/res/values-sv/phrases.xml b/OsmAnd/res/values-sv/phrases.xml index 355c5b396a..480b92653e 100644 --- a/OsmAnd/res/values-sv/phrases.xml +++ b/OsmAnd/res/values-sv/phrases.xml @@ -1,4 +1,4 @@ - + Skoaffär Datorbutik Butik @@ -3105,4 +3105,199 @@ Troika Stöd: tak - + Massköp + Typ + Bok + Bilpoolplats + Bankomat + Pumptyp: domkraftspump + Pumptyp: India Mk II eller III> + Troika-kort accepteras ej + Stöd: billboard + Stöd: upphängd + Stöd: tak + Stöd: torn + Skärm för passagerarinformation: ja + Skärm för passagerarinformation: nej + Vattenbruk + Vattenbruk: räkor + Vattenbruk: fisk + Vattenbruk: musslor + Lägsta ålder + Ja + Nej + Endast + Trafikspegel + Konsulat + Generalkonsulat + Honorärkonsulat + Delegation + Ambassadörsresidens + Vattentank + Längd + Wikipedia + Jul + Julhändelse + Julmarknad + Julpyramid + Julbutik + Julgranshandel + Julgran + Kök + Pizza + Hamburgare + Kaffe + Smörgås + Kebab + Dönerkebab (shawarma) + Kyckling + Glass + Sushi + Mellanmål + Yoghurt + Gyros + Empanada + Crêpes + Yakiniku + Suki + Udon + Brasserie + Bubbelte + Yakitori + Sagardotegia + Kött + Vingar + Våfflor + Choklad + Vin + Potatis + Brunch + Sub + Pita + Fondue + Baguette + Pastell + Burrito + Teriyaki + Shawarma + Regionalt + Italienskt + Kinesiskt + Mexikanskt + Japanskt + Tyskt + Indiskt + Amerikanskt + Asiatiskt + Franskt + Grekiskt + Thailändskt + Internationellt + Turkiskt + Spanskt + Vietnamesiskt + Koreanskt + Medelhavs + Bayerskt + "Libanesiskt " + Ryskt + Filippinskt + Portugisiskt + Georgiskt + Polskt + Brasilianskt + Arabiskt + Danskt + Indonesiskt + Afrikanskt + Karibiskt + Argentinskt + Balkan + Peruanskt + Kroatiskt + Bolivianskt + Malagassiskt + Persiskt + Marockanskt + Österrikiskt + Malajiskt + Irländskt + Etiopiskt + Ungerskt + Laotiskt + Europeiskt + Uzbekiskt + Tjeckiskt + Kubanskt + Brittiskt + Latinamerikanskt + Nepalesiskt + Mongoliskt + Mellanöstern + Ukrainskt + Afghanskt + Belgiskt + Baskiskt + "Schweiziskt " + Kantonesiskt + Svenskt + Jamaicanskt + Armeniskt + "Hawaiianskt " + Engelskt + Pakistanskt + Taiwanesiskt + Tex-mex + Holländskt + Syrianskt + Australienskt + Cajun + Egyptiskt + Senegalesiskt + Judiskt + Bulgariskt + Tibetanskt + Plats för utfodring av djur + Festutrustning + Elbutik + Låssmed + Belysning + Lotterier + Typ + Lotteri + Pachinko + Spelautomater + Betting + Bingo + E-cigarettbutik + Lok + Kosttillskott + Fotostudio + Klippa + Djurhållning + Djurhållning: häst + Djurhållning: får + Typ: paddock + Typ: öppet stall + Konstruktion: fackverk + Konstruktion: fristående + Konstruktion: disk + Konstruktion: dom + Konstruktion: dold + Fraktstation + Ja + Vin: ja + Vin: detaljhandel + Vin: serveras + Energileverantör + Reparation av elektronik: datorer + Elektronik reparationer: apparater + Reparation av elektronik: telefon + Reparation av elektronik: tv + Barnläger + Musikskola + Språkskola + Livboj + Safaripark + Fåglar + \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java index 1dfbec146a..dc87ba73cb 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java @@ -364,6 +364,7 @@ public class MapActivityActions implements DialogProvider { adapter.addItem(new ContextMenuItem.ItemBuilder() .setTitleId(R.string.context_menu_item_add_waypoint, mapActivity) .setTitle(MAP_CONTEXT_MENU_ADD_GPX_WAYPOINT) + .setId(POINT_ADD_GPX_WAYPOINT) .setIcon(R.drawable.ic_action_gnew_label_dark) .setOrder(ADD_GPX_WAYPOINT_ITEM_ORDER) .setListener(listener).createItem()); diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java index 88f3431eb3..e8fbe08c66 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java @@ -233,7 +233,7 @@ public class DownloadActivityType { } public String getBaseUrl(OsmandApplication ctx, String fileName) { - String url = "http://" + IndexConstants.INDEX_DOWNLOAD_DOMAIN + "/download?event=2&" + String url = "https://" + IndexConstants.INDEX_DOWNLOAD_DOMAIN + "/download?event=2&" + Version.getVersionAsURLParam(ctx) + "&file=" + encode(fileName); if(this == LIVE_UPDATES_FILE && fileName.length() > 16) { // DATE_AND_EXT_STR_LEN = "_18_06_02.obf.gz".length()