diff --git a/OsmAnd-java/src/main/java/net/osmand/NativeLibrary.java b/OsmAnd-java/src/main/java/net/osmand/NativeLibrary.java index 49f08c1372c..5d182f23208 100644 --- a/OsmAnd-java/src/main/java/net/osmand/NativeLibrary.java +++ b/OsmAnd-java/src/main/java/net/osmand/NativeLibrary.java @@ -2,6 +2,7 @@ import static net.osmand.IndexConstants.GPX_FILE_EXT; import static net.osmand.IndexConstants.GPX_GZ_FILE_EXT; +import static net.osmand.data.Amenity.ROUTE_ID; import java.io.File; import java.io.FileOutputStream; @@ -696,7 +697,7 @@ public List getOriginalNames() { public String getRouteID() { for (Map.Entry entry : getTags().entrySet()) { - if ("route_id".equals(entry.getKey())) { + if (ROUTE_ID.equals(entry.getKey())) { return entry.getValue(); } } diff --git a/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java b/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java index 13e72fae141..d57d0c6457e 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java @@ -1,7 +1,11 @@ package net.osmand.data; -import static net.osmand.gpx.GPXUtilities.AMENITY_PREFIX; -import static net.osmand.gpx.GPXUtilities.OSM_PREFIX; +import static net.osmand.gpx.GPXUtilities.*; +import static net.osmand.osm.MapPoiTypes.ROUTES_PREFIX; +import static net.osmand.osm.MapPoiTypes.ROUTE_ARTICLE_POINT; +import static net.osmand.osm.MapPoiTypes.ROUTE_TRACK; +import static net.osmand.osm.MapPoiTypes.ROUTE_TRACK_POINT; +import static net.osmand.shared.gpx.GpxFile.XML_COLON; import net.osmand.Location; import net.osmand.binary.BinaryMapIndexReader.TagValuePair; @@ -48,6 +52,7 @@ public class Amenity extends MapObject { public static final String IS_AGGR_PART = "is_aggr_part"; public static final String CONTENT_JSON = "content_json"; public static final String ROUTE_ID = "route_id"; + public static final String ROUTE_ID_OSM_PREFIX = "OSM"; public static final String ROUTE_SOURCE = "route_source"; public static final String ROUTE_NAME = "route_name"; public static final String COLOR = "color"; @@ -169,7 +174,7 @@ public String getAdditionalInfo(String key) { } String str = additionalInfo.get(key); if (str == null && key.contains(":")) { - str = additionalInfo.get(key.replaceAll(":", "_-_")); // try content_-_uk after content:uk + str = additionalInfo.get(key.replaceAll(":", XML_COLON)); // try content_-_uk after content:uk } if (str != null) { str = unzipContent(str); @@ -309,7 +314,7 @@ public StringBuilder printNamesAndAdditional() { private void printNames(String prefix, Map stringMap, StringBuilder s) { for (Entry e : stringMap.entrySet()) { if (e.getValue().startsWith(" gz ")) { - s.append(prefix).append(e.getKey()).append("='gzip ...'"); + s.append(prefix).append(e.getKey()).append("='gzip ...' "); } else { s.append(prefix).append(e.getKey()).append("='").append(e.getValue()).append("' "); } @@ -347,10 +352,11 @@ public String getColor() { } public String getGpxIcon() { - return getAdditionalInfo(GPX_ICON); + String wikiVoyageIcon = getAdditionalInfo(GPX_ICON); + String travelGpxIcon = getAdditionalInfo(ICON_NAME_EXTENSION); + return Algorithms.isEmpty(wikiVoyageIcon) ? travelGpxIcon : wikiVoyageIcon; } - public String getContentLanguage(String tag, String lang, String defLang) { if (lang != null) { String translateName = getAdditionalInfo(tag + ":" + lang); @@ -432,7 +438,7 @@ public String getTagContent(String tag, String lang) { return translateName; } for (String nm : getAdditionalInfoKeys()) { - if (nm.startsWith(tag + ":") || nm.startsWith(tag + "_-_")) { + if (nm.startsWith(tag + ":") || nm.startsWith(tag + XML_COLON)) { return getAdditionalInfo(nm); } } @@ -447,6 +453,25 @@ public String getRouteId() { return getAdditionalInfo(ROUTE_ID); } + public boolean hasOsmRouteId() { + String routeId = getRouteId(); + return routeId != null && routeId.startsWith(ROUTE_ID_OSM_PREFIX); + } + + public String getGpxFileName(String lang) { + final String gpxFileName = lang != null ? getName(lang) : getEnName(true); + if (!Algorithms.isEmpty(gpxFileName)) { + return gpxFileName; + } + if (!Algorithms.isEmpty(getRouteId())) { + return getRouteId(); + } + if (!Algorithms.isEmpty(getSubType())) { + return getType().getKeyName() + " " + getSubType(); + } + return getType().getKeyName(); + } + public String getStrictTagContent(String tag, String lang) { if (lang != null) { String translateName = getAdditionalInfo(tag + ":" + lang); @@ -549,6 +574,14 @@ public boolean isPrivateAccess() { return PRIVATE_VALUE.equals(getTagContent(ACCESS_PRIVATE_TAG)); } + public boolean isRouteTrack() { + return subType != null && (subType.equals(ROUTE_TRACK) || subType.startsWith(ROUTES_PREFIX)); + } + + public boolean isRoutePoint() { + return subType != null && (subType.equals(ROUTE_TRACK_POINT) || subType.equals(ROUTE_ARTICLE_POINT)); + } + public JSONObject toJSON() { JSONObject json = super.toJSON(); json.put("subType", subType); diff --git a/OsmAnd-java/src/main/java/net/osmand/gpx/GPXUtilities.java b/OsmAnd-java/src/main/java/net/osmand/gpx/GPXUtilities.java index e7a42961fbb..fc4d91f3b1d 100644 --- a/OsmAnd-java/src/main/java/net/osmand/gpx/GPXUtilities.java +++ b/OsmAnd-java/src/main/java/net/osmand/gpx/GPXUtilities.java @@ -3,6 +3,7 @@ import static net.osmand.gpx.GPXUtilities.RouteSegment.START_TRKPT_IDX_ATTR; +import static net.osmand.shared.gpx.GpxFile.XML_COLON; import static net.osmand.util.Algorithms.isDigit; import net.osmand.IProgress; @@ -1363,7 +1364,7 @@ private static String getOsmandTagKey(final Entry entry) { if (key.startsWith(OSMAND_EXTENSIONS_PREFIX)) { key = key.replace(OSMAND_EXTENSIONS_PREFIX, ""); } - key = key.replace(":", "_-_"); + key = key.replace(":", XML_COLON); return OSMAND_EXTENSIONS_PREFIX + key; } diff --git a/OsmAnd-java/src/main/java/net/osmand/osm/MapPoiTypes.java b/OsmAnd-java/src/main/java/net/osmand/osm/MapPoiTypes.java index 05170babbd1..8e14cf29fd8 100644 --- a/OsmAnd-java/src/main/java/net/osmand/osm/MapPoiTypes.java +++ b/OsmAnd-java/src/main/java/net/osmand/osm/MapPoiTypes.java @@ -31,7 +31,7 @@ public class MapPoiTypes { - private static final String OTHER_MAP_CATEGORY = "Other"; + public static final String OTHER_MAP_CATEGORY = "Other"; private static MapPoiTypes DEFAULT_INSTANCE = null; private static final Log log = PlatformUtil.getLog(MapRenderingTypes.class); private String resourceName; @@ -48,7 +48,8 @@ public class MapPoiTypes { public static final String ROUTE_ARTICLE = "route_article"; public static final String ROUTE_ARTICLE_POINT = "route_article_point"; public static final String CATEGORY = "category"; - public static final String ROUTE_TRACK = "route_track"; + public static final String ROUTE_TRACK = "route_track"; // routes:route_track (no activity) + public static final String ROUTES_PREFIX = "routes_"; // routes:routes_xxx (any activity type) public static final String ROUTE_TRACK_POINT = "route_track_point"; private PoiTranslator poiTranslator = null; @@ -970,11 +971,7 @@ public String replaceDeprecatedSubtype(PoiCategory type, String subtype) { } public Amenity parseAmenity(String tag, String val, boolean relation, Map otherTags) { - initPoiTypesByTag(); - PoiType pt = poiTypesByTag.get(tag + "/" + val); - if (pt == null) { - pt = poiTypesByTag.get(tag); - } + PoiType pt = getPoiTypeByTagValue(tag, val); if (pt == null || pt.isAdditional()) { return null; } @@ -1032,6 +1029,19 @@ public Amenity parseAmenity(String tag, String val, boolean relation, Map getWikidataImageWikidata(String wikidataId, Lis private static List getImagesOsmAndAPIRequestV2(String url, List wikiImages) { OsmandAPIResponseV2 response = sendWikipediaApiRequest(url, OsmandAPIResponseV2.class); if (response != null && !Algorithms.isEmpty(response.images)) { - for (Map image : response.images) { + for (Map image : response.images) { WikiImage wikiImage = parseImageDataWithMetaData(image); if (wikiImage != null && isUrlFileImage(wikiImage)) { wikiImages.add(wikiImage); @@ -187,25 +187,27 @@ private static List getImagesOsmAndAPIRequest(String url, List image) { - String imageUrl = (String) image.get("image"); + private static WikiImage parseImageDataWithMetaData(Map image) { + String imageUrl = image.get("image"); if (!Algorithms.isEmpty(image)) { WikiImage wikiImage = parseImageDataFromFile(imageUrl); if (wikiImage != null) { Metadata metadata = wikiImage.getMetadata(); - String date = (String) image.get("date"); + String date = image.get("date"); if (date != null) { metadata.setDate(date); } - String author = (String) image.get("author"); + String author = image.get("author"); if (date != null) { metadata.setAuthor(author); } - String license = (String) image.get("license"); + String license = image.get("license"); if (date != null) { metadata.setLicense(license); } + long mediaId = Algorithms.parseLongSilently(image.get("mediaId"), -1); + wikiImage.setMediaId(mediaId); return wikiImage; } } @@ -258,7 +260,7 @@ private static T sendWikipediaApiRequest(String url, Class responseClass) public static class OsmandAPIResponseV2 { @SerializedName("features-v2") @Expose - private final Set> images = null; + private final Set> images = null; } public static class OsmandAPIResponse { diff --git a/OsmAnd-java/src/main/java/net/osmand/wiki/WikiImage.java b/OsmAnd-java/src/main/java/net/osmand/wiki/WikiImage.java index 27e98264420..bb241c51159 100644 --- a/OsmAnd-java/src/main/java/net/osmand/wiki/WikiImage.java +++ b/OsmAnd-java/src/main/java/net/osmand/wiki/WikiImage.java @@ -9,6 +9,7 @@ public class WikiImage { private final String imageName; private final String imageStubUrl; private final String imageHiResUrl; + private long mediaId = -1; private final Metadata metadata = new Metadata(); @@ -19,6 +20,14 @@ public WikiImage(String wikiMediaTag, String imageName, String imageStubUrl, Str this.imageHiResUrl = imageHiResUrl; } + public void setMediaId(long mediaId) { + this.mediaId = mediaId; + } + + public long getMediaId() { + return mediaId; + } + public String getUrlWithCommonAttributions() { return WIKIMEDIA_COMMONS_URL + WIKIMEDIA_FILE + wikiMediaTag; } diff --git a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/GpxFile.kt b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/GpxFile.kt index 04ac06a4c1c..b79d879a1d4 100644 --- a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/GpxFile.kt +++ b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/GpxFile.kt @@ -894,5 +894,6 @@ class GpxFile : GpxExtensions { companion object { const val OSMAND_AUTHOR_PREFIX = "OsmAnd" const val DEFAULT_WPT_GROUP_NAME = "" + const val XML_COLON = "_-_" } } diff --git a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/GpxUtilities.kt b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/GpxUtilities.kt index c9c43baecf7..f629663c4e7 100644 --- a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/GpxUtilities.kt +++ b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/GpxUtilities.kt @@ -11,6 +11,7 @@ import kotlinx.datetime.toLocalDateTime import net.osmand.shared.KException import net.osmand.shared.data.KQuadRect import net.osmand.shared.extensions.currentTimeMillis +import net.osmand.shared.gpx.GpxFile.Companion.XML_COLON import net.osmand.shared.gpx.primitives.Author import net.osmand.shared.gpx.primitives.Bounds import net.osmand.shared.gpx.primitives.Copyright @@ -349,6 +350,7 @@ object GpxUtilities { const val OBF_POINTS_GROUPS_ICONS = "points_groups_icons" const val OBF_POINTS_GROUPS_COLORS = "points_groups_colors" const val OBF_POINTS_GROUPS_BACKGROUNDS = "points_groups_backgrounds" + const val OBF_POINTS_GROUPS_EMPTY_NAME_STUB = "." // stub to store empty points_groups_names const val OBF_POINTS_GROUPS_CATEGORY = "points_groups_category" // optional category of OBF-GPX point fun parsePointsGroupAttributes(parser: XmlPullParser): PointsGroup { @@ -818,7 +820,7 @@ object GpxUtilities { if (newKey.startsWith(OSMAND_EXTENSIONS_PREFIX)) { newKey = newKey.replace(OSMAND_EXTENSIONS_PREFIX, "") } - newKey = newKey.replace(":", "_-_") + newKey = newKey.replace(":", XML_COLON) return OSMAND_EXTENSIONS_PREFIX + newKey } @@ -999,6 +1001,7 @@ object GpxUtilities { extensionsReader: GpxExtensionsReader?, addGeneralTrack: Boolean ): GpxFile { + val insideTagDepth = mutableMapOf("trk" to 0) oneOffLogParseTimeErrors = true val gpxFile = GpxFile(null) gpxFile.metadata.time = 0 @@ -1032,6 +1035,7 @@ object GpxUtilities { if (tok == XmlPullParser.START_TAG) { val parse = parserState.lastOrNull() val tag = parser.getName() ?: "" + insideTagDepth[tag]?.let { insideTagDepth[tag] = it + 1} if (extensionReadMode && parse != null && !routePointExtension) { val tagName = tag.lowercase() when { @@ -1062,8 +1066,8 @@ object GpxUtilities { } } - tagName == "route" -> routeExtension = true - tagName == "types" -> typesExtension = true + tagName == "route" && insideTagDepth["trk"]!! > 0 -> routeExtension = true + tagName == "types" && insideTagDepth["trk"]!! > 0 -> typesExtension = true tagName == "points_groups" -> pointsGroupsExtension = true tagName == "network_route" -> networkRoute = true else -> { @@ -1314,7 +1318,8 @@ object GpxUtilities { } } else if (tok == XmlPullParser.END_TAG) { val parse = parserState.lastOrNull() - val tag = parser.getName() + val tag = parser.getName() ?: "" + insideTagDepth[tag]?.let { insideTagDepth[tag] = it - 1} if (tag.equals("routepointextension", ignoreCase = true)) { routePointExtension = false @@ -1661,5 +1666,4 @@ object GpxUtilities { i += if (processedPoints > 0) processedPoints else 1 } } - } diff --git a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/RouteActivityHelper.kt b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/RouteActivityHelper.kt index 0cc947be854..85c99daf1ff 100644 --- a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/RouteActivityHelper.kt +++ b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/RouteActivityHelper.kt @@ -45,6 +45,15 @@ object RouteActivityHelper { return cachedActivities } + fun findActivityByTag(tag: String): RouteActivity? { + for (activity in getActivities()) { + if (activity.tags != null && activity.tags.contains(tag)) { + return activity + } + } + return null + } + fun saveRouteActivity(trackItems: Collection, routeActivity: RouteActivity?) { runAsync { trackItems.forEach { trackItem -> @@ -124,7 +133,8 @@ object RouteActivityHelper { val activityId = activityJson["id"]!!.jsonPrimitive.content val activityLabel = activityJson["label"]!!.jsonPrimitive.content val iconName = activityJson["icon_name"]!!.jsonPrimitive.content - val activity = RouteActivity(activityId, activityLabel, iconName, activitiesGroup) + val tags = activityJson["tags"]?.jsonArray?.map { it.jsonPrimitive.content }?.toSet() + val activity = RouteActivity(activityId, activityLabel, iconName, activitiesGroup, tags) cachedActivities.add(activity) activities.add(activity) } diff --git a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/primitives/GpxExtensions.kt b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/primitives/GpxExtensions.kt index dde416446f2..aa771f6ae15 100644 --- a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/primitives/GpxExtensions.kt +++ b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/primitives/GpxExtensions.kt @@ -87,8 +87,4 @@ open class GpxExtensions { fun removeColor() { getExtensionsToWrite().remove(GpxUtilities.COLOR_NAME_EXTENSION) } - - companion object { - const val OBF_GPX_EXTENSION_TAG_PREFIX = "gpx_" // enlisted in poi_types.xml under name="route_track" - } } diff --git a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/primitives/RouteActivity.kt b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/primitives/RouteActivity.kt index ffa8981e566..4df75a8906d 100644 --- a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/primitives/RouteActivity.kt +++ b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/primitives/RouteActivity.kt @@ -4,7 +4,8 @@ data class RouteActivity( val id: String, val label: String, val iconName: String, - val group: RouteActivityGroup + val group: RouteActivityGroup, + val tags: Set? = null ) { override fun toString(): String { return id diff --git a/OsmAnd/res/values-ar/strings.xml b/OsmAnd/res/values-ar/strings.xml index 6e326e881b1..b3871a825ed 100644 --- a/OsmAnd/res/values-ar/strings.xml +++ b/OsmAnd/res/values-ar/strings.xml @@ -5745,11 +5745,11 @@ دورة بالدقيقة يوضح المسافة التي يمكن للمركبة قطعها على مستوى الوقود الأيسر فوق مستشعر OBD يعرض مستوى وقود السيارة عبر مستشعر OBD - درجة حرارة سائل تبريد المحرك - مستوى الوقود + درجة حرارة سائل التبريد + كفاءة الوقود (%) معدل استهلاك الوقود يعرض معدل استهلاك وقود السيارة استنادًا إلى OBD والموقع - درجة حرارة الهواء المحيط + درجة الحرارة المحيطة جهد البطارية تغيير طبقات النقطية تغيير طبقات الكنتور @@ -5791,8 +5791,8 @@ ميثانول الديزل إعدادات مقاييس السيارة - درجة حرارة الهواء الداخل - l/h + درجة الحرارة الداخلة + لتر/ساعة • تمت إضافة عارض ملء الشاشة لصور ويكيميديا\n\n• تم تقديم مكون إضافي جديد \"مقاييس المركبات\" لمراقبة أداء المركبات باستخدام بروتوكول OBD-II\n\n• تمت إضافة القدرة على تعيين الأنشطة للمسارات وتصفيتها وفقًا لذلك\n\n• تم تنفيذ إجراءات سريعة جديدة لتسجيل الرحلة وقفل شاشة اللمس\n\n• تم تقديم مظهر زر خريطة قابل للتخصيص وشبكة دقيقة\n\n• تمت إضافة قائمة سياقية وإجراء \"إعادة تعيين متوسط السرعة\" إلى الأدوات\n\n• تمت إضافة طبقة مسار جديدة \"مسارات الدراجات النارية\"\n\n• تم إصلاح \"التسجيل التلقائي للمسار أثناء التنقل\"\n\n• تم إصلاح مشكلة الإحداثيات المقلوبة في RTL\n\n• تم إصلاح بيانات المستشعر المفقودة للتسجيل الحالي للمسار\n\n• تمت إضافة معلومات حول المناطق المحيطة بالنقطة المحددة\n\n• تمت إضافة إجراء سريع للتحكم في رؤية تعديلات OSM\n\n• أدوات رؤية منفصلة لخيارات تلوين التضاريس\n\n• تمت إضافة أيقونة قابلة للنقر لعرض الوصول أو سمات الطريق أو المسار الأخرى الروابط الثالثة إعادة ضبط متوسط السرعة @@ -5859,7 +5859,7 @@ يظهر درجة حرارة سائل تبريد المحرك مستوى الوقود (لتر) يرجى التحقق مما يلي:\n- تأكد من تشغيل البلوتوث\n- تشغيل محرك السيارة\n- تأكد من أن جهازك ضمن نطاق الماسح الضوئي. - لـ/100كم + لتر/100كم حمولة المحرك المحسوبة معدل استهلاك الوقود (لتر/ساعة) معدل استهلاك الوقود (المستشعر) @@ -5870,7 +5870,7 @@ تتغير الأيقونة استنادًا إلى حالة الإجراء. متصّل بـ %s مدة تشغيل المحرك - يعرض معدل استهلاك الوقود للمركبة بناءً على الحسابات (لتر/كم) + يعرض معدل استهلاك الوقود للمركبة بناءً على الحسابات (لتر/100 كم) درجة حرارة زيت المحرك يعرض النسبة المئوية للطاقة المتوفرة للمحرك والتي يتم استخدامها في لحظة معينة. يظهر معدل استخدام الوقود بواسطة المحرك، والذي يتم حسابه على أساس الوقود المتبقي وحجم الخزان والمسافة المقطوعة كما هو محدد بواسطة نظام تحديد المواقع العالمي (GPS). @@ -5880,7 +5880,7 @@ ضغط الوقود يظهر معدل استهلاك الوقود للمركبة بناءً على الحسابات (لتر/ساعة) يعرض معدل استهلاك الوقود للمركبة بناءً على المستشعر (لتر/ساعة) - معدل استهلاك الوقود لتر/ساعة + معدل استهلاك الوقود معدل استهلاك الوقود %/ساعة توجيه الطريق سرعة السيارة @@ -5907,7 +5907,7 @@ الاتصال بـ %s لا يمكن الاقتران بالجهاز يعرض معدل استهلاك وقود السيارة بناءً على الحسابات (%/ساعة) - معدل استهلاك الوقود (لتر/كم) + معدل استهلاك الوقود (لتر/100 كم) كيلو باسكال لتر مستوى الوقود (%) @@ -5927,4 +5927,5 @@ سيتم تطبيق الوحدات المحددة على كافة البيانات المتعلقة بالحجم في الملف الشخصي الحالي. وحدة الحجم يعرض درجة الحرارة خارج السيارة. + الوضع المحدد \"%s\" \ No newline at end of file diff --git a/OsmAnd/res/values-b+sr+Latn/phrases.xml b/OsmAnd/res/values-b+sr+Latn/phrases.xml index 773b98f0f5d..8662fdb1d62 100644 --- a/OsmAnd/res/values-b+sr+Latn/phrases.xml +++ b/OsmAnd/res/values-b+sr+Latn/phrases.xml @@ -4883,18 +4883,7 @@ Članci o putovanjima Tačke na putovanjima Druge rute - Tačke drugih ruta - Bicikl - Pešačenje - Trčanje - Hodanje - Sportovi na vodi - Zimski sportovi - Koturaljke - Jahanje konja - Motorne sanke - Staze za trčanje - Brdski biciklizam + Tačke rute Etimologija imena Naziv leve strane Naziv desne strane @@ -4904,4 +4893,12 @@ Osnovno Standardno Uslužno + Motociklizam + Peške + Biciklizam + Vazdušni sportovi + Zimski sportovi + Vožnja + Sportovi na vodi + Druge rute \ No newline at end of file diff --git a/OsmAnd/res/values-cs/phrases.xml b/OsmAnd/res/values-cs/phrases.xml index 6e6da50a1eb..a4cf69a8d1e 100644 --- a/OsmAnd/res/values-cs/phrases.xml +++ b/OsmAnd/res/values-cs/phrases.xml @@ -4883,18 +4883,7 @@ Články o cestování Body na cestách Další trasy - Další body trasy - Kolo - Pěší turistika - Běh - Chůze - Vodní sporty - Jízda na koni - Zimní sporty - Sněžný skútr - Fitness stezky - Inline brusle - Horská cyklistika + Body trasy Původ názvu Název pravé strany Název levé strany @@ -4904,4 +4893,12 @@ Základní Standardní Se službami + Jízda na motorce + Chůze + Zimní sporty + Vzdušné sporty + Vodní sporty + Řízení vozidla + Jízda na kole + Další trasy \ No newline at end of file diff --git a/OsmAnd/res/values-da/phrases.xml b/OsmAnd/res/values-da/phrases.xml index b6123b68632..72291a62fa7 100644 --- a/OsmAnd/res/values-da/phrases.xml +++ b/OsmAnd/res/values-da/phrases.xml @@ -4070,4 +4070,39 @@ Simpel Standard Campingpladsens type + Cykling + Luftsport + Fod + Vandsport + Vintersport + Andre ruter + Motorcykling + Naturskøn kørsel + Langrend + Rygsækvandring + Spadsere + Skiløb + Snesko + El-cykling + El-mountainbike + Luftfart + Varmluftballon + Høstning + Ridning + Træklatring + Droneflyvning + Skøjteløb + Svæveflyvning + Snowboarding + Svampeplukning + Trailløb + Kajak + Svømning i det fri + Bil + Vandring + Snescooter + Trekking + Turski + Landevejscykling + Faldskærmsudspring \ No newline at end of file diff --git a/OsmAnd/res/values-de/phrases.xml b/OsmAnd/res/values-de/phrases.xml index c380b1c3d24..084f3aa3b05 100644 --- a/OsmAnd/res/values-de/phrases.xml +++ b/OsmAnd/res/values-de/phrases.xml @@ -4881,20 +4881,9 @@ Tunnel für Fußgänger verboten Tunnel für Radfahrer verboten Reisepunkte - Wassersport - Gehen - Reiten Reiseartikel Andere Routen - Weitere Routenpunkte - Fahrrad - Wandern - Laufen - Wintersport - Schneemobil - Fitness-Parcour - Inlineskates - Mountainbiken + Routenpunkte Namensetymologie Name der linken Seite Name der rechten Seite @@ -4904,4 +4893,13 @@ Standard Einfach Kletterfels + Fahrzeug + Motorradfahren + Wintersport + Radfahren + Luftsport + Andere Routen + Zu Fuß + Wassersport + Auto \ No newline at end of file diff --git a/OsmAnd/res/values-de/strings.xml b/OsmAnd/res/values-de/strings.xml index 0cf6fce8878..9ef9ca8156b 100644 --- a/OsmAnd/res/values-de/strings.xml +++ b/OsmAnd/res/values-de/strings.xml @@ -5682,4 +5682,5 @@ Legt den Prozentsatz der Standortinterpolation während der Routennavigation fest. Dieser Parameter reduziert die Verzögerung der Position des Standorts auf der Karte während der Animation. Interpolation Prozentualer Anteil der Standortinterpolation + Ausgewähltes Profil \"%s\" \ No newline at end of file diff --git a/OsmAnd/res/values-eo/phrases.xml b/OsmAnd/res/values-eo/phrases.xml index 2152de546ed..e84fdcbb809 100644 --- a/OsmAnd/res/values-eo/phrases.xml +++ b/OsmAnd/res/values-eo/phrases.xml @@ -4880,17 +4880,6 @@ Vojaĝaj punktoj Aliaj kursoj Aliaj kursoj: punktoj - Biciklado - Marŝado - Kurado - Irado - Akvaj sportoj - Ĉevalrajdado - Vintraj sportoj - Motorsledado - Vojetoj de sportiloj - Rulglitado - Montbiciklado Parco Parco malakceptata Parkster diff --git a/OsmAnd/res/values-es-rAR/phrases.xml b/OsmAnd/res/values-es-rAR/phrases.xml index 56a46027054..cc3510b979e 100644 --- a/OsmAnd/res/values-es-rAR/phrases.xml +++ b/OsmAnd/res/values-es-rAR/phrases.xml @@ -4883,18 +4883,7 @@ Artículos turísticos Puntos turísticos Otras rutas - Otros puntos de ruta - Bicicleta - Senderismo - Correr - Peatón - Deportes acuáticos - Equitación - Deportes de invierno - Motonieve - Circuitos aeróbicos - Patines en línea - Ciclismo de montaña + Puntos de ruta Nombre etimológico Nombre del lado izquierdo Nombre del lado derecho @@ -4904,4 +4893,20 @@ Con servicios Tipo de campamento De lujo + A pie + Deportes de invierno + Automóvil + Deportes aéreos + Motocicleta + Ciclismo + Deportes acuáticos + Otras rutas + Todo terreno + Motociclismo enduro + Motocross + Automóvil + Camión (HGV) + ATV (Vehículo Todo Terreno) + Karting + Motociclismo de aventura \ No newline at end of file diff --git a/OsmAnd/res/values-es-rAR/strings.xml b/OsmAnd/res/values-es-rAR/strings.xml index 7736c77cfa7..49d0894b7e4 100644 --- a/OsmAnd/res/values-es-rAR/strings.xml +++ b/OsmAnd/res/values-es-rAR/strings.xml @@ -5531,4 +5531,5 @@ Porcentaje de interpolación de la ubicación Define el porcentaje de interpolación de la ubicación, al navegar por la ruta. Esto reduce el desfase del movimiento de la ubicación en el mapa. Interpolación + Perfil elegido «%s» \ No newline at end of file diff --git a/OsmAnd/res/values-es-rUS/phrases.xml b/OsmAnd/res/values-es-rUS/phrases.xml index 44dae07f6ea..38bc55a04ff 100644 --- a/OsmAnd/res/values-es-rUS/phrases.xml +++ b/OsmAnd/res/values-es-rUS/phrases.xml @@ -4979,15 +4979,4 @@ Puntos turísticos Otras rutas Otros puntos de ruta - Bicicleta - Senderismo - Correr - Peatón - Deportes acuáticos - Equitación - Deportes de invierno - Motonieve - Circuitos aeróbicos - Patines en línea - Ciclismo de montaña \ No newline at end of file diff --git a/OsmAnd/res/values-es/phrases.xml b/OsmAnd/res/values-es/phrases.xml index bc9644cadbc..199371d9970 100644 --- a/OsmAnd/res/values-es/phrases.xml +++ b/OsmAnd/res/values-es/phrases.xml @@ -4880,21 +4880,10 @@ Parco Túnel prohibido para peatones Túnel prohibido para ciclistas - Bicicleta - Senderismo - Correr - Peatonal - Deportes acuáticos - Equitación - Motonieve - Circuitos aeróbicos - Patines en línea - Bicicleta de montaña Artículos de viajes Puntos del viaje Otras rutas - Otros puntos de la ruta - Deportes de invierno + Puntos de la ruta Nombre en el lado izquierdo Etimología del nombre Nombre en el lado derecho @@ -4904,4 +4893,20 @@ De lujo Tipo de camping Básico + Andando + Deportes de invierno + Ciclismo + Deportes aéreos + Otras rutas + Deportes acuáticos + Conduciendo + Motociclismo + ATV (Vehículo todoterreno) + Motociclismo enduro + Todoterreno + Karting + Motociclismo de aventura + Motocrós + Coche + Camión (vehículo pesado) \ No newline at end of file diff --git a/OsmAnd/res/values-et/phrases.xml b/OsmAnd/res/values-et/phrases.xml index 1a027924ebb..a7a9d523ff5 100644 --- a/OsmAnd/res/values-et/phrases.xml +++ b/OsmAnd/res/values-et/phrases.xml @@ -4872,19 +4872,8 @@ Rattateede võrgustik Kategooria: määramine Kategooria: servituut - Veespordialad - Kõndimine - Jooksmine - Matkamine - Rattasõit - Muud teekonnapunktid + Teekonnapunktid Muud teekonnad - Talispordialad - Ratsutamine - Rulluisutamine üherealiste uiskudega - Terviserajad - Mootorsaanisõit - Mägirattasõit Parksteri teenus pole kasutatav Parkster Parco teenus pole kasutatav @@ -4897,4 +4886,13 @@ Lisateenustega Standardne Luksuslik + Veespordialade marsruudid + Muud marsruudid + Mootorrattamarsruudid + Rattamarsruudid + Kõndimismarsruudid + Automarsruudid + Talispordialade marsruudid + Õhuspordialade marsruudid + Ronimiskalju \ No newline at end of file diff --git a/OsmAnd/res/values-et/strings.xml b/OsmAnd/res/values-et/strings.xml index 9a25c6d58ef..3b0b2c7babf 100644 --- a/OsmAnd/res/values-et/strings.xml +++ b/OsmAnd/res/values-et/strings.xml @@ -465,7 +465,7 @@ Tee sellest lähtepunkt Kaardi importimise viga Kaart imporditud - Kirjuta faili nimi. + Sisesta failinimi. Lähedalolevad teekonnad Ulatus DD°MM′SS″ @@ -2992,7 +2992,7 @@ Muudatuste arv Kanna ette Öö - Faili nimi sisaldab keelatud sümbolit + Failinimi sisaldab keelatud sümbolit Kiirtoiming Tegevus %d Ekraan %d @@ -3505,7 +3505,7 @@ Sisesta aadress Lihtsustatud teekond Salvestatakse ainult üldine teekond ilma teekonnapunktideta. - Faili nimi + Failinimi %s rajafaili valitud Raja logimine peatub, kui rakendus tapetakse (viimaste rakenduste kaudu). (OsmAnd-i tausttähis kaob Androidi teateribalt.) Määra üldisel teekonna salvestamisel kasutatav logimisvälp (lülitatakse sisse kaardil asuvast teekonna salvestamise vidinast). diff --git a/OsmAnd/res/values-fr/phrases.xml b/OsmAnd/res/values-fr/phrases.xml index 06db2dead69..2eb2b8a4474 100644 --- a/OsmAnd/res/values-fr/phrases.xml +++ b/OsmAnd/res/values-fr/phrases.xml @@ -383,7 +383,7 @@ Magasin de matériel informatique Magasin pour la plongée sous-marine Magasin d\'accastillage - Pont + Barrière Feu de circulation Pneus Arrêt de bus @@ -400,7 +400,7 @@ Antiquaire Magasin d\'articles de chasse Magasin de décoration intérieure - Pont levant + Barrière levante Poste frontière Ralentisseur (plateau) Centre de contrôle technique automobile @@ -820,7 +820,7 @@ Wiki volapük Barrière cycliste Barrière anti-motocycles - Ralentisseur (dos d\'âne) + Barrière à pousser Limiteur de hauteur Échalier à chevaux Échalier @@ -4029,7 +4029,7 @@ Salle de sport Raccord de bouche d\'incendie Valideur de billets - Barrière en barbelés + Barrière en fil de fer Institut de recherche Lieu de formation à la conduite Café diff --git a/OsmAnd/res/values-fr/strings.xml b/OsmAnd/res/values-fr/strings.xml index ce1bd8661b4..aefd451f4a4 100644 --- a/OsmAnd/res/values-fr/strings.xml +++ b/OsmAnd/res/values-fr/strings.xml @@ -5676,4 +5676,8 @@ Affiche le taux de consommation du carburant par le moteur, calculé en fonction du carburant restant, de la taille du réservoir et de la distance parcourue selon le GPS. Les voies seront affichées sur toutes les intersections rencontrées indépendamment des instructions vocales Afficher sur les intersections mineures lors de la navigation en ligne droite + Profil \"%s\" sélectionné + Interpolation + Pourcentage d\'interpolation de la position + Définit le pourcentage d\'interpolation de la position lors du guidage. Ce paramètre rend l\'animation de votre position sur la carte plus fluide. \ No newline at end of file diff --git a/OsmAnd/res/values-ga/phrases.xml b/OsmAnd/res/values-ga/phrases.xml index 01ac6a9d2b4..df6165a41d9 100644 --- a/OsmAnd/res/values-ga/phrases.xml +++ b/OsmAnd/res/values-ga/phrases.xml @@ -1873,7 +1873,7 @@ Bia breá Siopa tae Anraith - Crúibe + Pióg Iógart reoite Ceaintín YakitoriName @@ -4865,7 +4865,7 @@ Ní hea Aiste bia pescetarian: níl - Pointí bealaí eile + Pointí bealaigh Marcaíocht capall Rothaíocht sléibhe Ainm an taoibh dheis @@ -4890,4 +4890,74 @@ Níor glacadh le Parco ParksterName Ní ghlactar le Parkster + Gluaisrothair + Crúibe + Spóirt gheimhridh + Rothaíocht + Spóirt uisce + Bealaí eile + Spóirt Aeir + Ag tiomáint + ATV (Feithicil Uile-tíre) + Gluaisrothair Enduro + Moto tras + Gluaisrothar Camchuairte + Rásaíocht Rian + Taisteal le mála droma + Sléibhteoireacht + Cnocadóireacht + Conaire Rith + Turas sléibhteoireachta + Sciáil Tras-Tíre + Scátáil Oighir + Ag cruadhadh sneachta + R-Rothar + Rothaíocht bóthair + Camchuairt + Cadhc + Surfáil + Clársheoltóireacht + Gluaiseacht (Eitleán Seolta) + Scútar mótair + Clár sneachta + Eitlíochta Ultralight + Paraisiútáil (Spítumadóireacht) + Ag sciáil + Croch gliding + Ag siúl + Raftáil + Snámh amuigh + Bád mótair + Buainteoireacht + Trucail (HGV) + Cartús + Eis-bhóthair + Cluiche Drone ag eitilt + Tumadóireacht scúba + Carr + Gluaisrothair Bóthair + Scátaí inlíne + Cluiche Gluaisrothar Eachtraíochta + Rothaíocht sléibhe + Rothaíocht Ghairbhéal + Eitlíocht + Gluaiseacht sneachta + Spóirt Gluaisrothar + Camchuairt Sciála + Gluaisrothair Easbhóthair (DIRT Biking) + Rith Bóthair + R-rothar sléibhe + Balún Aer Te + Dreapadóireacht Crann + Gailf + Marcaíocht capall + Uaimheadóireacht + Sciáil uisce + Beacáin ag piocadh + Paraeitilt + Paragliding Cumhachtaithe (Paramotoring) + Curach + Tumadóireacht + Marcaíocht Traenach + Aclaíochta \ No newline at end of file diff --git a/OsmAnd/res/values-hr/phrases.xml b/OsmAnd/res/values-hr/phrases.xml index 33986dd25d4..c497855929a 100644 --- a/OsmAnd/res/values-hr/phrases.xml +++ b/OsmAnd/res/values-hr/phrases.xml @@ -410,7 +410,7 @@ Tramvajska stanica Tramvajska stanica Stanica javnog prijevoza - Autobusno stajalište + Autobusna stanica Ulaz u podzemnu željeznicu Da;Stanica podzemne željeznice Raskršće @@ -1216,7 +1216,7 @@ Institucija legislative VHF kanal Glina - Karirano + Tartan Vidljivost: kuća Vidljivost: ulica Vidljivost: područje @@ -1341,12 +1341,12 @@ Minsko polje Ruševine Da - Prijenos - Distribucija + Velika trafostanica + Trafostanica Prijelaz Konverter - Nadoknađivanje - Komprimiranje + Nadoknada + Kompresija Mjerenje Doniranje krvi Stripovi @@ -1383,7 +1383,7 @@ Opasnost: minsko polje Opasnost: kontaminirano Lokacija - Da + Da;Automobil Pristup autobusu: ne Da;Autobus;Autobusi Da;Motori @@ -1801,7 +1801,7 @@ Garažni ormari Natkrivene garaže Traka - U upotrebi: da + Na dužnosti: da Fizijatrija Maksilofacijalna kirurgija Tropska medicina @@ -1886,4 +1886,133 @@ Pravoslavlje Reformist Crkva krista + Zakladni ured + Streličarstvo + Notar + Klizalište + Trkalište + Kanjon + Tjesnac + Iznajmljivanje čamaca + Planinsko područje + Lokacija: platforma + Lokacija: kiosk + Lokacija: vani + Slijetanje + Lokacija: podzemno + Lokacija: krov + Lokacija: ulaz + Vuča + Obuka + Lokacija: nadzemno + Lokacija: iznad + Paragliding: ne + Lokacija: unutar + Lokacija: most + Lokacija: vrh krova + Lokacija: podvodno + Lokacija: zid + Polijetanje + Briga o djeci + Taksi služba + Pristup automobilom: dozvoljeno + Pristup motornim vozilom: da + Pristup biciklom: privatno + Pristup biciklom: guranje bicikla + Baklja na plin;Baklja + Dućan veleprodaje + Pristup kamionom: privatno + Da;Komoda za mijenjanje pelena: da + Naknada za komodu za mijenjanje pelena: ne + Pristup motornim vozilom: ne + Pristup javnim prijevozom: označeno + Pristup pječacima: ne + Uloga zdravstvenog radnika: asistent + Brijeg + Suha bačva + Jezerce + Uloga zdravstvenog radnika: podolog + Uloga zdravstvenog radnika: liječnički asistent + Manja trafostanica + Atol + nekategorizirano + nekategorizirano* + Ne;Komoda za mijenjanje pelena: ne + Ograničeno;Komoda za mijenjanje pelena: ograničeno + Mjesto komode za mijenjanje pelena: muški WC + Kafić s nargilom + Pristup motornim vozilom: vojska + Pristup motornim vozilom: mušterije + Pristup motornim vozilom: šumarstvo + Pristup motornim vozilom: poljoprivreda + Pristup lakim vozilima dostave: ne + Pristup kamionom: ne preporučuje se + Pristup motociklom: privatno + Pristup mopedom: ne + Konji su dozvoljeni + Pristup konjem: privatno + Pristup konjem: odredište + Pristup konjem: šumarstvo + Pristup pječacima: odredište + Pristup prikolicama: ne + Uloga zdravstvenog radnika: vrač + Jezerce + Uloga zdravstvenog radnika: iscjelitelj + Uloga zdravstvenog radnika: babica + Pristup motociklom: ne + Označeno + Pristup vozilom: šumarstvo + Pristup vozilom: mušterije + Pristup automobilom: šumarstvo + Uloga zdravstvenog radnika: psiholog + Uloga zdravstvenog radnika: terapeut + Cjevovodno postrojenje + Mjesto komode za mijenjanje pelena: soba + Mjesto komode za mijenjanje pelena: ženski WC + Pristup motornim vozilom: dostava + Pristup biciklom: odredište + Pristup kamionom: nepogodno + Pristup biciklom: dozvoljeno + Pristup javnim prijevozom: da + Pristup automobilom: ne + Pristup automobilom: odredište + Pristup motornim vozilom: odredište + Pristup motornim vozilom: dozvoljeno + Pristup karavanom: ne + Pristup kamp-kućicama: ne + Proizvodnja slastica + Medicinski laboratorij + Naplatna kućica + Kutija za spašavanje + Pristup vozilom: privatno + Pristup vozilom: dozvoljeno + Pristup vozilom: vojska + Konji nisu dozvoljeni + Pristup vozilom: ne + Pristup vozilom: odredište + Pristup vozilom: dostava + Pristup motornim vozilom: privatno + Pristup kamionom: ne + Pristup biciklom: mušterije + Pristup pječacima: privatno + Pristup pječacima: da + Pristup pječacima: dozvoljeno + Pristup pječacima: mušterije + Uloga zdravstvenog radnika: tehničar + Pristup vozilom: da + Pristup mopedom: ne + Gomila ugljena + Pristup kamionom: poljoprivreda + Stup + Broj komoda za mijenjanje pelena + Mjesto komode za mijenjanje pelena: uniseks WC + Naknada za komodu za mijenjanje pelena: da + Pristup automobilom: privatno + Ventil + Grupa ventila + Pristup automobilom: mušterije + Pristup konjem: dozvoljeno + Uloga zdravstvenog radnika: medicinska sestra + Uloga zdravstvenog radnika: bolničar + Uloga zdravstvenog radnika: liječnik \ No newline at end of file diff --git a/OsmAnd/res/values-hr/strings.xml b/OsmAnd/res/values-hr/strings.xml index cb8a6d8bc5a..fa6d1692e84 100644 --- a/OsmAnd/res/values-hr/strings.xml +++ b/OsmAnd/res/values-hr/strings.xml @@ -67,7 +67,7 @@ Pokrenite aplikaciju u sigurnom načinu rada. Siguran način rada Aplikacija je pokrenuta sigurnom načinu rada (isključite ga u postavkama). - OsmAnd je pokrenut, ali je u stanju mirovanja. Želite li ga isključiti? + Pozadinska usluga OsmAnd-a još uvijek radi. Želiš li je također zaustaviti? ZXing Barcode Scanner aplikacija nije instalirana. Potraži na Market? Odaberite shemu boja za ceste: Sheme boja za ceste @@ -165,8 +165,8 @@ Uključeno Isključeno Povratak na izbornik - Udalji - Približi + Umanji + Uvećaj sjever sjever-sjeveroistok sjeveroistok @@ -966,9 +966,9 @@ Obojane zgrade Nastavi navigaciju Pauziraj navigaciju - Omogućite stanje mirovanja GPS-a + Uključi GPS modus u pozadini Interval uključivanja GPS-a - Prekinuti GPS stanje mirovanja? + Zaustaviti GPS modus u pozadini? Izaberite jezik za oznake na karti (ako jezik nije dostupan prebaciti će se na Engleski ili lokalni jezik) Jezik karte Lokalni nazivi @@ -980,7 +980,7 @@ Udaljenost Sve Prometna upozorenja - Najavi favorite u blizini + Favoriti u blizini POI u blizini Preuzeti karte koje nedostaju %1$s (%2$d MB)? Započnite online praćenje @@ -1643,4 +1643,13 @@ Najprije odaberite odredište Staza Ne pregledavaj online karte razinom zumiranja izvan ove. + Vrsta prijevoza + Izbjegni vrste prijevoza … + Pomakni lijevo + Pomakni gore + Pomakni dolje + Pomakni desno + Uvećaj + Umanji + Srpski (latinica) \ No newline at end of file diff --git a/OsmAnd/res/values-hu/phrases.xml b/OsmAnd/res/values-hu/phrases.xml index fb3fb66137b..60db9d67e71 100644 --- a/OsmAnd/res/values-hu/phrases.xml +++ b/OsmAnd/res/values-hu/phrases.xml @@ -4880,14 +4880,67 @@ Parcót nem fogad el Parkster Parkstert nem fogad el - Vízi sportok - Lovaglás - Téli sportok - Hójáró - Hegyi kerékpározás Egyéb útvonalak - Kerékpározás - Futás - Gyaloglás - Túrázás + Motorozás + Gyaloglás + Kerékpározás + Vízi sportok + Egyéb útvonalak + Enduro motorozás + Motokrossz + Sportmotorozás + Hátizsákkal + Hegymászás + Síelés + Snowboardozás + Elektromos hegyi kerékpározás + Túrázás + Repülés + Vitorlázórepülés + Ejtőernyőzés + Siklóernyőzés + Motoros siklóernyőzés (paramotorozás) + Kenuzás + Búvárkodás + Sárkányrepülés + Ultrakönnyű repülés + Hőlégballonozás + Szörfözés + Úszás a szabadban + Vízisízés + Motorcsónakozás + Lovaglás + Gombázás + Vadvízi evezés + Fára mászás + Aratás + A bal oldal neve + A jobb oldal neve + Fitness + Sétálás + Vezetés + Kajakozás + Téli sportok + Autó + Gokartozás + Széllovaglás + Országúti motorozás + + Jégkorcsolyázás + Országúti kerékpározás + Túramotorozás + Motoros robogó + Túrázás + Hegyi kerékpározás + Drón repülés + Deluxe + Görkorcsolyázás + Golfozás + Vonatozás + Barlangászat + Elektromos kerékpározás + Alap + Standard + Hegymászó szikla + Név etimológiája \ No newline at end of file diff --git a/OsmAnd/res/values-is/phrases.xml b/OsmAnd/res/values-is/phrases.xml index 0bbfa51f5e5..4fc41c1d4d7 100644 --- a/OsmAnd/res/values-is/phrases.xml +++ b/OsmAnd/res/values-is/phrases.xml @@ -4932,23 +4932,12 @@ Flúð Fjársjóðaleit á strönd Túlkunarsvæði - Línuskautar - Fjallahjólreiðar Göng bönnuð fyrir gangandi Göng bönnuð fyrir hjólandi Ferðagreinar Punktar ferðar Aðrar leiðir Punktar í öðrum leiðum - Hjólandi - Gönguferðir - Hlaupandi - Gangandi - Vatnaíþróttir - Hestaferðir - Vetraríþróttir - Vélsleðar - Fitness-leiðir Nafn hægra megin Orðsifjar nafns Nafn vinstra megin diff --git a/OsmAnd/res/values-it/strings.xml b/OsmAnd/res/values-it/strings.xml index 02eb2fa2eff..d21ad1ec3d8 100644 --- a/OsmAnd/res/values-it/strings.xml +++ b/OsmAnd/res/values-it/strings.xml @@ -5560,11 +5560,11 @@ Posizione Acceleratore kPa %/h - Tasso di consumo di carburante (l/h) + Tasso di consumo di carburante (L/h) Tasso di consumo di carburante - Mostra il tasso di consumo di carburante del veicolo in base ai calcoli (l/h) + Mostra il tasso di consumo di carburante del veicolo in base ai calcoli (L/h) Tasso di consumo di carburante (sensore) - Mostra il tasso di consumo di carburante del veicolo basato sul sensore (l/h) + Mostra il tasso di consumo di carburante del veicolo basato sul sensore (L/h) Carburante rimasto (l) Carburante rimasto (km) Tasso di consumo di carburante (obd) @@ -5606,7 +5606,7 @@ Il touch screen è bloccato. Per sbloccare, premere il tasto %1$s o toccare il pulsante sullo schermo. Le unità selezionate verranno applicate a tutti i dati relativi al volume nel profilo corrente. Unità di volume - l/100 km + L/100 km Schermata di blocco La traccia non contiene dati da salvare. Traccia i dati vitali del veicolo utilizzando il protocollo OBD-II. Monitorate le metriche chiave come la velocità, il numero di giri del motore, il consumo di carburante e altro ancora. @@ -5666,7 +5666,7 @@ Collegamenti a Terzi Impostazioni Metriche Veicolo Un pulsante per salvare il percorso correntemente registrato come file GPX e continuare a registrare un nuovo tratto senza interruzioni. - l/h + L/h Sentieri per bici da sterrato Carburante rimanente (litri) Frequenza di campionamento audio diff --git a/OsmAnd/res/values-pt-rBR/phrases.xml b/OsmAnd/res/values-pt-rBR/phrases.xml index 6f9c21b7334..f9f3caacff4 100644 --- a/OsmAnd/res/values-pt-rBR/phrases.xml +++ b/OsmAnd/res/values-pt-rBR/phrases.xml @@ -4874,21 +4874,11 @@ Depósito de dinheiro Acesso Outras rotas - Corrida Túnel proibido para pedestres Túnel proibido para ciclistas Artigos de viagem Pontos de viagem Outros pontos de rotas - Bicicleta - Caminhada - A pé - Esportes aquáticos - Local de equitação - Esportes de inverno - Moto de neve - Trilhas de exercícios - Patins em linha Parco não aceito Parkster não aceito \ No newline at end of file diff --git a/OsmAnd/res/values-pt-rBR/strings.xml b/OsmAnd/res/values-pt-rBR/strings.xml index 6235c465f16..ca153f5341b 100644 --- a/OsmAnd/res/values-pt-rBR/strings.xml +++ b/OsmAnd/res/values-pt-rBR/strings.xml @@ -5859,7 +5859,7 @@ Tipo de atividade Alterar atividade Aumentar - Mostrar trilhas de terra para bicicletas + Mostrar trilhas de terra para motos fora de estrada Diminuir V Wikimedia @@ -5933,7 +5933,7 @@ Ative o Bluetooth no seu dispositivo Saiba mais sobre sensores. A tela sensível ao toque está bloqueada. Toque no botão para desbloquear. - Trilhas de terra para bicicletas + Trilhas de terra para motos fora de estrada Funcionamento bicombustível a etanol l/h Híbrido elétrico e motor a combustão @@ -5970,7 +5970,7 @@ Ligue o motor do seu veículo O OsmAnd suporta conexões Bluetooth com scanners OBD-II. Conecte o scanner à porta OBD-II do seu veículo. - Toque em \'Conectar\' e selecione o scanner na lista. + Toque em \'%1$s\' e selecione o scanner na lista. Configurações de métricas do veículo Mostra as RPM do tacômetro do veículo pelo sensor OBD A tela sensível ao toque está bloqueada. Para desbloquear, pressione o botão %1$s ou toque no botão na tela. diff --git a/OsmAnd/res/values-pt/phrases.xml b/OsmAnd/res/values-pt/phrases.xml index c67585ab4ff..1a7e1f22e93 100644 --- a/OsmAnd/res/values-pt/phrases.xml +++ b/OsmAnd/res/values-pt/phrases.xml @@ -185,7 +185,7 @@ Reservatório de água contra incêndios Caixa de sal-gema Posto de ambulância - Serviço de emergência + Estação de resposta a catástrofes Ponto de acesso de emergência Vau Ponte pedonal em pedras separadas @@ -4880,4 +4880,24 @@ Parkster não aceite Túnel proibido para pedestres Túnel proibido para ciclistas + Tipo de parque de campismo + A conduzir + De motorizada + A pé + Desportos de inverno + Ciclismo + Desportos aéreos + Outras rotas + Desportos aquáticos + Pontos de rota + Pedregulho de escalada + Com serviços + De luxo + Normal + Básico + Etimologia do nome + Nome do lado esquerdo + Nome do lado direito + Artigos de viagem + Pontos de viagem \ No newline at end of file diff --git a/OsmAnd/res/values-pt/strings.xml b/OsmAnd/res/values-pt/strings.xml index 6d7e60ea5e5..6ec3f73c930 100644 --- a/OsmAnd/res/values-pt/strings.xml +++ b/OsmAnd/res/values-pt/strings.xml @@ -582,9 +582,7 @@ Não, obrigado Descarregue o mapa base do mundo para obter uma visão geral cobrindo o mundo inteiro em níveis de ampliação distantes. Descarregar dados offline para utilizar mapas offline. - " -\n -\nToque longo para ver opções" + \n\nToque longo para ver opções Versão local %1$d de %2$d itens desativados. %1$d de %2$d itens eliminados. @@ -1034,27 +1032,7 @@ Definir/editar… Lupa do mapa Mapa base mundial - OsmAnd+ (OSM Automated Navigation Directions / direções de navegação automatizadas do OSM) -\n -\nO OsmAnd+ é uma aplicação de navegação com código-fonte aberto, com acesso a uma ampla variedade de dados globais do OpenStreetMap (OSM). Todos os dados dos mapas (vetoriais ou imagens raster) podem ser armazenados no cartão de memória do telemóvel para usar quando não tiver Internet. O OsmAnd+ também permite usar rotas, tanto ligado como desligado da Internet, incluindo a funcionalidade de rotas curva a curva com orientação por voz. -\n -\nOsmAnd+ é a versão paga da aplicação, ao comprá-la está a apoiar o projeto, a financiar o desenvolvimento de novas funcionalidades e a receber as últimas atualizações nos mapas. -\n -\nAlgumas das características principais: -\n- Funcionamento totalmente desligado da Internet (guarda os mapas obtidos, sejam eles vetoriais ou imagens, numa pasta selecionável). -\n- Disponíveis mapas vetoriais offline compactados do mundo inteiro. -\n- Descarregar mapas de países ou regiões diretamente na aplicação. -\n- Recurso Wikipédia desligado da Internet (descarregamento de POIs da Wikipédia), bom para passeios turísticos. -\n- Sobreposição de mapas diversos, como trilhos GPX ou trajetos de navegação, pontos de interesse (POI), favoritos, paragens de transportes públicos, mapas adicionais com transparência personalizável, características de topografia como curvas de nível, sombras de relevo e declives. -\n -\n- Pesquisa desligado da Internet para endereços e locais (POIs). -\n- Cálculo de rotas desligado da Internet para distâncias médias. -\n- Modo de carro, bicicleta, pedestre, entre outros. -\n- Visualização de dia e noite, com alteração automática. -\n- Ampliação do mapa dependente da velocidade. -\n- Orientação do mapa de acordo com bússola ou direção do movimento. -\n- Indicação da via de trânsito a seguir, aviso de limite de velocidade, vozes gravadas e vozes de conversão de texto para voz. -\n + OsmAnd+ (OSM Automated Navigation Directions / direções de navegação automatizadas do OSM) \n \nO OsmAnd+ é uma aplicação de navegação com código-fonte aberto, com acesso a uma ampla variedade de dados globais do OpenStreetMap (OSM). Todos os dados dos mapas (vetoriais ou imagens raster) podem ser armazenados no cartão de memória do telemóvel para usar quando não tiver Internet. O OsmAnd+ também permite usar rotas, tanto ligado como desligado da Internet, incluindo a funcionalidade de rotas curva a curva com orientação por voz. \n \nOsmAnd+ é a versão paga da aplicação, ao comprá-la está a apoiar o projeto, a financiar o desenvolvimento de novas funcionalidades e a receber as últimas atualizações nos mapas. \n \nAlgumas das características principais: \n- Funcionamento totalmente desligado da Internet (guarda os mapas obtidos, sejam eles vetoriais ou imagens, numa pasta selecionável). \n- Disponíveis mapas vetoriais offline compactados do mundo inteiro. \n- Descarregar mapas de países ou regiões diretamente na aplicação. \n- Recurso Wikipédia desligado da Internet (descarregamento de POIs da Wikipédia), bom para passeios turísticos. \n- Sobreposição de mapas diversos, como trilhos GPX ou trajetos de navegação, pontos de interesse (POI), favoritos, paragens de transportes públicos, mapas adicionais com transparência personalizável, características de topografia como curvas de nível, sombras de relevo e declives. \n \n- Pesquisa desligado da Internet para endereços e locais (POIs). \n- Cálculo de rotas desligado da Internet para distâncias médias. \n- Modo de carro, bicicleta, pedestre, entre outros. \n- Visualização de dia e noite, com alteração automática. \n- Ampliação do mapa dependente da velocidade. \n- Orientação do mapa de acordo com bússola ou direção do movimento. \n- Indicação da via de trânsito a seguir, aviso de limite de velocidade, vozes gravadas e vozes de conversão de texto para voz. Sem ampliação automática Aproximar Intermédia @@ -1148,7 +1126,7 @@ Ponto %1$s %1$s \nPontos da rota %2$s - %1$s\nPontos + %1$s \nPontos %1$s \nTrajeto %2$s O ficheiro GPX está vazio @@ -1212,9 +1190,7 @@ A carregar %1$s… Hora atual Ponto de rota - " -\n -\nToque longo para ver no mapa" + \n\nToque longo para ver no mapa Iniciar a orientação curva a curva automaticamente selecionado(s) Intervalo de divisão @@ -2476,27 +2452,8 @@ OsmAnd (sigla em inglês de direções de navegação automatizada do OSM) é uma aplicação de mapas e navegação com acesso a dados livres, mundiais e de alta qualidade do OSM. \n \nPoderá usar o navegador visual e por voz, ver POIs (pontos de interesse), criar e gerir trilhos GPX, usar curvas de nível (através de um complemento) e dados de topografia, escolher entre os modos motorista, ciclista e pedestre, editar o OpenStreetMap e muito mais. - Navegação GPS -\n• Escolha entre modos offline (sem tarifa de roaming quando estiver no estrangeiro) ou online (mais rápido) -\n• Orientação por voz curva a curva lhe guia ao longo do caminho (vozes gravadas e sintetizadas) -\n• A rota é recalculada sempre que se desviar desta -\n• Ajudas na orientação da via de trânsito a tomar, nomes de ruas e tempo estimado de chegada -\n• Para fazer a sua viagem mais segura, o modo dia/noite muda automaticamente -\n• Veja os limites de velocidade e receba aviso se os ultrapassar -\n• Ampliação do mapa automática conforme a velocidade atual -\n• Pesquise destinos pelo endereço, tipo (por ex.: estacionamento, restaurante, hotel, posto de gasolina, museu) ou coordenadas geográficas -\n• Possibilidade de usar pontos intermédios no itinerário -\n• Grave ou envie um trilho GPX e percorra-o -\n - Mapa -\n• Mostra POIs (ponto de interesse) perto de si. -\n• Ajusta o mapa na sua direção de movimento (ou bússola). -\n• Mostra a sua posição e direção para onde está a olhar. -\n• Partilha a sua posição para que seus amigos possam encontrá-lo. -\n• Mantém os seus lugares mais importantes em \"Favoritos\". -\n• Permite-lhe escolher como mostrar nomes no mapa: em inglês, idioma local ou escrita fonética. -\n• Mostra mapas online especializados, vista de satélite (do Bing), sobreposições diferentes como trajetos GPX de navegação/turismo e camadas adicionais com transparência personalizável. -\n + Navegação GPS \n• Escolha entre modos offline (sem tarifa de roaming quando estiver no estrangeiro) ou online (mais rápido) \n• Orientação por voz curva a curva lhe guia ao longo do caminho (vozes gravadas e sintetizadas) \n• A rota é recalculada sempre que se desviar desta \n• Ajudas na orientação da via de trânsito a tomar, nomes de ruas e tempo estimado de chegada \n• Para fazer a sua viagem mais segura, o modo dia/noite muda automaticamente \n• Veja os limites de velocidade e receba aviso se os ultrapassar \n• Ampliação do mapa automática conforme a velocidade atual \n• Pesquise destinos pelo endereço, tipo (por ex.: estacionamento, restaurante, hotel, posto de gasolina, museu) ou coordenadas geográficas \n• Possibilidade de usar pontos intermédios no itinerário \n• Grave ou envie um trilho GPX e siga-o + Mapa\n• Mostra POIs (ponto de interesse) perto de si\n• Ajusta o mapa na sua direção de movimento (ou bússola)\n• Mostra a sua posição e direção para onde está a olhar\n• Partilha a sua posição para que seus amigos possam encontrá-lo\n• Mantém os seus lugares mais importantes em \"Favoritos\"\n• Permite-lhe escolher como mostrar nomes no mapa: em inglês, idioma local ou escrita fonética\n• Mostra mapas online especializados, vista de satélite (do Bing), sobreposições diferentes como trajetos GPX de navegação/turismo e camadas adicionais com transparência personalizável Esqui \nO complemento de mapas de esqui OsmAnd Ski permite que veja pistas de esqui com nível de complexidade e algumas informações adicionais, como localização de elevadores e outras instalações invernais. Ciclismo @@ -2505,18 +2462,8 @@ \n • Veja a sua velocidade e altitude. \n • Opção de gravação GPX permite-lhe gravar a sua viagem e partilhá-la. \n • Através de complementos adicionais, pode ativar curvas de nível e sombreamento de relevo. - Passeio, caminhada, turismo pela cidade -\n• O mapa mostra caminhos para passeios e caminhadas -\n• A Wikipédia no seu idioma preferido pode dizer muito durante um percurso pela cidade -\n• Paragens de transportes públicos (autocarro, elétrico, comboio) incluindo nomes de linha, ajuda a navegar numa nova cidade -\n• Navegação GPS no modo pedestre cria a sua rota usando caminhos para pedestres -\n• Enviar e seguir uma rota GPX ou gravar e partilhar a sua própria rota -\n - Contribua para o OpenStreetMap -\n • Envie relatórios de erros -\n • Envie trilhos GPX para o OpenStreetMap diretamente da aplicação -\n • Adicione POIs e envie-os diretamente para o OpenStreetMap (ou mais tarde se estiver desconectado da Internet) -\n + Passeio, caminhada, turismo pela cidade \n• O mapa mostra caminhos para passeios e caminhadas \n• A Wikipédia no seu idioma preferido pode dizer muito durante um percurso pela cidade \n• Paragens de transportes públicos (autocarro, elétrico, comboio) incluindo nomes de linha, ajuda a navegar numa nova cidade \n• Navegação GPS no modo pedestre cria a sua rota usando caminhos para pedestres \n• Enviar e seguir uma rota GPX ou gravar e partilhar a sua própria rota + Contribua para o OpenStreetMap \n • Envie relatórios de erros \n • Envie trilhos GPX para o OpenStreetMap diretamente da aplicação \n • Adicione POIs e envie-os diretamente para o OpenStreetMap (ou mais tarde se estiver desconectado da Internet) OsmAnd é um programa de fonte aberta desenvolvido ativamente. Todos podem contribuir para a aplicação reportando erros, melhorando as traduções ou programando novas funcionalidades. Além disso, o projeto conta com contribuições financeiras para financiar a programação e testes de novas funcionalidades. \n Cobertura de mapa e qualidade aproximada: \n • Europa Ocidental: **** @@ -2537,22 +2484,8 @@ \nOsmAnd+ é a versão paga da aplicação, ao comprá-la está a apoiar o projeto, a financiar o desenvolvimento de novas funcionalidades e a receber as últimas atualizações nos mapas. \n \nAlgumas das características principais: - Navegação -\n• Funciona online (rápido) ou offline (sem custos de roaming quando estiver no estrangeiro) -\n• Orientação por voz curva a curva (vozes gravadas e sintetizadas) -\n• Opção de visualização da via de trânsito a tomar, visualização do nome da rua e tempo estimado de chegada -\n• Opção de indicar pontos intermédios por onde passar no itinerário -\n• Correção automática da rota sempre que sair da rota -\n• Procure lugares por endereço, pelo tipo (por exemplo: restaurante, hotel, posto de gasolina, museu...) ou por coordenadas geográficas -\n - Visualização de mapa -\n• Mostra a sua posição e orientação. -\n• Oriente opcionalmente o ecrã de acordo com a bússola ou a direção do seu movimento. -\n• Guarde os seus lugares mais importantes como favoritos. -\n• Mostra POIs (pontos de interesse) por perto. -\n• Mostra imagens online especializadas, vistas de satélite (do Bing), diferentes camadas como trilhos de navegação GPX e camadas adicionais com transparência personalizável. -\n• Mostra opcionalmente nomes de lugares em inglês, na língua local ou grafia fonética. -\n + Navegação \n• Funciona online (rápido) ou offline (sem custos de roaming quando estiver no estrangeiro) \n• Orientação por voz curva a curva (vozes gravadas e sintetizadas) \n• Opção de visualização da via de trânsito a tomar, visualização do nome da rua e tempo estimado de chegada \n• Opção de indicar pontos intermédios por onde passar no itinerário \n• Correção automática da rota sempre que sair da rota \n• Procure lugares por endereço, pelo tipo (por exemplo: restaurante, hotel, posto de gasolina, museu...) ou por coordenadas geográficas + Visualização de mapa \n• Mostra a sua posição e orientação. \n• Oriente opcionalmente o ecrã de acordo com a bússola ou a direção do seu movimento. \n• Guarde os seus lugares mais importantes como favoritos. \n• Mostra POIs (pontos de interesse) por perto. \n• Mostra imagens online especializadas, vistas de satélite (do Bing), diferentes camadas como trilhos de navegação GPX e camadas adicionais com transparência personalizável. \n• Mostra opcionalmente nomes de lugares em inglês, na língua local ou grafia fonética. Use dados do OpenStreetMap e da Wikipédia \n • Informações de alta qualidade dos melhores projetos colaborativos do mundo \n • Dados do OpenStreetMap disponíveis por país ou região @@ -2561,12 +2494,7 @@ \n • Mapas vetoriais offline compactados e atualizados mensalmente \n \n • Escolha entre região completa ou apenas a rede rodoviária (exemplo: Portugal inteiro tem 315 MB e a rede rodoviária tem apenas 152 MB) - Recursos de segurança -\n• Mudança da visualização automática para dia/noite opcional -\n• Visualização do limite de velocidade (opcional), com lembrete se o ultrapassar -\n• Ampliação dependente da velocidade (opcional) -\n• Partilhe a sua localização para que os seus amigos o possam encontrar -\n + Recursos de segurança \n• Mudança da visualização automática para dia/noite opcional \n• Visualização do limite de velocidade (opcional), com lembrete se o ultrapassar \n• Ampliação dependente da velocidade (opcional) \n• Partilhe a sua localização para que os seus amigos o possam encontrar Recursos de ciclismo e pedestre \n• Visualização de caminhos a pé, pistas de caminhadas e ciclovias, ideal para atividades ao ar livre \n• Navegação e modos de visualização especiais para bicicleta e pedestres @@ -2574,27 +2502,8 @@ \n• Gravação opcional de viagem para ficheiro GPX local ou serviço online \n• Visualização opcional de velocidade e altitudes \n• Visualização de curvas de nível e sombreamento de relevo (com um complemento adicional) - Contribua diretamente para o OpenStreetMap -\n• Envie notas de erros que encontrou para informar os editores do OpenStreetMap. -\n• Envie trilhos GPX para o OpenStreetMap diretamente da aplicação. -\n• Adicione POIs e envie-os diretamente para o OpenStreetMap (ou mais tarde se estiver desconectado da Internet). -\n• Gravar viagem opcional também em plano de fundo (enquanto o dispositivo está no modo adormecido). -\n• O OsmAnd é um programa com código-fonte aberto desenvolvido ativamente. Todos podem contribuir para a aplicação reportando erros, melhorar as traduções ou a programar novas funcionalidades. Além disso, o projeto conta com contribuições financeiras para financiar a programação e testes de novas funcionalidades. -\n - Cobertura de mapa e qualidade aproximada: -\n• Europa Ocidental: **** -\n• Europa Oriental: *** -\n• Rússia: *** -\n• América do Norte: *** -\n• América do Sul: ** -\n• Ásia: ** -\n• Japão e Coreia: *** -\n• Oriente Médio: ** -\n• África: ** -\n• Antártida: * -\nA maioria dos países ao redor do globo disponível como descarregamentos! -\nDo Afeganistão ao Zimbabué, da Austrália aos Estados Unidos. Argentina, Brasil, Canadá, França, Alemanha, México, Reino Unido, Espanha… -\n + Contribua diretamente para o OpenStreetMap \n• Envie notas de erros que encontrou para informar os editores do OpenStreetMap\n• Envie trilhos GPX para o OpenStreetMap diretamente da aplicação\n• Adicione POIs e envie-os diretamente para o OpenStreetMap (ou mais tarde se estiver desconectado da Internet)\n• Gravar viagem opcional também em plano de fundo (enquanto o dispositivo está no modo adormecido)\n• O OsmAnd é um programa com código-fonte aberto desenvolvido ativamente. Todos podem contribuir para a aplicação reportando erros, melhorar as traduções ou a programar novas funcionalidades. Além disso, o projeto conta com contribuições financeiras para financiar a programação e testes de novas funcionalidades. + Cobertura de mapa e qualidade aproximada:\n • Europa Ocidental: ****\n • Europa Oriental: ***\n • Rússia: ***\n • América do Norte: ***\n • América do Sul: **\n • Ásia: **\n • Japão e Coreia: ***\n • Oriente Médio: **\n • África: **\n • Antártida: *\n A maioria dos países ao redor do globo disponível como descarregamentos!\n Do Afeganistão ao Zimbabué, da Austrália aos Estados Unidos. Argentina, Brasil, Canadá, França, Alemanha, México, Reino Unido, Espanha… Toque em qualquer item para ver mais detalhes, toque longo para desativar ou eliminar. Dados atuais no dispositivo (%1$s livre): Adiantado Normal @@ -2994,27 +2903,7 @@ Encravou A última execução do OsmAnd bloqueou. Por favor ajude-nos a melhorar o OsmAnd enviando-nos uma mensagem de erro. OVNI - - Perfis de aplicação: crie o seu perfil personalizado para as suas necessidades, com um ícone e cor personalizados -\n -\n - Configure as velocidades mínimas/máximas predefinidas de cada perfil -\n -\n - Widget adicionado para mostrar as coordenadas atuais -\n -\n - Adicionadas opções para mostrar a bússola no mapa com uma régua radial -\n -\n - Corrigido o registo de gravação de trilhos em segundo plano -\n -\n - Melhorado o descarregamento de mapas em segundo plano -\n -\n- Opção \"Ligar o ecrã\" novamente disponível -\n -\n - Corrigida a seleção do idioma dos artigos da Wikipédia -\n -\n - Corrigido o comportamento do botão da bússola durante a navegação -\n -\n - Outras correções -\n -\n + - Perfis de aplicação: crie o seu perfil personalizado para as suas necessidades, com um ícone e cor personalizados \n \n - Configure as velocidades mínimas/máximas predefinidas de cada perfil \n \n - Widget adicionado para mostrar as coordenadas atuais \n \n - Adicionadas opções para mostrar a bússola no mapa com uma régua radial \n \n - Corrigido o registo de gravação de trilhos em segundo plano \n \n - Melhorado o descarregamento de mapas em segundo plano \n \n- Opção \"Ligar o ecrã\" novamente disponível \n \n - Corrigida a seleção do idioma dos artigos da Wikipédia \n \n - Corrigido o comportamento do botão da bússola durante a navegação \n \n - Outras correções Transporte pessoal Monoroda Lambreta @@ -3191,23 +3080,7 @@ Preferir estradas não pavimentadas. Atualizar todos os mapas Atualizar todos os mapas (%1$d)\? - - Configurações atualizadas da aplicação e do perfil: as definições estão agora organizadas por tipo. Cada perfil pode ser personalizado separadamente. -\n -\n - Novo ecrã para descarregar mapas sugerindo um mapa para descarregar enquanto navega -\n -\n - Correções de temas escuros -\n -\n - Vários problemas de roteamento resolvidos ao redor do mundo -\n -\n - Mapa base atualizado com uma rede rodoviária mais detalhada -\n -\n - Áreas inundadas corrigidas em todo o mundo -\n -\n - Roteamento do esqui: perfil de elevação e complexidade da rota adicionada aos detalhes da rota -\n -\n - Outras correções de erros -\n -\n + - Configurações atualizadas da aplicação e do perfil: as definições estão agora organizadas por tipo. Cada perfil pode ser personalizado separadamente. \n \n - Novo ecrã para descarregar mapas sugerindo um mapa para descarregar enquanto navega \n \n - Correções de temas escuros \n \n - Vários problemas de roteamento resolvidos ao redor do mundo \n \n - Mapa base atualizado com uma rede rodoviária mais detalhada \n \n - Áreas inundadas corrigidas em todo o mundo \n \n - Roteamento do esqui: perfil de elevação e complexidade da rota adicionada aos detalhes da rota \n \n - Outras correções de erros Aplique esta alteração a todos os perfis ou ao selecionado atualmente. Partilhado Preferir estradas não pavimentadas @@ -3354,31 +3227,7 @@ Classificar por categoria Direto ao ponto Copiar coordenadas - • Perfis: agora pode alterar a ordem, definir o ícone para o mapa, alterar todas as configurações dos perfis básicos e restaurá-los para as predefinições -\n -\n  • Número de saída adicionado na navegação -\n -\n  • Configurações dos complementos reformuladas -\n -\n  • Ecrã \"Configurações\" reformulado para acesso rápido a todos os perfis -\n -\n  • Opção adicionada para copiar configurações de outro perfil -\n -\n  • Adicionado capacidade de alterar um pedido ou ocultar categorias de PI na Pesquisa -\n -\n  • Ícones de POI corretamente alinhados no mapa -\n -\n  • Dados do pôr do sol/nascer do sol adicionados para configurar o mapa -\n -\n  • Adicionado ícones Casa/Trabalho no mapa -\n -\n  • Adicionado suporte para descrição de múltiplas linhas em Configurações -\n -\n  • Adicionada transliteração correta no mapa do Japão -\n -\n  • Adicionado mapa da Antártica -\n -\n + • Perfis: agora pode alterar a ordem, definir o ícone para o mapa, alterar todas as configurações dos perfis básicos e restaurá-los para as predefinições \n \n  • Número de saída adicionado na navegação \n \n  • Configurações dos complementos reformuladas \n \n  • Ecrã \"Configurações\" reformulado para acesso rápido a todos os perfis \n \n  • Opção adicionada para copiar configurações de outro perfil \n \n  • Adicionado capacidade de alterar um pedido ou ocultar categorias de PI na Pesquisa \n \n  • Ícones de POI corretamente alinhados no mapa \n \n  • Dados do pôr do sol/nascer do sol adicionados para configurar o mapa \n \n  • Adicionado ícones Casa/Trabalho no mapa \n \n  • Adicionado suporte para descrição de múltiplas linhas em Configurações \n \n  • Adicionada transliteração correta no mapa do Japão \n \n  • Adicionado mapa da Antártica Limpar dados gravados Desativado por predefinição: enquanto OsmAnd estiver a ser executado em primeiro plano, o ecrã não atingirá o tempo limite. \n @@ -3732,21 +3581,7 @@ Nome do ficheiro %s ficheiros de trilhos selecionados A gravação de trilhos será pausada quando o OsmAnd for encerrado totalmente, através do ecrã das aplicações recentes. A indicação do OsmAnd desaparece da barra de notificações do Android. - - Função atualizada de Planear uma rota: permite utilizar diferentes tipos de navegação por segmento e a inclusão de trilhos -\n -\n - Novo menu Aparência para trilhos: selecionar cor, espessura, setas de direção de visualização, ícones de início e fim -\n -\n - Melhoria da visibilidade dos nós da bicicleta. -\n -\n - Os trilhos agora podem ser tocados, ter menu de contexto com informações básicas. -\n -\n - Algoritmos de pesquisa melhorados -\n -\n - Opções de seguir trilhos melhoradas na navegação -\n -\n - Problemas com as configurações de importação/exportação de perfis resolvidos -\n -\n + - Função atualizada de Planear uma rota: permite utilizar diferentes tipos de navegação por segmento e a inclusão de trilhos \n \n - Novo menu Aparência para trilhos: selecionar cor, espessura, setas de direção de visualização, ícones de início e fim \n \n - Melhoria da visibilidade dos nós da bicicleta. \n \n - Os trilhos agora podem ser tocados, ter menu de contexto com informações básicas. \n \n - Algoritmos de pesquisa melhorados \n \n - Opções de seguir trilhos melhoradas na navegação \n \n - Problemas com as configurações de importação/exportação de perfis resolvidos Última alteração Nome: Z – A Nome: A – Z @@ -3848,15 +3683,7 @@ Dividir antes Dividir após Adicionar um novo segmento - • Adicionada opção de exportar e importar todos os dados, incluindo configurações, recursos e meus lugares. -\n -\n• Planear rota: gráficos para segmentos de trilhos com rota, e adicionada a capacidade de criar e editar múltiplos trilhos de segmentos. -\n -\n• Adicionado método de autenticação OAuth para o OpenStreetMap, melhoria da interface dos ecrãs de opções do OpenStreetMap. -\n -\n• Cores personalizadas para favoritos e pontos de rotas de trilhos. -\n -\n + • Adicionada opção de exportar e importar todos os dados, incluindo configurações, recursos e meus lugares \n \n• Planear rota: gráficos para segmentos de trilhos com rota, e adicionada a capacidade de criar e editar múltiplos trilhos de segmentos \n \n• Adicionado método de autenticação OAuth para o OpenStreetMap, melhoria da interface dos ecrãs de opções do OpenStreetMap \n \n• Cores personalizadas para favoritos e pontos de rotas de trilhos Editar o mecanismo de roteamento online Subtipo Veículo @@ -3905,21 +3732,7 @@ Ao passar Bicicleta elétrica Eliminar todas as %s atualizações OsmAnd Live\? - • Tradução para português europeu completamente revista. -\n -\n• Adicionada opção para descarregar curvas de nível na unidade de medida \"pés\". -\n -\n• Planear rota: adicionadas abas para alternar entre pontos ou gráficos. -\n -\n• Atualizações OsmAnd Live movidas para \"Descarregar mapas > Atualizações\" -\n -\n• Os trilhos podem ser agora coloridos conforme a altitude, velocidade ou declive. -\n -\n• Adicionada opção para alterar a aparência da linha de rota. -\n -\n• Ecrã \"Gravar viagem\" atualizado. -\n -\n + • Tradução para português europeu completamente revista\n \n• Adicionada opção para descarregar curvas de nível na unidade de medida \"pés\"\n \n• Planear rota: adicionadas abas para alternar entre pontos ou gráficos\n \n• Atualizações OsmAnd Live movidas para \"Descarregar mapas > Atualizações\"\n \n• Os trilhos podem ser agora coloridos conforme a altitude, velocidade ou declive\n \n• Adicionada opção para alterar a aparência da linha de rota\n \n• Ecrã \"Gravar viagem\" atualizado O tempo de viagem projetado refletirá o impacto da elevação. A preferência de roteamento pode ajudar a evitar subidas íngremes: OsmAnd Live Ciclismo casual @@ -4169,29 +3982,7 @@ Token inválido ou expirado Não está registado nenhum utilizador com esse endereço de email Não existe nenhuma subscrição válida - - Tradução para português europeu completamente revista -\n -\n- Cópia de segurança para a nuvem -\n -\n- Adicionado o modo noturno para o estilo de mapa \"Topográfico\" -\n -\n- Adicionada a opção de descarregar as linhas de contorno na unidade de medida \"pés\" -\n -\n- Os trilhos podem agora ser coloridos conforme a altitude, velocidade ou declive. -\n -\n- Ecrã \"Gravar viagem\" atualizado -\n -\n- Opção de distância por toque movido da régua de raio para uma opção separada -\n -\n- Planear rota: agora pode alterar as opções de navegação, aceder ao mapa de configuração e pesquisar sem sair do ecrã de planear rota -\n -\n- Novo ecrã \"Comprar\" com informações detalhadas -\n -\n- Adicionada a opção para alterar a aparência da linha de rota. -\n -\n- Atualizações OsmAnd Live movidas para \"Descarregar mapas > Atualizações\". -\n -\n + - Tradução para português europeu completamente revista \n \n- Cópia de segurança para a nuvem \n \n- Adicionado o modo noturno para o estilo de mapa \"Topográfico\" \n \n- Adicionada a opção de descarregar as linhas de contorno na unidade de medida \"pés\" \n \n- Os trilhos podem agora ser coloridos conforme a altitude, velocidade ou declive\n \n- Ecrã \"Gravar viagem\" atualizado \n \n- Opção de distância por toque movido da régua de raio para uma opção separada \n \n- Planear rota: agora pode alterar as opções de navegação, aceder ao mapa de configuração e pesquisar sem sair do ecrã de planear rota \n \n- Novo ecrã \"Comprar\" com informações detalhadas \n \n- Adicionada a opção para alterar a aparência da linha de rota\n \n- Atualizações OsmAnd Live movidas para \"Descarregar mapas > Atualizações\" Introduza a palavra-passe de uso único há um momento atrás Sair do OsmAnd Cloud @@ -4309,31 +4100,7 @@ Conceder acesso Aprovado: %1$s Rejeitado: %2$s Categorias incluídas - - Adicionado o suporte inicial para o Android Auto -\n -\n - Atualização da interface do utilizador para pesquisa de coordenadas UTM -\n -\n • Filtro GPS para trilhos GPX -\n -\n • Widget de elevação (Pro) -\n -\n - Favoritos: agora pode-se ver os ícones usados recentemente -\n -\n - Planeamento de rotas: agora usa o perfil selecionado ao abrir a aplicação -\n -\n - Correção da camada Mapilary, o complemento está agora desativado por predefinição -\n -\n - Novo ecrã para gerir todo o histórico da aplicação -\n -\n - A orientação do mapa não é redefinida após reiniciar a aplicação -\n -\n - Melhoria da renderização do marcador de altura SRTM -\n -\n - Corrigida a visualização em árabe no mapa -\n -\n - Corrigidos diferentes problemas de roteamento -\n -\n + - Adicionado o suporte inicial para o Android Auto \n \n - Atualização da interface do utilizador para pesquisa de coordenadas UTM \n \n • Filtro GPS para trilhos GPX \n \n • Widget de elevação (Pro) \n \n - Favoritos: agora pode-se ver os ícones usados recentemente \n \n - Planeamento de rotas: agora usa o perfil selecionado ao abrir a aplicação \n \n - Correção da camada Mapilary, o complemento está agora desativado por predefinição \n \n - Novo ecrã para gerir todo o histórico da aplicação \n \n - A orientação do mapa não é redefinida após reiniciar a aplicação \n \n - Melhoria da renderização do marcador de altura SRTM \n \n - Corrigida a visualização em árabe no mapa \n \n - Corrigidos diferentes problemas de roteamento %1$s está desativado Devido a novas regras do Android, partir de novembro de 2021 o OsmAnd 4.2 vai perder o acesso ao armazenamento partilhado. \n @@ -4561,15 +4328,7 @@ Intermédia Preferir fora de pista Sem preferência - • Redesenho de widgets: nova aparência, agrupar por Páginas, alterar a ordem e combinar como desejar. -\n -\n • Caminhadas / ciclismo / rotas de viagem: toque no símbolo da rota e obtenha informações completas sobre a rota. -\n -\n • Grupos de favoritos: defina a aparência predefinida para novos pontos no grupo -\n -\n • Correções: atualização automática de mapas de mosaicos online -\n -\n + • Redesenho de widgets: nova aparência, agrupar por Páginas, alterar a ordem e combinar como desejar. \n \n • Caminhadas / ciclismo / rotas de viagem: toque no símbolo da rota e obtenha informações completas sobre a rota. \n \n • Grupos de favoritos: defina a aparência predefinida para novos pontos no grupo \n \n • Correções: atualização automática de mapas de mosaicos online Ative para ver as rotas de transportes públicos no mapa. A pedido Mostra a hora atual configurada no seu dispositivo. @@ -4717,7 +4476,7 @@ Largura da curva de nível de profundidade Vire à esquerda e mantenha-se na esquerda Mostra trilhos específicos para bicicletas de montanha. - Mostrar trilhos BTT + Mostrar trilhos BTT IMBA Está a ser acrescentado à lista um widget duplicado. Vire à esquerda e mantenha-se na direita Vire à direita e mantenha-se na esquerda @@ -4791,7 +4550,7 @@ Mostra a distância percorrida Mapas mundiais Sensores externos - Acede a sensores externos para ler, por exemplo, o ritmo cardíaco, velocidade da bicicleta, potência e cadência da bicicleta. Requer que o seu dispositivo esteja ligado aos respetivos sensores através do ANT + (Wireless Personal Network protocol). + Acede a sensores externos para ler, por exemplo, o ritmo cardíaco, velocidade da bicicleta, potência e cadência da bicicleta. Requer que o seu dispositivo esteja ligado aos respetivos sensores através do protocolo de rede pessoal sem fios ANT+. Mostra a elevação acima do nível do mar do centro do mapa atual. Altitude: localização atual Mostra a altitude da geolocalização atual ou a elevação do centro do mapa atual @@ -4813,31 +4572,7 @@ Sensores externos suportados Avisar perda ou recuperação do sinal GPS Avisar recálculo da rota - • Novo mecanismo de renderização de mapas mais rápido (versão 2 - OpenGL), com visualização 2.5D -\n -\n • Animação suave em alterações de localização durante o movimento -\n -\n • Novo widget com coordenadas do centro do mapa e elevação de geolocalizações -\n -\n • Adicionadas opções de tamanho de texto em \"Distância com 2 dedos\" -\n -\n • Adicionada opção ao widget \"Marcadores\" para alterar o comportamento do toque/clique -\n -\n • Corrigida opção de \"Posição no ecrã\" -\n -\n • GPX: adicionado suporte para etiquetas \"link\", \"cmt\", \"desc\" e para quaisquer extensões personalizadas -\n -\n • Reorganização da lista de \"mapas náuticos\" -\n -\n • Adicionado suporte a sensores ANT+ -\n -\n • Adicionada opção para desativar o descarregamento do UUID -\n -\n • Previsão ajustável de velocidade padrão para tempos de viagem dos perfis \"a pé/caminhada/corrida\" dependentes da elevação -\n -\n • Correções na saída de áudio e comportamento do controlo deslizante do volume -\n -\n + • Novo mecanismo de renderização de mapas mais rápido (versão 2 - OpenGL), com visualização 2.5D \n \n • Animação suave em alterações de localização durante o movimento \n \n • Novo widget com coordenadas do centro do mapa e elevação de geolocalizações \n \n • Adicionadas opções de tamanho de texto em \"Distância com 2 dedos\" \n \n • Adicionada opção ao widget \"Marcadores\" para alterar o comportamento do toque/clique \n \n • Corrigida opção de \"Posição no ecrã\" \n \n • GPX: adicionado suporte para etiquetas \"link\", \"cmt\", \"desc\" e para quaisquer extensões personalizadas \n \n • Reorganização da lista de \"mapas náuticos\" \n \n • Adicionado suporte a sensores ANT+ \n \n • Adicionada opção para desativar o descarregamento do UUID \n \n • Previsão ajustável de velocidade padrão para tempos de viagem dos perfis \"a pé/caminhada/corrida\" dependentes da elevação \n \n • Correções na saída de áudio e comportamento do controlo deslizante do volume Mostra e grava dados dos sensores externos: velocidade da bicicleta, potência da bicicleta, cadência e frequência cardíaca. Texto, sinais de trânsito e outros. Mostrar símbolos do mapa @@ -4854,12 +4589,7 @@ Versão 2 (OpenGL) A versão 2 (OpenGL) é um motor de renderização de mapas mais rápido também na visualização 2.5D. A versão 1 foi a versão padrão antes da versão do OsmAnd 4.3, e ainda é a recomendada em dispositivos mais antigos. Motor de renderização do mapa - Fornece -\n- a previsão meteorológica de 24h e 7 dias com 5 camadas -\n- widgets baseados em localização -\n- as isolinhas de temperatura / pressão -\n -\nOs dados meteorológicos são fornecidos pelo Global Forecast System (GFS, %1$s). + Fornece \n- a previsão meteorológica semanal com 5 camadas \n- widgets baseados em localização \n- as isolinhas de temperatura / pressão \n \nOs dados meteorológicos são fornecidos pelo Global Forecast System (GFS, %1$s). Previsão offline Ative para visualizar a camada de temperatura no mapa. As unidades de medida podem ser alteradas nas configurações do complemento Previsão meteorológica. @@ -4880,7 +4610,7 @@ Nascer do sol, pôr do sol Curvas de nível de profundidade Ative para visualizar os dados de profundidade náutica no mapa. - Cache de online + Cache online Temperatura Pressão atmosférica Vento @@ -4909,7 +4639,7 @@ Hectopascais Polegadas de mercúrio Milímetros de mercúrio - Previsão de 24 horas e 7 dias, atualizada a cada 3 horas + Previsão semanal, atualizada a cada 3 horas Alterne o modo tocando no widget. km/h Ative para visualizar a camada de vento no mapa. @@ -4923,20 +4653,20 @@ Mostra a precipitação para o centro do mapa atual. Espessura da linha A cópia está apenas %1$s%% completa. Se parar agora, a aplicação pode não funcionar corretamente. - Mostrar escala MTB - Mostrar trilhos conforme a escala MTB. + Mostrar escala BTT + Mostrar trilhos conforme a sua escala BTT. Uma opção para mostrar ou ocultar a camada de temperatura no mapa. Uma opção para mostrar ou ocultar a camada de precipitação no mapa. - Um botão para mostrar ou ocultar a camada de ventos no mapa. + Um botão para mostrar ou ocultar a camada de vento no mapa. Um botão para mostrar ou ocultar a camada de nuvens no mapa. Um botão para mostrar ou ocultar a camada de pressão do ar no mapa. Camada de temperatura Camada de pressão - Camada de ventos + Camada de vento Camada de nuvens Camada de precipitação Associação Internacional de Ciclismo de Montanha - Escala MTB + Escala BTT IMBA Classificação de segmento Isolinhas de nuvens @@ -5027,37 +4757,7 @@ Velocidade do movimento Selecione o ficheiro de um trilho que será usado na simulação do movimento. Adicionar manualmente… - • Menu \"Trilhos\" em \"Configurar mapa\" melhorado -\n -\n • O menu de contexto agora mostra nomes localizados nos POIs -\n -\n • Adicionada opção para partilhar rota com uma hiperligação -\n -\n • Adicionada unidade de comprimento \"milhas náuticas/pés\" -\n -\n • A orientação do mapa agora tem um novo modo fixo -\n -\n • Ecrã inicial simplificado -\n -\n • Posição de localização fixa no modo de ecrã dividido no Android Auto -\n -\n • Adicionado gráfico \"Velocidade/inclinação\" na análise do trilho de navegação -\n -\n • O Android 13 mudou a escolha do idioma da aplicação para as configurações do sistema Android -\n -\n • Corrigidos problemas com a visualização de locais com menus veganos -\n -\n • Adicionada a possibilidade de descarregar mosaicos on-line usados como mapas da \"Camada superior\" ou \"Camada inferior\" -\n -\n • Adicionado suporte de unidades imperiais em \"Parâmetros do veículo\" -\n -\n • Novo perfil de ciclomotor -\n -\n • Nova opção \"Automático\" em \"Posição do ecrã\" -\n -\n • Corrigidos gestos para inclinação, aproximação e rotação -\n -\n + • Menu \"Trilhos\" em \"Configurar mapa\" melhorado \n \n • O menu de contexto agora mostra nomes localizados nos POIs \n \n • Adicionada opção para partilhar rota com uma hiperligação \n \n • Adicionada unidade de comprimento \"milhas náuticas/pés\" \n \n • A orientação do mapa agora tem um novo modo fixo \n \n • Ecrã inicial simplificado \n \n • Posição de localização fixa no modo de ecrã dividido no Android Auto \n \n • Adicionado gráfico \"Velocidade/inclinação\" na análise do trilho de navegação \n \n • O Android 13 mudou a escolha do idioma da aplicação para as configurações do sistema Android \n \n • Corrigidos problemas com a visualização de locais com menus veganos \n \n • Adicionada a possibilidade de descarregar mosaicos on-line usados como mapas da \"Camada superior\" ou \"Camada inferior\" \n \n • Adicionado suporte de unidades imperiais em \"Parâmetros do veículo\" \n \n • Novo perfil de ciclomotor \n \n • Nova opção \"Automático\" em \"Posição do ecrã\" \n \n • Corrigidos gestos para inclinação, aproximação e rotação Primeiro a duração mais curta Primeiro a duração mais longa Primeiro a distância mais curta @@ -5139,7 +4839,7 @@ Lóbulo da orelha O Bluetooth está desligado - Ative os sensores de localização e emparelhamento do Bluetooth. + Ative o Bluetooth para encontrar e emparelhar sensores. A procurar sensores Certifique-se que: \n- O Bluetooth está ativado @@ -5215,12 +4915,8 @@ A conta e todos os dados foram eliminados do OsmAnd Cloud. Eliminar a conta %1$s? Eliminar conta - Em poucos instantes, todos os seus dados e conta serão eliminados do OsmAnd Cloud. -\n -\nTodos os dados no dispositivo permanecem intactos. - Eliminar a conta OsmAnd Cloud? -\n -\nEsta ação não pode ser desfeita. + Em poucos instantes, todos os seus dados e conta serão eliminados do OsmAnd Cloud. \n \nTodos os dados no dispositivo permanecerão intactos. + Eliminar a conta OsmAnd Cloud? \n \nEsta ação não pode ser revertida. Esta ação não pode ser desfeita. Os dispositivos secundários serão desconectados do OsmAnd Cloud e perderão o acesso às funcionalidades pagas. A sua conta e todos os detalhes da conta serão %1$s @@ -5246,49 +4942,7 @@ Cópia de segurança das configurações gratuita Percurso de montanha equipado com cabos fixos, grampos, escadas e pontes. Usar sqltiedb raster para sombras de relevo / declives - - Sensores BLE/ANT+: ligação reformulada, escrita e visualização dos dados recebidos no GPX -\n -\n - Cópia de segurança gratuita dos favoritos e das configurações para \"OsmAnd Cloud\" -\n -\n - Novo formato para mapas de terreno com suporte de relevo 3D -\n -\n - Gestão de trilhos: interface de utilizador atualizada, ordenação adicionada, suporte de pastas aninhadas -\n -\n - Android Auto: novo menu inicial para aceder aos favoritos, trajetos e categorias de POI -\n -\n - Uma lista de widgets para o painel superior/inferior -\n -\n - Novo perfil \"Comboio\", que permite calcular percursos ferroviários -\n -\n - As curvas de nível funcionam com qualquer \"fonte de mapa\" -\n -\n - Lista alargada de ações predefinidas para teclados externos -\n -\n - Ficheiros de encaminhamento personalizados, opções adicionadas para eliminar ou substituir -\n -\n - Opção adicionada para \"Anunciar o desvio do itinerário\" -\n -\n - Ecrã inicial: opção adicionada para restaurar o estado do OsmAnd a partir de um ficheiro -\n -\n - Adicionadas classes \"Materiais perigosos\" na navegação de camiões na América do Norte -\n -\n - Adicionado o suporte da \"Via ferrata\" para rotas pedestres -\n -\n - Adicionada a escala \"CAI\" para classificação da dificuldade dos percursos -\n -\n - Correção da seleção do idioma por aplicação -\n -\n - Adicionado suporte para ícones temáticos -\n -\n - OsmAnd Cloud: adicionada a opção de eliminar a conta -\n -\n - Acessibilidade: o tamanho do alvo de toque para os botões foi alterado para 48 dp; foi corrigido um problema com a IU do mapa visível para leitores de ecrã -\n -\n - Avisos de voz em japonês reformulados -\n -\n - Alteração da elevação e avaliação do declive reformuladas -\n -\n + - Sensores BLE/ANT+: ligação reformulada, escrita e visualização dos dados recebidos no GPX \n \n - Cópia de segurança gratuita dos favoritos e das configurações para \"OsmAnd Cloud\" \n \n - Novo formato para mapas de terreno com suporte de relevo 3D \n \n - Gestão de trilhos: interface de utilizador atualizada, ordenação adicionada, suporte de pastas aninhadas \n \n - Android Auto: novo menu inicial para aceder aos favoritos, trajetos e categorias de POI \n \n - Uma lista de widgets para o painel superior/inferior \n \n - Novo perfil \"Comboio\", que permite calcular percursos ferroviários \n \n - As curvas de nível funcionam com qualquer \"fonte de mapa\" \n \n - Lista alargada de ações predefinidas para teclados externos \n \n - Ficheiros de encaminhamento personalizados, opções adicionadas para eliminar ou substituir \n \n - Opção adicionada para \"Anunciar o desvio do itinerário\" \n \n - Ecrã inicial: opção adicionada para restaurar o estado do OsmAnd a partir de um ficheiro \n \n - Adicionadas classes \"Materiais perigosos\" na navegação de camiões na América do Norte \n \n - Adicionado o suporte da \"Via ferrata\" para rotas pedestres \n \n - Adicionada a escala \"CAI\" para classificação da dificuldade dos percursos \n \n - Correção da seleção do idioma por aplicação \n \n - Adicionado suporte para ícones temáticos \n \n - OsmAnd Cloud: adicionada a opção de eliminar a conta \n \n - Acessibilidade: o tamanho do alvo de toque para os botões foi alterado para 48 dp; foi corrigido um problema com a IU do mapa visível para leitores de ecrã \n \n - Avisos de voz em japonês reformulados \n \n - Alteração da elevação e avaliação do declive reformuladas Passeio gratuito Tipos de classificação de dificuldade para as rotas. %1$s • %2$s • %3$s @@ -5383,29 +5037,7 @@ Tirar nota de mídia Atividade de retorno pressionada Discussão no GitHub - • Android Auto: adicionado modo 3D -\n -\n• Trilhos: filtros introduzidos e pastas inteligentes, memória otimizada para ficheiros maiores -\n -\n• Painéis superiores e inferiores melhorados para acomodar todos os widgets -\n -\n• Melhoria da gestão de recursos locais -\n -\n• Mapeamento de botões personalizável para controladores externos -\n -\n• Novos widgets: \"Rácio de planar\" e \"Posição do sol\" -\n -\n• OsmAnd Cloud: acesso a ficheiros eliminados -\n -\n• Coloração melhorada do trilho no modo Declive com novo gradiente simétrico -\n -\n• Problemas resolvidos com barra de estado transparente -\n -\n• Adicionado suporte para widget de temperatura ANT+ -\n -\n• Novo widget \"RAM disponível\" -\n -\n + • Android Auto: adicionado modo 3D \n \n• Trilhos: filtros introduzidos e pastas inteligentes, memória otimizada para ficheiros maiores \n \n• Painéis superiores e inferiores melhorados para acomodar todos os widgets \n \n• Melhoria da gestão de recursos locais \n \n• Mapeamento de botões personalizável para controladores externos \n \n• Novos widgets: \"Rácio de planar\" e \"Posição do sol\" \n \n• OsmAnd Cloud: acesso a ficheiros eliminados \n \n• Coloração melhorada do trilho no modo Declive com novo gradiente simétrico \n \n• Problemas resolvidos com barra de estado transparente \n \n• Adicionado suporte para widget de temperatura ANT+ \n \n• Novo widget \"RAM disponível\" Trilhos e rotas Parapente Mais visto @@ -5516,7 +5148,7 @@ Eliminar item Começar com o OsmAnd Compras iOS - Rotas de bicicleta (MTB) + Rotas de bicicleta (BTT) Widgets de navegação Interagir com o mapa Ações @@ -5625,25 +5257,7 @@ Java (seguro) A* 2 fases Tâmil (Índia) - - Novo widget \"Velocímetro\" compatível com Android Auto -\n -\n - Configure o ecrã do mapa adicionando vários botões de \"Ação rápida\" -\n -\n - Melhorada a legibilidade dos dados nos gráficos -\n -\n - Adicionados filtros por dados do sensor em trilhos -\n -\n - Personalização da aparência melhorada para grupos de trilhos -\n -\n - Adicionado suporte para etiquetas GPX adicionais -\n -\n - Personalização da \"Distância durante a navegação\": escolha entre números exatos ou arredondados -\n -\n - Interface unificada para seleção de trilhos -\n -\n - Autenticação no OpenStreetMap alterada para OAuth 2.0 -\n -\n + - Novo widget \"Velocímetro\" compatível com Android Auto \n \n - Configure o ecrã do mapa adicionando vários botões de \"Ação rápida\" \n \n - Melhorada a legibilidade dos dados nos gráficos \n \n - Adicionados filtros por dados do sensor em trilhos \n \n - Personalização da aparência melhorada para grupos de trilhos \n \n - Adicionado suporte para etiquetas GPX adicionais \n \n - Personalização da \"Distância durante a navegação\": escolha entre números exatos ou arredondados \n \n - Interface unificada para seleção de trilhos \n \n - Autenticação no OpenStreetMap alterada para OAuth 2.0 Distância durante a navegação Preciso Arredondado @@ -5665,29 +5279,7 @@ Alterações Aplicar alterações aos trilhos existentes na pasta ou apenas aos novos? Aplicar apenas a novos - • Nova opção de coloração do terreno \"Altitude\" -\n -\n • Paletas de cores personalizadas para terreno, trilhos e rotas -\n -\n • Variante 3D para ícones de posição de localização -\n -\n • As ações rápidas podem agora ser atribuídas a teclados externos -\n -\n • Previsão meteorológica: atualização da interface do utilizador, adição de animação do vento -\n -\n • Melhorias nos percursos 3D: novas opções de visualização e coloração -\n -\n • Seleção alargada de percursos turísticos no mapa -\n -\n • Adicionada a opção de tema da aplicação para seguir o modo de mapa -\n -\n • Correção das estatísticas dos percursos, ordenação e pesquisa -\n -\n • Edição OSM: método de autorização de login/palavra-passe eliminado -\n -\n • Mudança para a folha de partilha do sistema a partir do Android 14 -\n -\n + • Nova opção de coloração do terreno \"Altitude\"\n\n • Paletas de cores personalizadas para terreno, trilhos e rotas\n \n • Variante 3D para ícones de posição de localização \n \n • As ações rápidas podem agora ser atribuídas a teclados externos\n \n • Previsão meteorológica: atualização da interface do utilizador, adição de animação do vento\n \n • Melhorias nos percursos 3D: novas opções de visualização e coloração\n \n • Seleção alargada de percursos turísticos no mapa\n \n • Adicionada a opção de tema da aplicação para seguir o modo de mapa\n \n • Correção das estatísticas dos percursos, ordenação e pesquisa\n \n • Edição OSM: método de autorização de login/palavra-passe eliminado\n \n • Mudança para a folha de partilha do sistema a partir do Android 14 Escala São necessários mapas adicionais para visualizar o terreno no mapa. Ao alterar o valor da escala, o relevo 3D é realçado. @@ -5889,7 +5481,7 @@ Alterar camadas de contornos Alterar ampliação do mapa. Alterar inclinação do mapa. - Mostrar trilhos de terra para bicicletas + Mostrar trilhos para motos todo-o-terreno Alterar o estilo do mapa, as camadas ou outras configurações. dp Tipo de atividade @@ -5945,7 +5537,7 @@ Hiperligações de terceiros Repor velocidade média Dispositivo desconhecido - Trilhos para motos fora de estrada + Trilhos para motos todo-o-terreno Não foi indicado o tipo de combustível Gasolina Botão para mostrar ou ocultar as edições locais do OSM no mapa. @@ -5958,12 +5550,12 @@ L/h Novo segmento de viagem Um botão para iniciar, pausar ou retomar a gravação do trilho GPX. - • Adicionado um visualizador de galeria de ecrã inteiropara imagens Wikimedia\n\n • Introduzido um novo complemento “Métricas do veículo” para monitorizar o desempenho do veículo utilizando o protocolo OBD-II\n\n • Adicionada a capacidade de atribuir atividades a trilhos e filtrá-los em conformidade\n\n • Implementação de novas ações rápidas para gravação de viagens e bloqueio do ecrã tátil\n\n • Introdução de um aspeto personalizável do botão do mapa e de uma grelha precisa\n\n • Adicionado um menu de contexto e uma ação “Repor a velocidade média” aos widgets\n\n • Adicionada uma nova camada de percurso “Trilhos de motas fora de estrada”\n\n • Corrigido o “Registo automático de trajeto durante a navegação”\n\n • Corrigido o problema com coordenadas invertidas em RTL\n\n • Corrigida a falta de dados do sensor para o percurso atualmente gravado\n\n • Adicionada informação sobre as áreas circundantes para o ponto selecionado\n\n • Adicionada ação rápida para controlar a visibilidade das edições OSM\n\n • Parâmetros de visibilidade separados para as opções de colorização do terreno\n\n • Adicionado um ícone que pode ser tocado para mostrar o acesso ou outros atributos de estradas ou caminhos\n\n + • Adicionado um visualizador de galeria de ecrã inteiro para imagens Wikimedia\n\n • Introduzido um novo complemento “Métricas do veículo” para monitorizar o desempenho do veículo utilizando o protocolo OBD-II\n\n • Adicionada a capacidade de atribuir atividades a trilhos e filtrá-los em conformidade\n\n • Implementação de novas ações rápidas para gravação de viagens e bloqueio do ecrã tátil\n\n • Introdução de um aspeto personalizável do botão do mapa e de uma grelha precisa\n\n • Adicionado um menu de contexto e uma ação “Repor a velocidade média” aos widgets\n\n • Adicionada uma nova camada de percurso “Trilhos de motas fora de estrada”\n\n • Corrigido o “Registo automático de trajeto durante a navegação”\n\n • Corrigido o problema com coordenadas invertidas em RTL\n\n • Corrigida a falta de dados do sensor para o percurso atualmente gravado\n\n • Adicionada informação sobre as áreas circundantes para o ponto selecionado\n\n • Adicionada ação rápida para controlar a visibilidade das edições OpenStreetMap\n\n • Parâmetros de visibilidade separados para as opções de colorização do terreno\n\n • Adicionado um ícone que pode ser tocado para mostrar o acesso ou outros atributos de estradas ou caminhos Mostra o nível de combustível do veículo através do sensor OBD - Temperatura do ar de admissão - Temperatura do ar ambiente - Temperatura do líquido de arrefecimento do motor - Nível de combustível + Temperatura de admissão + Temperatura ambiente + Temperatura do líquido de refrigeração + Eficiência de combustível (%) Taxa de consumo de combustível Mostra a taxa de consumo de combustível do veículo com base no OBD e localização Tipo de combustível @@ -5974,7 +5566,112 @@ Wikivoyage offline Mostra o tipo de combustível do veículo através do sensor OBD Voltagem da bateria - Acede às informações do seu veículo através do protocolo OBD + Monitorizar os principais parâmetros do veículo utilizando o protocolo OBD-II. Aceder a dados que incluem:\n • Velocidade do motor, tempo de funcionamento do motor, carga calculada do motor \n • Consumo de combustível, pressão do combustível, combustível restante\n • Temperatura de admissão, temperatura ambiente, temperatura do líquido de refrigeração, temperatura do óleo do motor\n • Velocidade do veículo, posição do acelerador, tensão da bateria\n %1$s — %2$s Mostra a distância que o veículo pode percorrer com o nível de combustível restante no sensor OBD + Tempo de funcionamento do motor + Mostra a velocidade do sensor de velocidade do veículo (VSS). + Mostra a temperatura do ar que entra no coletor de admissão do motor. + Monitorize os dados vitais do veículo utilizando o protocolo OBD-II. Monitorize as principais métricas como a velocidade, as RPM do motor, o consumo de combustível e muito mais. + As vias de trânsito (faixas de rodagem) serão apresentadas em todas as intersecções de passagem, independentemente das instruções de voz + Conectado a %s + Simular dados OBD + Orientação do itinerário + Velocidade do veículo + Velocidade do motor + A procurar leitores + Ative o Bluetooth no seu dispositivo + Toque em \'%1$s\' e selecione o leitor na lista. + Mostra a temperatura do líquido de arrefecimento do motor + Mostra a temperatura no exterior do veículo. + Mostra o nível de tensão da bateria do veículo + Registar as métricas do veículo + Pressão do combustível + O leitor será removido da lista. Poderá voltar a emparelhar este leitor em qualquer altura. + 1. Conecte o leitor à porta OBD-II do seu veículo.\n\n 2. Ligue o motor do veículo\n\n 3. Ative o Bluetooth no seu dispositivo\n\n 4. Toque em “%1$s” e selecione o leitor na lista. + Posição do acelerador + O OsmAnd suporta conexões Bluetooth com leitores OBD-II. + Taxa de consumo de combustível (L/h) + Mostra a taxa de consumo de combustível do veículo com base em cálculos (L/h) + Taxa de consumo de combustível + Combustível restante (km) + Taxa de consumo de combustível (obd) + O ecrã tátil está bloqueado. Para desbloquear, prima o botão %1$s ou toque no botão no ecrã. + O ícone muda com base no estado da ação. + A conectar a %s + Pré-visualizar a próxima viragem + %1$s por %2$s + Repor o valor médio + Selecionar o intervalo de tempo para medir o valor médio. + Selecionar o intervalo de tempo para medir a temperatura média. + Temperatura atual + Temperatura média + Esquecer leitor + Não foram encontrados leitores + Fornece orientação de percurso em tempo real, incluindo a distância até à próxima curva, o nome da estrada, o nome da rua atual e informações sobre a via de trânsito (faixa de rodagem). Os widgets estão ativos durante a navegação. + Perfil selecionado \"%s\" + Máximo + Mostra a taxa de consumo de combustível do veículo com base em cálculos (L/100 km) + Mostra há quanto tempo o motor está a funcionar desde que foi ligado. + Taxa de consumo de combustível %/h + Ativar o Bluetooth para localizar e conectar os leitores. + Ângulo 3D com a ampliação automática + Taxa de amostragem de áudio + Selecionar a taxa de amostragem de áudio. + Carga calculada do motor + Instantâneo + Mostra a taxa de consumo de combustível pelo motor, calculada com base no combustível restante, no tamanho do depósito e na distância percorrida determinada pelo GPS. + Consumo de combustível (L/100 km) + Mostra a taxa de consumo de combustível do veículo com base no sensor (L/h) + Não é possível iniciar o emparelhamento com o dispositivo + Mostra a taxa de consumo de combustível do veículo com base em cálculos (%/h) + Visualização em intersecções menores quando se navega em linha reta + Nível de combustível (%) + Verifique o seguinte:\n - Verifique se o Bluetooth está ativado\n - O motor do veículo está a funcionar\n - Certifique-se de que o seu dispositivo está dentro do alcance do leitor. + Bluetooth + Informação do veículo + Como conectar: + Ligue o motor do seu veículo + L/100km + Mostra a percentagem da potência disponível do motor que está a ser utilizada num determinado momento. + Mostra a temperatura do óleo do motor. + Conectar leitor novo + Interpolação + Percentagem de interpolação de localização + Defina a percentagem de interpolação da localização durante a navegação na rota. Este parâmetro reduz o desfasamento da posição da sua localização no mapa durante a animação. + Métrica do veículo (OBD-II) + Hora + Volume + Mostra a pressão no sistema de combustível + Mostra o ângulo do pedal do acelerador no corpo do acelerador. + Utilizar unidades selecionadas no nome. + Grelha de dados WunderLINQ + Temperatura do óleo do motor + Dinâmico + Desconectado de %s + Taxa de consumo de combustível (sensor) + l + %/h + Nível de combustível (L) + Mantenha o seu dispositivo perto do leitor. Certifique-se de que o motor do veículo está a funcionar. + Conecte o leitor à porta OBD-II do seu veículo. + Saiba mais sobre sensores. + Motor + Selecionar os parâmetros a registar no ficheiro GPX. + Mostra a quantidade de combustível restante no depósito do veículo + Combustível restante + Introduza a capacidade do seu depósito para controlar com precisão os níveis de combustível e o consumo + Capacidade do depósito de combustível + Dimensões + Litros + gal + Galões imperiais + Galões americanos + As unidades selecionadas serão aplicadas a todos os dados relacionados com o volume no perfil atual. + Unidade de volume + O ecrã tátil está desbloqueado. + O ecrã tátil está bloqueado. Toque no botão para desbloquear. + Combustível restante (litros) + Consumo de combustível + kPa \ No newline at end of file diff --git a/OsmAnd/res/values-ro/strings.xml b/OsmAnd/res/values-ro/strings.xml index e560914974a..fffbce859cc 100644 --- a/OsmAnd/res/values-ro/strings.xml +++ b/OsmAnd/res/values-ro/strings.xml @@ -5935,4 +5935,64 @@ Unitate de volum Înregistrarea măsurătorilor vehiculului Pictograma se modifică în funcție de starea acțiunii. + kPa + Simularea datelor obd + Ghidare pe traseu + Presiunea combustibilului + Călătorie înregistrată & Continuare + Un buton pentru a începe un nou segment în înregistrarea pistei GPX în curs. + l + Combustibil rămas (km) + Viteza vehiculului + Turația motorului + Căutarea de scanere + Deconectat de la %s + Conectare la %s + Conectat la %s + Rata consumului de combustibil %/h + Rata consumului de combustibil (obd) + Bluetooth + Scanerul va fi eliminat din listă. Veți putea oricând să asociați din nou acest scaner. + Vă rugăm să verificați următoarele:\n - Asigurați-vă că Bluetooth este activat\n - Motorul vehiculului este pornit\n - Asigurați-vă că dispozitivul dumneavoastră se află în raza de acțiune a scanerului. + Oferă ghidare pe traseu în timp real, inclusiv distanța până la următorul viraj, numele drumului, numele actual al străzii și informații despre benzi. Widget-urile sunt active în timpul navigării. + Informații despre vehicul + OsmAnd acceptă conexiuni Bluetooth la scanerele OBD-II. + Activați Bluetooth pentru a găsi și conecta scanerele. + Uitați de scaner + Dinamică + Nu se poate iniția împerecherea cu dispozitivul + Nivelul combustibilului (l) + Nivelul combustibilului (%) + Nu s-au găsit scanere + Vă rugăm să țineți dispozitivul aproape de scaner. Asigurați-vă că motorul autovehiculului este pornit. + Rata consumului de combustibil + %/h + L/100km + Maxim + Indică rata consumului de combustibil al vehiculului pe baza calculelor (L/100 km) + Indică rata consumului de combustibil al vehiculului pe baza calculelor (%/h) + Rata consumului de combustibil (senzor) + Cum să vă conectați: + Conectați scanerul la portul OBD-II al autovehiculului. + 1. Conectați scanerul la portul OBD-II al vehiculului.\n\n 2. Porniți motorul vehiculului \n\n 3. Activați Bluetooth pe dispozitiv\n\n 4. Atingeți „%1$s” și selectați scanerul din listă. + Rata consumului de combustibil (L/100 km) + Indică rata consumului de combustibil al vehiculului pe baza senzorului (L/h) + Rata consumului de combustibil (L/h) + Porniți motorul autovehiculului + Activați Bluetooth pe dispozitivul dumneavoastră + Atingeți „%1$s,” și selectați scanerul din listă. + Un buton pentru a salva traseul GPX și a termina înregistrarea călătoriei. + Nou segment de călătorie + Start / Pauză + Un buton pentru a salva călătoria curentă înregistrată ca fișier GPX și pentru a continua înregistrarea unei noi piste fără întrerupere. + Un buton pentru a porni, întrerupe sau relua înregistrarea pistei GPX. + Vă rugăm să începeți mai întâi înregistrarea călătoriei. + Indică rata consumului de combustibil al vehiculului pe baza calculelor (L/h) + Aflați mai multe despre senzori. + Ecranul tactil este blocat. Pentru deblocare, apăsați butonul %1$s sau atingeți butonul de pe ecran. + A început înregistrarea unui nou segment de pistă. + Track-ul nu conține date de salvat. + Ecranul tactil este blocat. Atingeți butonul pentru a debloca. + - A fost adăugat un vizualizator de galerii full-screen pentru imaginile Wikimedia\n\n - Introdus un nou plugin „Vehicle Metrics” pentru a monitoriza performanța vehiculului utilizând protocolul OBD-II\n\n - A fost adăugată posibilitatea de a atribui activități pistelor și de a le filtra în consecință\n\n - Implementate noi acțiuni rapide pentru înregistrarea călătoriei și blocarea ecranului tactil\n\n - Introdus aspectul personalizabil al butonului de hartă și o grilă precisă\n\n - Adăugat un meniu contextual și o acțiune „Resetare viteză medie” pentru widget-uri\n\n - Adăugat un nou strat de traseu „Trasee pentru biciclete Dirt”\n\n - Fixat „Înregistrare automată a traseului în timpul navigării”\n\n - S-a rezolvat problema cu coordonatele răsturnate în RTL\n\n - S-a rezolvat problema lipsei datelor senzorilor pentru înregistrarea curentă a traseului\n\n - Au fost adăugate informații despre zonele înconjurătoare pentru punctul selectat\n\n - A fost adăugată o acțiune rapidă pentru a controla vizibilitatea edițiilor OSM\n\n - Parametrii de vizibilitate separați pentru opțiunile de colorare a terenului\n\n - Adăugat o pictogramă care poate fi atinsă pentru a afișa accesul sau alte atribute de drum sau cărare + Ecranul tactil este deblocat. \ No newline at end of file diff --git a/OsmAnd/res/values-ru/strings.xml b/OsmAnd/res/values-ru/strings.xml index 8850ef1e219..062f2cb0554 100644 --- a/OsmAnd/res/values-ru/strings.xml +++ b/OsmAnd/res/values-ru/strings.xml @@ -6080,4 +6080,5 @@ Процент интерполяции местоположения Интерполяция Установите процент интерполяции местоположения при навигации по маршруту. Этот параметр уменьшает задержку отображения вашего местоположения на карте во время анимации. + Выбранный профиль «%s» \ No newline at end of file diff --git a/OsmAnd/res/values-sk/strings.xml b/OsmAnd/res/values-sk/strings.xml index 824e1181f62..35ae88438a3 100644 --- a/OsmAnd/res/values-sk/strings.xml +++ b/OsmAnd/res/values-sk/strings.xml @@ -5665,4 +5665,20 @@ Odkazy inam Interpolácia Zvolený profil \"%s\" + Hybridný motor na etanol + Hybridný motor na LPG + Hybridný motor na CNG + Hybridný elektrický a spaľovací motor + Hybridný motor na benzín + Hybridný motor na metanol + Hybridný motor na propán + Hybridný elektrický + Hybridný etanolový + Hybridný naftový + Hybridný benzínový + Hybridný na elektrický a spaľovací motor + Hybridný regeneratívny + Hybridný na vodík + Hybridný vodíkový + Hybridný motor na elektrinu \ No newline at end of file diff --git a/OsmAnd/res/values-sr/phrases.xml b/OsmAnd/res/values-sr/phrases.xml index a7468220dcd..1718742237a 100644 --- a/OsmAnd/res/values-sr/phrases.xml +++ b/OsmAnd/res/values-sr/phrases.xml @@ -4884,20 +4884,9 @@ Тунел забрањен за бициклисте Тунел забрањен за пешаке Тачке на путовањима - Планинарење - Спортови на води - Јахање коња - Зимски спортови - Моторне санке - Брдски бициклизам Чланци о путовањима Друге руте - Тачке других рута - Бицикл - Пешачење - Трчање - Стазе за трчање - Котураљке + Тачке руте Етимологија имена Назив леве стране Назив десне стране @@ -4907,4 +4896,17 @@ Основно Стандардно Делукс + Зимски спортови + Ваздушни спортови + Вожња + Спортови на води + Бициклизам + Пешке + Друге руте + Мотоциклизам + Ван пута + Картинг + ВСТ (Возило за све терене) + Камион (ХГВ) + Кола \ No newline at end of file diff --git a/OsmAnd/res/values-uk/phrases.xml b/OsmAnd/res/values-uk/phrases.xml index d6bbc9af9cf..72e61aa467e 100644 --- a/OsmAnd/res/values-uk/phrases.xml +++ b/OsmAnd/res/values-uk/phrases.xml @@ -4880,18 +4880,7 @@ Статті про подорожі Пункти Інші маршрути - Інші пункти маршруту - Велосипед - Піший туризм - Біг - Ходьба - Водні види спорту - Верхова їзда - Зимові види спорту - Снігохід - Фітнес-стежки - Роликові ковзани - Катання на гірських велосипедах + Точки маршруту Паркомісце не прийнято Паркомісце Паркер не прийнято @@ -4904,4 +4893,74 @@ Делюкс Тип турбази Стандартний + стопа + Мотоциклів + Інші маршрути + Велоспорт + Зимові види спорту + Водіння + Повітряний спорт + Водні види спорту + Пригодницький мотоцикл + Гравійний велосипед + Туризм Мотоцикл + Ходьба на снігоступах + Шосейний мотоцикл + Біг по стежці + Надлегка Авіація + Роликові ковзани + бздоріжжя + Піші прогулянки + ATV (Всюдихід) + Вантажівка (HGV) + картинг + Мотокрос + Мотоцикл по бездоріжжю (байк) + Гонки на треку + Моторолер + Задня упаковка + Трекінг + ходьба + Лижні гонки + Катання на лижах + Катання на снігоходах + Політ на параплані + Моторний параплан (парамотор) + Каное + Дайвінг + Підводне плавання + Катання на водних лижах + Віндсерфінг + Моторний човен + Верхова Їзда + Їзда на поїзді + Гра в гольф + Збирання грибів + Спорт Мотоцикл + Мотоцикл Ендуро + Катання на сноуборді + Політ на повітряній кулі + Дельтапланеризм + Фітнес + Шосейний велосипед + автомобіль + Лижний Туризм + Електричний Велосипед + Гастролі + Авіація + Рафтинг + Електричний гірський велосипед + Спелеологія + Біг по дорозі + Катання на Ковзанах + Байдарка + Серфінг + Підйом на гору + Катання на гірських велосипедах + Лазіння по деревах + Польоти дронів + Стрибки з парашутом + Планерування (планер) + збирання врожаю + Плавання на відкритому повітрі \ No newline at end of file diff --git a/OsmAnd/res/values-vi/strings.xml b/OsmAnd/res/values-vi/strings.xml index 3d040534a5d..f8887f0c0bd 100644 --- a/OsmAnd/res/values-vi/strings.xml +++ b/OsmAnd/res/values-vi/strings.xml @@ -2939,4 +2939,49 @@ Động cơ Khối lượng Hồ sơ đã chọn \"%s\" + Dùng dấu thời gian bên ngoài + Do các quy định mới của Android, bắt đầu từ tháng 11 năm 2021, OsmAnd 4.2 sẽ mất quyền truy cập vào Bộ nhớ dùng chung.\n\nVui lòng chuyển thư mục dữ liệu từ Bộ nhớ dùng chung sang bộ nhớ khả dụng, nếu không ứng dụng sẽ mất quyền truy cập vào dữ liệu của bạn như bản đồ ngoại tuyến, các tuyến GPX, v.v. + Các phố được cho phép rõ ràng + Ưu tiên dải cảnh báo cho người khiếm thị + Cung cấp thông tin về bề mặt đường/lối đi bộ. + Ưu tiên tuyến đường cho ngựa + Phân loại khả năng di chuyển của đường/lối đi bộ đối với phương tiện có bánh, đặc biệt là về độ đều/độ phẳng của bề mặt. + Chỉ dùng những lối đi (cung đường, đường mòn v.v.) được cho phép một cách rõ ràng + %1$s đã bị tắt + Các danh mục đã bao gồm + Chỉ dùng những phố được cho phép một cách rõ ràng + Nhấn Chọn để thoát chế độ kéo + Những loại tuyến đường + Cho phép việc sử dụng các cổng + Thêm nhóm vào danh sách điểm đánh dấu + Bạn không có phần bổ trợ nào được bật. Bạn có thể tìm được danh sách phần bổ trợ trong mục \'Menu\' → \' \'Phần bổ trợ\'. + Điều hướng tới \'Nhà\' + Mua OsmAnd Pro hoặc Maps+ để dùng OsmAnd với Android Auto + Các tệp + Ngắt thiết bị của bạn khỏi Android Auto để xem bản đồ trong ứng dụng. + OsmAnd cần quyền Vị trí để chỉ đường. + Cho phép truy cập + Những lối đi được phép rõ ràng + Ví dụ Wikivoyage + Dịch vụ không khả dụng. Vui lòng thử lại sau. + Bộ nhớ dùng chung không còn khả dụng do quyền truy cập bộ nhớ của Android đã thay đổi. + Điều hướng tới \'Nơi làm việc\' + Dùng dấu thời gian của bộ định tuyến trực tuyến để điều chỉnh thời gian dự kiến và tốc độ trung bình + Bản đồ được hiển thị trên màn hình của phương tiện. + Đã cho phép: %1$s Đã từ chối: %2$s + Những thay đổi với quy định về quyền truy cập bộ nhớ + Ưu tiên dải cảnh báo cho người khiếm thị + Cho phép sử dụng cổng + Cưỡi ngựa + Bắt đầu + Hiển thị bản đồ dưới thanh trạng thái + Gỡ nhóm khỏi danh sách điểm đánh dấu + Tô màu tuyến đường hoặc nét vẽ cung đường theo phân loại đường. + Để trống, tên cung đường sẽ được dùng làm mô tả. Phần mô tả được nhập vào sẽ áp dụng cho từng cung đường. + Điều hướng tới… + Tiếp tục trên thiết bị di động + Ưu tiên tuyến đường cho ngựa + Cho phép lịch sử lưu lại danh sách những điểm đánh dấu bản đồ đã được ghé thăm. + Mang những bản đồ ngoại tuyến của OsmAnd và điều hướng tới màn hình trên xe của bạn và tích hợp với những điều khiển xe của bạn. + Tìm kiếm: %1$s \ No newline at end of file diff --git a/OsmAnd/res/values-zh-rCN/phrases.xml b/OsmAnd/res/values-zh-rCN/phrases.xml index 3e5a3b3f947..f8b76d1e0a4 100644 --- a/OsmAnd/res/values-zh-rCN/phrases.xml +++ b/OsmAnd/res/values-zh-rCN/phrases.xml @@ -10,7 +10,7 @@ 支付类别 燃料类型 加油卡 - 额外的 + 额外 互联网接入类型 自行车服务 自助 diff --git a/OsmAnd/res/values-zh-rCN/strings.xml b/OsmAnd/res/values-zh-rCN/strings.xml index 9146bacdc9b..d8e108cbffa 100644 --- a/OsmAnd/res/values-zh-rCN/strings.xml +++ b/OsmAnd/res/values-zh-rCN/strings.xml @@ -5774,4 +5774,69 @@ 显示油门在节流阀体内的角度。 显示引擎冷却液的温度 显示车外温度。 + 立即 + 选中的配置文件 “%s” + 选择平均值测量的时间间隔。 + 平均温度 + 当前温度 + 插值 + 引擎 + 选择要记录到 GPX 文件中的参数。 + 位置插值百分比 + 设置路线导航时位置插值的百分比。该参数可减少动画播放时,地图上出现的位置滞后。 + 容量 + 选择测量平均温度的时间间隔。 + 触摸屏已解锁。 + 提供实时路线导航,包括到下一个转弯的距离、道路名称、当前街道名称和车道信息。小工具会在导航过程中启用。 + 未发现扫描仪 + 扫描仪将从列表中移除。您随时可以重新配对该扫描仪。 + 1. 将扫描仪连接到车辆的 OBD-II 接口。\n\n 2. 打开车辆发动机\n\n 3. 启用设备上的蓝牙功能\n\n 4. 点击 “%1$s” ,然后从列表中选择您的扫描仪。 + 触摸屏已经锁定。轻按按钮以解锁。 + 燃料消耗率 (百分比%/小时) + 将扫描仪连接到车辆的 OBD-II 接口。 + 燃料消耗率(升/100 公里) + 燃料消耗率(升/小时) + 请检查以下内容:\n - 确保蓝牙已打开\n - 车辆发动机正在运转\n - 确保您的设备在扫描仪的扫描范围内。 + 触摸屏已锁定。要解锁,请按 %1$s 按钮,或轻点屏幕上的按钮。 + 无法开始与设备的配对 + 根据传感器显示车辆燃料消耗率(升/小时) + 燃料消耗率(OBD) + 已连接到 %s + 图标会根据行为状态改变。 + 动态 + 记录车辆指标 + WunderLINQ 数据网格 + 蓝牙 + 车辆信息 + OsmAnd 支持蓝牙连接至 OBD-II 扫描仪。 + 无论语音指令如何,所有通行路口都将显示车道 + 引擎转速 + 打开蓝牙查找并连接扫描仪。 + 正在搜寻扫描仪 + 在地图上显示或隐藏本地 OSM 编辑的按钮。 + 直行导航时显示小交叉路口 + 最大 + - 为维基媒体图片添加了全屏图库浏览器\n\n - 推出新插件 “车辆指标”,使用 OBD-II 协议监控车辆性能\n\n - 增加了为轨迹分配活动并进行相应过滤的功能\n\n - 为行程记录和触摸屏锁定实现新的快速操作\n\n - 引入可定制的地图按钮外观和精确网格\n\n - 为小工具添加上下文菜单和 “重置平均速度”操作\n\n - 添加新的路线图层 “泥地自行车小径”\n\n - 已修复 “导航时自动记录轨迹”\n\n - 修复 RTL 中坐标翻转的问题\n\n - 修复当前记录轨迹时遗漏传感器数据的问题\n\n - 为当前选中的兴趣点添加周边区域信息\n\n - 添加了控制 OSM 编辑可见性的快速操作\n\n - 拆分了地形着色选项的可见性参数\n\n - 添加了可点击的图标,以显示通道或其他道路或路径属性 + 燃料消耗率(传感器) + 显示根据计算得出的车辆燃料消耗率(升/小时) + 请将设备靠近扫描仪。并确保车辆发动机正在运转。 + 如何连接: + 输入油箱容量,准确跟踪油量和消耗情况 + 油箱容量 + 尺寸规格 + 公升 + 加仑 + 英制加仑 + 美制加仑 + 所选单位将应用于当前配置文件中所有与容积相关的数据。 + 容积单位 + 已断开 %s + 正在连接到 %s + 升/100 公里 + 显示根据计算得出的车辆燃料消耗率(百分比%/小时) + 显示根据计算得出的车辆燃料消耗率(升/100 公里) + 模拟 OBD 数据 + 燃料消耗率 + 路线指引 + 车辆速度 \ No newline at end of file diff --git a/OsmAnd/res/values-zh-rTW/phrases.xml b/OsmAnd/res/values-zh-rTW/phrases.xml index 38881b1b4e4..ac13d14599f 100644 --- a/OsmAnd/res/values-zh-rTW/phrases.xml +++ b/OsmAnd/res/values-zh-rTW/phrases.xml @@ -4881,20 +4881,9 @@ 隧道禁止行人通行 隧道禁止腳踏車騎士通行 其他路線 - 其他路線點 - 腳踏車 - 騎馬 - 冬季運動 - 雪地摩托車 - 直排輪鞋 - 山區騎乘 + 路線點 旅遊點 旅遊文章 - 健行 - 跑步 - 步行 - 水上運動 - 健身路徑 名稱詞源學 左側的名稱 右側的名稱 @@ -4904,4 +4893,74 @@ 露營地類型 基本 豪華 + 騎機車 + 其他路線 + 冬季運動 + 開車 + 空中運動 + 步行 + 騎腳踏車 + 水上運動 + ATV(全地形車輛) + 卡丁車 + 機車越野賽 + 耐力摩托車 + 越野摩托車 + 公路摩托車 + 旅行摩托車 + 場地賽車 + 小型摩托車 + 健行 + 爬山 + 徒步旅行 + 步行 + 越野滑雪 + 溜冰 + 滑雪旅遊 + 滑雪 + 單板滑雪 + 雪鞋 + 電動腳踏車 + 電動山地腳踏車 + 旅行 + 航空 + 熱氣球 + 跳傘 + 動力飛行傘 + 獨木舟 + 潛水 + 皮艇 + 戶外游泳 + 風帆衝浪 + 快艇 + 泛舟 + 騎馬 + 收成 + 洞穴探險 + 小火車 + 打高爾夫球 + 健身 + 直排輪鞋 + 路跑 + 背包旅行 + 雪地摩托車 + 採蘑菇 + 爬樹 + 礫石腳踏車 + 汽車 + 越野 + 卡車 (HGV) + 運動摩托車 + 冒險摩托車 + 山區騎乘 + 越野跑步 + 滑翔機 + 公路腳踏車 + 無人機飛行 + 滑翔翼 + 飛行傘 + 超輕型航空 + 水肺潛水 + 衝浪 + 滑水 \ No newline at end of file diff --git a/OsmAnd/res/values/colors.xml b/OsmAnd/res/values/colors.xml index 0c89f189ee2..147d26a17b7 100644 --- a/OsmAnd/res/values/colors.xml +++ b/OsmAnd/res/values/colors.xml @@ -219,6 +219,10 @@ #FD9822 + + #E71D36 + #E71D36 + #0080FF #dd6CB336 diff --git a/OsmAnd/res/values/phrases.xml b/OsmAnd/res/values/phrases.xml index a2157f05ebb..7cdebf8386c 100644 --- a/OsmAnd/res/values/phrases.xml +++ b/OsmAnd/res/values/phrases.xml @@ -5454,19 +5454,78 @@ Travel Articles Travel Points - Other routes - Other routes points - Bicycle - Hiking - Running - Walking - Water sports - Horse riding - Winter sports - Snowmobile - Fitness trails - Inline skates - Mountain biking + Route points + Driving + Motorcycling + Foot + Winter sports + Cycling + Air Sports + Water sports + Other routes + + Car + Off-Road + ATV (All-Terrain Vehicle) + Truck (HGV) + Karting + Adventure Motorcycling + Enduro Motorcycling + Motocross + Off-Road Motorcycling (Dirt Biking) + Road Motorcycling + Sport Motorcycling + Touring Motorcycling + Track Racing + Motor scooter + Backpacking + Hiking + Hill climbing + Road Running + Trail Running + Trekking + Walking + Cross-Country Skiing + Ice Skating + Ski Touring + Skiing + Snowboarding + Snowmobiling + Snowshoeing + E-Biking + E-mountain bike + Gravel Biking + Mountain biking + Road cycling + Touring + Aviation + Drone Flying + Gliding (Sailplane) + Hang gliding + Hot Air Ballooning + Parachuting (Skydiving) + Paragliding + Powered Paragliding (Paramotoring) + Ultralight Aviation + Canoe + Diving + Scuba diving + Kayak + Surfing + Swimming outdoor + Waterskiing + Windsurfing + Motorboat + Rafting + Horse riding + Mushrooms picking + Harvesting + Tree Climbing + Caving + Train Riding + Golfing + Fitness + Inline skates Name etymology Name of the left side diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java index 04717ce30a7..69089483c45 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java @@ -1373,6 +1373,10 @@ public boolean dispatchTouchEvent(MotionEvent event) { return lockHelper.getLockGestureDetector(this).onTouchEvent(event); } + if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) { + lockHelper.resetLockTimerIfNeeded(); + } + if (settings.DO_NOT_USE_ANIMATIONS.get()) { if (event.getAction() == MotionEvent.ACTION_DOWN) { if (drawerLayout.isDrawerOpen(GravityCompat.START)) { diff --git a/OsmAnd/src/net/osmand/plus/auto/screens/BaseAndroidAutoScreen.kt b/OsmAnd/src/net/osmand/plus/auto/screens/BaseAndroidAutoScreen.kt index 15c76f1307f..b2d77b1ad87 100644 --- a/OsmAnd/src/net/osmand/plus/auto/screens/BaseAndroidAutoScreen.kt +++ b/OsmAnd/src/net/osmand/plus/auto/screens/BaseAndroidAutoScreen.kt @@ -12,7 +12,6 @@ import net.osmand.data.LatLon import net.osmand.data.QuadRect import net.osmand.plus.OsmandApplication import net.osmand.plus.R -import net.osmand.plus.auto.views.CarSurfaceView import net.osmand.plus.views.Zoom import net.osmand.search.core.SearchResult import net.osmand.util.Algorithms @@ -20,11 +19,13 @@ import net.osmand.util.Algorithms abstract class BaseAndroidAutoScreen(carContext: CarContext) : Screen(carContext), DefaultLifecycleObserver { - protected var prevElevationAngle = 90f - protected var prevRotationAngle = 0f - protected var prevZoom: Zoom? = null - protected var prevMapLinkedToLocation = false - protected val ANIMATION_RETURN_FROM_PREVIEW_TIME = 1500 + private val ANIMATION_RETURN_FROM_PREVIEW_TIME = 1500 + + private var prevElevationAngle = 90f + private var prevRotationAngle = 0f + private var prevZoom: Zoom? = null + private var prevMapLinkedToLocation = false + private var prevStateSaved = false protected val app: OsmandApplication get() { @@ -47,6 +48,8 @@ abstract class BaseAndroidAutoScreen(carContext: CarContext) : Screen(carContext ) } + protected open fun shouldRestoreMapState(): Boolean = false + protected open fun getConstraintLimitType(): Int { return ConstraintManager.CONTENT_LIMIT_TYPE_LIST } @@ -59,6 +62,7 @@ abstract class BaseAndroidAutoScreen(carContext: CarContext) : Screen(carContext RoutePreviewScreen(carContext, settingsAction, result, true) ) { obj: Any? -> obj?.let { + screenManager.popToRoot() startNavigation() finish() } @@ -111,7 +115,7 @@ abstract class BaseAndroidAutoScreen(carContext: CarContext) : Screen(carContext val leftPanel = tb.pixWidth / 2; // assume panel takes half screen val tileBoxWidthPx = tb.pixWidth - leftPanel; mapView.fitRectToMap(tb, mapRect.left, mapRect.right, mapRect.top, mapRect.bottom, - tileBoxWidthPx, 0, 0, 0, rtl, 0.85f,true) + tileBoxWidthPx, 0, 0, 0, rtl, 0.75f,true) mapView.refreshMap() } } @@ -121,14 +125,37 @@ abstract class BaseAndroidAutoScreen(carContext: CarContext) : Screen(carContext session?.navigationCarSurface?.handleRecenter() } - override fun onStop(owner: LifecycleOwner) { + override fun onCreate(owner: LifecycleOwner) { + if (shouldSaveMapState()) { + saveMapState() + } + } + + override fun onDestroy(owner: LifecycleOwner) { if (prevMapLinkedToLocation != app.mapViewTrackingUtilities.isMapLinkedToLocation) { app.mapViewTrackingUtilities.isMapLinkedToLocation = prevMapLinkedToLocation } - restoreMapState() + if (prevStateSaved) { + restoreMapState() + } + } + + private fun shouldSaveMapState(): Boolean { + return shouldRestoreMapState() && screenManager.screenStack + .filterIsInstance() + .none { it != this && it.shouldRestoreMapState() } + } + + private fun saveMapState() { + val mapView = app.osmandMap.mapView + prevMapLinkedToLocation = app.mapViewTrackingUtilities.isMapLinkedToLocation + prevZoom = mapView.currentZoom + prevRotationAngle = mapView.rotate + prevElevationAngle = mapView.normalizeElevationAngle(mapView.elevationAngle) + prevStateSaved = true } - protected open fun restoreMapState() { + private fun restoreMapState() { val mapView = app.osmandMap.mapView val locationProvider = app.locationProvider val lastKnownLocation = locationProvider.lastKnownLocation @@ -142,14 +169,6 @@ abstract class BaseAndroidAutoScreen(carContext: CarContext) : Screen(carContext false) } - override fun onStart(owner: LifecycleOwner) { - val mapView = app.osmandMap.mapView - prevMapLinkedToLocation = app.mapViewTrackingUtilities.isMapLinkedToLocation - prevZoom = mapView.currentZoom - prevRotationAngle = mapView.rotate - prevElevationAngle = mapView.normalizeElevationAngle(mapView.elevationAngle) - } - companion object { private const val DEFAULT_CONTENT_LIMIT = 12 } diff --git a/OsmAnd/src/net/osmand/plus/auto/screens/FavoritesScreen.java b/OsmAnd/src/net/osmand/plus/auto/screens/FavoritesScreen.java index 4f8dc246ed3..e709b889b60 100644 --- a/OsmAnd/src/net/osmand/plus/auto/screens/FavoritesScreen.java +++ b/OsmAnd/src/net/osmand/plus/auto/screens/FavoritesScreen.java @@ -65,6 +65,11 @@ public FavoritesScreen( getLifecycle().addObserver(this); } + @Override + protected boolean shouldRestoreMapState() { + return true; + } + @Override public void onDestroy(@NonNull LifecycleOwner owner) { super.onDestroy(owner); @@ -73,8 +78,8 @@ public void onDestroy(@NonNull LifecycleOwner owner) { } @Override - public void onStart(@NonNull LifecycleOwner owner) { - super.onStart(owner); + public void onCreate(@NonNull LifecycleOwner owner) { + super.onCreate(owner); getFavouritesLayer().customObjectsDelegate = new OsmandMapLayer.CustomMapObjects<>(); } diff --git a/OsmAnd/src/net/osmand/plus/auto/screens/MapMarkersScreen.kt b/OsmAnd/src/net/osmand/plus/auto/screens/MapMarkersScreen.kt index a84b953010b..3b7f1588da9 100644 --- a/OsmAnd/src/net/osmand/plus/auto/screens/MapMarkersScreen.kt +++ b/OsmAnd/src/net/osmand/plus/auto/screens/MapMarkersScreen.kt @@ -38,6 +38,8 @@ class MapMarkersScreen( lifecycle.addObserver(this) } + override fun shouldRestoreMapState() = true + override fun onGetTemplate(): Template { val listBuilder = ItemList.Builder() val markersSize = app.mapMarkersHelper.mapMarkers.size @@ -105,8 +107,8 @@ class MapMarkersScreen( app.osmandMap.mapLayers.mapMarkersLayer.customObjectsDelegate = null } - override fun onStart(owner: LifecycleOwner) { - super.onStart(owner) + override fun onCreate(owner: LifecycleOwner) { + super.onCreate(owner) app.osmandMap.mapLayers.mapMarkersLayer.customObjectsDelegate = CustomMapObjects() } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/auto/screens/NavigationScreen.java b/OsmAnd/src/net/osmand/plus/auto/screens/NavigationScreen.java index bfd4e4ec068..decbb8f6e7d 100644 --- a/OsmAnd/src/net/osmand/plus/auto/screens/NavigationScreen.java +++ b/OsmAnd/src/net/osmand/plus/auto/screens/NavigationScreen.java @@ -4,7 +4,6 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; -import android.util.Log; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; @@ -130,6 +129,7 @@ public void onPause(@NonNull LifecycleOwner owner) { @Override public void onDestroy(@NonNull LifecycleOwner owner) { + super.onDestroy(owner); adjustMapPosition(false); getApp().getRoutingHelper().removeListener(this); getLifecycle().removeObserver(this); @@ -399,11 +399,6 @@ public Template onGetTemplate() { return builder.build(); } - @Override - protected void restoreMapState() { - //no automatic map adjust - } - private void updateCompass() { OsmandSettings settings = getApp().getSettings(); boolean nightMode = getCarContext().isDarkMode(); diff --git a/OsmAnd/src/net/osmand/plus/auto/screens/POIScreen.kt b/OsmAnd/src/net/osmand/plus/auto/screens/POIScreen.kt index 1c5433aba53..9e22101f9a9 100644 --- a/OsmAnd/src/net/osmand/plus/auto/screens/POIScreen.kt +++ b/OsmAnd/src/net/osmand/plus/auto/screens/POIScreen.kt @@ -49,6 +49,8 @@ class POIScreen( lifecycle.addObserver(this) } + override fun shouldRestoreMapState() = true + override fun onGetTemplate(): Template { val templateBuilder = PlaceListNavigationTemplate.Builder() if (loading) { @@ -178,10 +180,8 @@ class POIScreen( } } - override fun onStart(owner: LifecycleOwner) { - super.onStart(owner) - app.osmandMap.mapLayers.poiMapLayer.customObjectsDelegate = - OsmandMapLayer.CustomMapObjects() - + override fun onCreate(owner: LifecycleOwner) { + super.onCreate(owner) + app.osmandMap.mapLayers.poiMapLayer.customObjectsDelegate = OsmandMapLayer.CustomMapObjects() } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/auto/screens/RoutePreviewScreen.java b/OsmAnd/src/net/osmand/plus/auto/screens/RoutePreviewScreen.java index 6fa42dda2e9..dd740ef0162 100644 --- a/OsmAnd/src/net/osmand/plus/auto/screens/RoutePreviewScreen.java +++ b/OsmAnd/src/net/osmand/plus/auto/screens/RoutePreviewScreen.java @@ -2,8 +2,6 @@ import static net.osmand.search.core.ObjectType.GPX_TRACK; -import android.os.Handler; -import android.os.Looper; import android.text.SpannableString; import androidx.annotation.NonNull; @@ -23,11 +21,9 @@ import net.osmand.PlatformUtil; -import net.osmand.plus.auto.NavigationSession; import net.osmand.plus.auto.TripUtils; import net.osmand.plus.shared.SharedUtil; import net.osmand.StateChangedListener; -import net.osmand.data.LatLon; import net.osmand.data.QuadRect; import net.osmand.data.ValueHolder; import net.osmand.plus.OsmandApplication; @@ -36,12 +32,9 @@ import net.osmand.plus.routing.RoutingHelper; import net.osmand.plus.routing.RoutingHelperUtils; import net.osmand.plus.search.listitems.QuickSearchListItem; -import net.osmand.plus.settings.enums.CompassMode; -import net.osmand.plus.shared.SharedUtil; import net.osmand.plus.track.data.GPXInfo; import net.osmand.plus.track.helpers.GpxFileLoaderTask; import net.osmand.plus.track.helpers.SelectedGpxFile; -import net.osmand.plus.views.OsmandMapTileView; import net.osmand.search.core.SearchResult; import net.osmand.shared.gpx.GpxFile; import net.osmand.util.Algorithms; @@ -72,7 +65,7 @@ public final class RoutePreviewScreen extends BaseAndroidAutoScreen implements I private boolean calculateRoute; private boolean calculating; - private final StateChangedListener stateChangedListener = new StateChangedListener() { + private final StateChangedListener stateChangedListener = new StateChangedListener<>() { @Override public void stateChanged(Void change) { if (routeGpxFile != null) { @@ -83,7 +76,6 @@ public void stateChanged(Void change) { } }; - public RoutePreviewScreen(@NonNull CarContext carContext, @NonNull Action settingsAction, @NonNull SearchResult searchResult, boolean calculateRoute) { super(carContext); @@ -94,6 +86,11 @@ public RoutePreviewScreen(@NonNull CarContext carContext, @NonNull Action settin calculating = calculateRoute; } + @Override + protected boolean shouldRestoreMapState() { + return true; + } + private void prepareRoute() { if (searchResult.objectType == GPX_TRACK) { GPXInfo gpxInfo = ((GPXInfo) searchResult.relatedObject); @@ -163,6 +160,7 @@ public void onCreate(@NonNull LifecycleOwner owner) { @Override public void onDestroy(@NonNull LifecycleOwner owner) { + super.onDestroy(owner); OsmandApplication app = getApp(); RoutingHelper routingHelper = app.getRoutingHelper(); routingHelper.removeListener(this); diff --git a/OsmAnd/src/net/osmand/plus/auto/screens/SearchResultsScreen.java b/OsmAnd/src/net/osmand/plus/auto/screens/SearchResultsScreen.java index 6ff4ecc227c..c73dc82f460 100644 --- a/OsmAnd/src/net/osmand/plus/auto/screens/SearchResultsScreen.java +++ b/OsmAnd/src/net/osmand/plus/auto/screens/SearchResultsScreen.java @@ -28,7 +28,6 @@ public final class SearchResultsScreen extends BaseSearchScreen implements DefaultLifecycleObserver, AppInitializeListener { - @NonNull private final Action settingsAction; @NonNull @@ -77,6 +76,7 @@ public Template onGetTemplate() { @Override public void onDestroy(@NonNull LifecycleOwner owner) { + super.onDestroy(owner); getApp().getAppInitializer().removeListener(this); getLifecycle().removeObserver(this); destroyed = true; diff --git a/OsmAnd/src/net/osmand/plus/auto/screens/SearchScreen.java b/OsmAnd/src/net/osmand/plus/auto/screens/SearchScreen.java index 4f05399242f..927a555ae47 100644 --- a/OsmAnd/src/net/osmand/plus/auto/screens/SearchScreen.java +++ b/OsmAnd/src/net/osmand/plus/auto/screens/SearchScreen.java @@ -52,7 +52,6 @@ public final class SearchScreen extends BaseSearchScreen implements DefaultLifec private static final Log LOG = PlatformUtil.getLog(SearchScreen.class); private static final int MAP_MARKERS_LIMIT = 3; - @NonNull private final Action settingsAction; @@ -73,7 +72,6 @@ public SearchScreen(@NonNull CarContext carContext, @NonNull Action settingsActi reloadHistory(); } - @NonNull public SearchUICore getSearchUICore() { return getApp().getSearchUICore().getCore(); @@ -81,6 +79,7 @@ public SearchUICore getSearchUICore() { @Override public void onDestroy(@NonNull LifecycleOwner owner) { + super.onDestroy(owner); getApp().getAppInitializer().removeListener(this); getLifecycle().removeObserver(this); destroyed = true; diff --git a/OsmAnd/src/net/osmand/plus/auto/screens/TracksScreen.kt b/OsmAnd/src/net/osmand/plus/auto/screens/TracksScreen.kt index 3b189bb87eb..bacefd631fc 100644 --- a/OsmAnd/src/net/osmand/plus/auto/screens/TracksScreen.kt +++ b/OsmAnd/src/net/osmand/plus/auto/screens/TracksScreen.kt @@ -51,6 +51,8 @@ class TracksScreen( lifecycle.addObserver(this) } + override fun shouldRestoreMapState() = true + override fun onCreate(owner: LifecycleOwner) { super.onCreate(owner) loadTracksTask = LoadTracksTask() diff --git a/OsmAnd/src/net/osmand/plus/configmap/TravelRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/TravelRoutesFragment.java index 5e21d5813b3..5c73a9d727d 100644 --- a/OsmAnd/src/net/osmand/plus/configmap/TravelRoutesFragment.java +++ b/OsmAnd/src/net/osmand/plus/configmap/TravelRoutesFragment.java @@ -1,7 +1,7 @@ package net.osmand.plus.configmap; import static net.osmand.IProgress.EMPTY_PROGRESS; -import static net.osmand.plus.wikivoyage.data.TravelGpx.ACTIVITY_TYPE; +import static net.osmand.plus.wikivoyage.data.TravelGpx.ROUTE_ACTIVITY_TYPE; import android.os.Bundle; import android.view.Gravity; @@ -115,7 +115,7 @@ private void showHideTopShadow(@NonNull View view) { } private void updateRouteTypes() { - List routesTypes = app.getResourceManager().searchPoiSubTypesByPrefix(ACTIVITY_TYPE); + List routesTypes = app.getResourceManager().searchPoiSubTypesByPrefix(ROUTE_ACTIVITY_TYPE); Collections.sort(routesTypes, OsmAndCollator.primaryCollator()::compare); this.routeTypes = routesTypes; } @@ -297,7 +297,7 @@ private void setupRouteTypes(ViewGroup container) { updateItemView(tracksView, getString(R.string.display_route_tracks), R.drawable.ic_action_track_16, selected, DescriptionType.VISIBLE_HIDDEN); app.runInUIThread(() -> { - rendererHelper.updateRouteTrackFilter(); + rendererHelper.updateRouteTrackFilters(); rendererHelper.updateRouteTypesVisibility(); app.getOsmandMap().refreshMap(true); app.getOsmandMap().getMapLayers().updateLayers((MapActivity) getMyActivity()); @@ -316,7 +316,7 @@ private void setupRouteTypes(ViewGroup container) { updateItemView(tracksAsPoiView, getString(R.string.display_route_tracks_as_poi), R.drawable.ic_action_info_dark, selected, DescriptionType.VISIBLE_HIDDEN); app.runInUIThread(() -> { - rendererHelper.updateRouteTrackFilter(); + rendererHelper.updateRouteTrackFilters(); rendererHelper.updateRouteTypesVisibility(); app.getOsmandMap().refreshMap(true); app.getOsmandMap().getMapLayers().updateLayers((MapActivity) getMyActivity()); @@ -331,7 +331,7 @@ private void setupRouteTypes(ViewGroup container) { View itemView = inflater.inflate(R.layout.list_item_icon_and_menu, container, false); AndroidUtils.setBackground(itemView, UiUtilities.getSelectableDrawable(app)); String name; - String attrName = type.replace(ACTIVITY_TYPE + "_", ""); + String attrName = type.replace(ROUTE_ACTIVITY_TYPE + "_", ""); PoiType poiType = poiTypes.getTextPoiAdditionalByKey(type); if (poiType != null) { name = poiType.getTranslation(); diff --git a/OsmAnd/src/net/osmand/plus/helpers/LockHelper.java b/OsmAnd/src/net/osmand/plus/helpers/LockHelper.java index 51c97ff70e1..28734b7113d 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/LockHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/LockHelper.java @@ -8,6 +8,7 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Handler; +import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.provider.Settings; @@ -18,6 +19,7 @@ import net.osmand.PlatformUtil; import net.osmand.StateChangedListener; +import net.osmand.plus.OsmAndConstants; import net.osmand.plus.OsmandApplication; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.quickaction.QuickAction; @@ -36,6 +38,7 @@ public class LockHelper implements SensorEventListener, StateChangedListener { private static final Log LOG = PlatformUtil.getLog(LockHelper.class); + private static final int LOCK_SCREEN_MESSAGE = OsmAndConstants.UI_HANDLER_MAP_VIEW + 8; private static final int SENSOR_SENSITIVITY = 4; @@ -78,24 +81,20 @@ public LockHelper(OsmandApplication app) { settings.APPLICATION_MODE.addListener(this); lockRunnable = this::lock; - voiceMessageListener = new VoiceMessageListener() { - @Override - public void onVoiceMessage(List listCommands, List played) { - if (turnScreenOnNavigationInstructions.get()) { - unlockEvent(); - } + + voiceMessageListener = (listCommands, played) -> { + if (turnScreenOnNavigationInstructions.get()) { + unlockEvent(); } }; - OsmAndAppCustomizationListener customizationListener = new OsmAndAppCustomizationListener() { - @Override - public void onOsmAndSettingsCustomized() { - OsmandSettings settings = app.getSettings(); - turnScreenOnTime = settings.TURN_SCREEN_ON_TIME_INT; - turnScreenOnSensor = settings.TURN_SCREEN_ON_SENSOR; - useSystemScreenTimeout = settings.USE_SYSTEM_SCREEN_TIMEOUT; - turnScreenOnPowerButton = settings.TURN_SCREEN_ON_POWER_BUTTON; - turnScreenOnNavigationInstructions = settings.TURN_SCREEN_ON_NAVIGATION_INSTRUCTIONS; - } + + OsmAndAppCustomizationListener customizationListener = () -> { + OsmandSettings osmSettings = app.getSettings(); + turnScreenOnTime = osmSettings.TURN_SCREEN_ON_TIME_INT; + turnScreenOnSensor = osmSettings.TURN_SCREEN_ON_SENSOR; + useSystemScreenTimeout = osmSettings.USE_SYSTEM_SCREEN_TIMEOUT; + turnScreenOnPowerButton = osmSettings.TURN_SCREEN_ON_POWER_BUTTON; + turnScreenOnNavigationInstructions = osmSettings.TURN_SCREEN_ON_NAVIGATION_INSTRUCTIONS; }; app.getAppCustomization().addListener(customizationListener); app.getRoutingHelper().getVoiceRouter().addVoiceMessageListener(voiceMessageListener); @@ -145,7 +144,7 @@ private void timedUnlock(long millis) { }); } if (millis > 0) { - uiHandler.postDelayed(lockRunnable, millis); + sendPostDelayedLockMessage(millis); } } @@ -158,6 +157,22 @@ private void unlockEvent() { } } + public void resetLockTimerIfNeeded() { + if (uiHandler.hasMessages(LOCK_SCREEN_MESSAGE)) { + uiHandler.removeCallbacks(lockRunnable); + int unlockTime = getUnlockTime(); + if (unlockTime > 0) { + sendPostDelayedLockMessage(unlockTime * 1000L); + } + } + } + + private void sendPostDelayedLockMessage(long delayMillis){ + Message message = Message.obtain(uiHandler, lockRunnable); + message.what = LOCK_SCREEN_MESSAGE; + uiHandler.sendMessageDelayed(message, delayMillis); + } + private int getUnlockTime() { int unlockTime = turnScreenOnTime.get(); if (useSystemScreenTimeout.get()) { @@ -218,7 +233,6 @@ public void setLockUIAdapter(@Nullable LockUIAdapter adapter) { * LockScreenAction part */ - public void toggleLockScreen() { lockScreen = !lockScreen; } diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenuFragment.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenuFragment.java index 736ab67f504..5003794bd40 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenuFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenuFragment.java @@ -192,7 +192,7 @@ public void handleOnBackPressed() { DialogManager dialogManager = mapActivity.getMyApplication().getDialogManager(); GalleryController controller = (GalleryController) dialogManager.findController(GalleryController.PROCESS_ID); if (controller == null) { - dialogManager.register(GalleryController.PROCESS_ID, new GalleryController(mapActivity.getMyApplication())); + dialogManager.register(GalleryController.PROCESS_ID, new GalleryController(app)); } } diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/MergeLocalizedTagsAlgorithm.kt b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/MergeLocalizedTagsAlgorithm.kt index 8863503dfd8..a8bc9c1218a 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/MergeLocalizedTagsAlgorithm.kt +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/MergeLocalizedTagsAlgorithm.kt @@ -3,6 +3,7 @@ package net.osmand.plus.mapcontextmenu.builders import net.osmand.osm.AbstractPoiType import net.osmand.osm.MapPoiTypes import net.osmand.plus.OsmandApplication +import net.osmand.shared.gpx.GpxFile.Companion.XML_COLON private val NAME_TAG_PREFIXES = listOf( "name", "int_name", "nat_name", "reg_name", "loc_name", @@ -135,6 +136,6 @@ class MergeLocalizedTagsAlgorithm private constructor(private val app: OsmandApp } private fun convertKey(key: String): String { - return key.replace("_-_", ":") + return key.replace(XML_COLON, ":") } } diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/AmenityMenuController.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/AmenityMenuController.java index 6790a7068b3..0ee897ea1be 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/AmenityMenuController.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/AmenityMenuController.java @@ -1,12 +1,14 @@ package net.osmand.plus.mapcontextmenu.controllers; import static net.osmand.osm.MapPoiTypes.ROUTE_ARTICLE_POINT; +import static net.osmand.osm.MapPoiTypes.ROUTE_TRACK_POINT; import android.graphics.drawable.Drawable; import android.text.TextUtils; import androidx.annotation.NonNull; +import net.osmand.PlatformUtil; import net.osmand.data.Amenity; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; @@ -25,13 +27,18 @@ import net.osmand.plus.transport.TransportStopRoute; import net.osmand.plus.wikipedia.WikipediaDialogFragment; import net.osmand.plus.wikivoyage.data.TravelArticle; +import net.osmand.plus.wikivoyage.data.TravelGpx; import net.osmand.plus.wikivoyage.data.TravelHelper; +import net.osmand.plus.wikivoyage.data.TravelObfHelper; import net.osmand.util.Algorithms; import net.osmand.util.OpeningHoursParser; +import org.apache.commons.logging.Log; + import java.util.List; public class AmenityMenuController extends MenuController { + private static final Log LOG = PlatformUtil.getLog(AmenityMenuController.class); private Amenity amenity; private final MapMarker marker; @@ -69,7 +76,7 @@ public AmenityMenuController(@NonNull MapActivity mapActivity, new MapMarkerMenuController(mapActivity, marker.getPointDescription(mapActivity), marker); leftTitleButtonController = markerMenuController.getLeftTitleButtonController(); rightTitleButtonController = markerMenuController.getRightTitleButtonController(); - } else if (amenity.getSubType().equals(ROUTE_ARTICLE_POINT)) { + } else if (amenity.isRoutePoint()) { TitleButtonController openTrackButtonController = new TitleButtonController() { @Override public void buttonPressed() { @@ -101,11 +108,20 @@ public void buttonPressed() { void openTrack(MapActivity mapActivity) { TravelHelper travelHelper = mapActivity.getMyApplication().getTravelHelper(); - String lang = amenity.getTagSuffix(Amenity.LANG_YES + ":"); - String name = amenity.getTagContent(Amenity.ROUTE_NAME); - TravelArticle article = travelHelper.getArticleByTitle(name, lang, true, null); - if (article != null) { - travelHelper.openTrackMenu(article, mapActivity, name, amenity.getLocation()); + if (ROUTE_ARTICLE_POINT.equals(amenity.getSubType())) { + String lang = amenity.getTagSuffix(Amenity.LANG_YES + ":"); + String name = amenity.getTagContent(Amenity.ROUTE_NAME); + TravelArticle article = travelHelper.getArticleByTitle(name, lang, true, null); + if (article != null) { + travelHelper.openTrackMenu(article, mapActivity, name, amenity.getLocation(), false); + } + } else if (ROUTE_TRACK_POINT.equals(amenity.getSubType())) { + TravelGpx travelGpx = travelHelper.searchGpx(amenity.getLocation(), amenity.getRouteId(), amenity.getRef()); + if (travelGpx != null) { + travelHelper.openTrackMenu(travelGpx, mapActivity, travelGpx.getTitle(), amenity.getLocation(), false); + } else { + LOG.error("openTrack() searchGpx() travelGpx is null"); + } } } diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryController.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryController.java index a87b92ba1d2..5bcaa18e89d 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryController.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryController.java @@ -11,26 +11,27 @@ import net.osmand.plus.base.dialog.interfaces.controller.IDialogController; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.mapcontextmenu.builders.cards.ImageCard; -import net.osmand.plus.mapcontextmenu.gallery.tasks.LoadImageMetadataTask; +import net.osmand.plus.mapcontextmenu.gallery.tasks.LoadImagesMetadataTask; import net.osmand.plus.wikipedia.WikiImageCard; import net.osmand.util.Algorithms; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; public class GalleryController implements IDialogController { public static final String PROCESS_ID = "gallery_context_controller"; - private final OsmandApplication app; private ImageCardsHolder currentCardsHolder; private final List> listeners = new LinkedList<>(); - + private final Set downloadingMetadata = new HashSet<>(); public GalleryController(@NonNull OsmandApplication app) { this.app = app; @@ -50,36 +51,53 @@ public ImageCardsHolder getCurrentCardsHolder() { return currentCardsHolder; } + public void updateMetadata(@Nullable Map> metadataMap, @NonNull Set cards) { + for (WikiImageCard card : cards) { + downloadingMetadata.remove(card); + } + if (currentCardsHolder != null && metadataMap != null) { + currentCardsHolder.updateWikiMetadata(metadataMap); + notifyMetaDataUpdated(metadataMap.keySet()); + } + } - public void addMetaDataListener(@NonNull DownloadMetadataListener listener) { - if (!listeners.contains(new WeakReference<>(listener))) { - listeners.add(new WeakReference<>(listener)); + public boolean isMetadataDownloading(@NonNull WikiImageCard card){ + return downloadingMetadata.contains(card); + } + + public void downloadWikiMetaData(@NonNull Set cards) { + if (Algorithms.isEmpty(cards)) { + return; } + downloadingMetadata.addAll(cards); + LoadImagesMetadataTask task = new LoadImagesMetadataTask(app, cards); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } - public void removeMetaDataListener(@NonNull DownloadMetadataListener listener) { + public void notifyMetaDataUpdated(@NonNull Set updatedMediaTagImages) { Iterator> it = listeners.iterator(); while (it.hasNext()) { - DownloadMetadataListener metadataListener = it.next().get(); - if (metadataListener == listener) { + DownloadMetadataListener listener = it.next().get(); + if (listener == null) { it.remove(); + } else { + listener.onMetadataUpdated(updatedMediaTagImages); } } } - public void downloadWikiMetaData(@NonNull WikiImageCard card) { - LoadImageMetadataTask task = new LoadImageMetadataTask(app, card, this::notifyMetaDataDownloaded); - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + public void addMetaDataListener(@NonNull DownloadMetadataListener listener) { + if (!listeners.contains(new WeakReference<>(listener))) { + listeners.add(new WeakReference<>(listener)); + } } - private void notifyMetaDataDownloaded(@NonNull WikiImageCard wikiImageCard) { + public void removeMetaDataListener(@NonNull DownloadMetadataListener listener) { Iterator> it = listeners.iterator(); while (it.hasNext()) { - DownloadMetadataListener listener = it.next().get(); - if (listener == null) { + DownloadMetadataListener metadataListener = it.next().get(); + if (metadataListener == listener) { it.remove(); - } else { - listener.onMetadataDownloaded(wikiImageCard); } } } @@ -130,6 +148,6 @@ public void clearListeners() { } public interface DownloadMetadataListener { - void onMetadataDownloaded(@NonNull WikiImageCard imageCard); + void onMetadataUpdated(@NonNull Set updatedMediaTagImages); } } diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryDetailsFragment.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryDetailsFragment.java index 169a28d1396..b673744da19 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryDetailsFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryDetailsFragment.java @@ -32,7 +32,7 @@ import net.osmand.util.Algorithms; import net.osmand.wiki.Metadata; -import org.jetbrains.annotations.NotNull; +import java.util.Set; public class GalleryDetailsFragment extends BaseOsmAndFragment implements DownloadMetadataListener { @@ -56,11 +56,7 @@ public boolean getContentStatusBarNightMode() { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - controller = (GalleryController) app.getDialogManager().findController(GalleryController.PROCESS_ID); - if (controller != null) { - controller.addMetaDataListener(this); - } Bundle args = getArguments(); if (savedInstanceState != null && savedInstanceState.containsKey(SELECTED_POSITION_KEY)) { @@ -84,6 +80,14 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c return view; } + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + if (controller != null) { + controller.addMetaDataListener(this); + } + } + private void setupToolbar(@NonNull View view) { Toolbar toolbar = view.findViewById(R.id.toolbar); TextView title = toolbar.findViewById(R.id.toolbar_title); @@ -181,9 +185,10 @@ private void buildItem(@NonNull ViewGroup container, @NonNull String title, @Non } @Override - public void onMetadataDownloaded(@NonNull @NotNull WikiImageCard imageCard) { + public void onMetadataUpdated(@NonNull Set updatedMediaTagImages) { + ImageCard imageCard = getSelectedCard(); View view = getView(); - if (view != null && Algorithms.stringsEqual(imageCard.getImageUrl(), getSelectedCard().getImageUrl())) { + if (view != null && imageCard instanceof WikiImageCard wikiImageCard && updatedMediaTagImages.contains(wikiImageCard.getWikiImage().getWikiMediaTag())) { updateContent(view); } } diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryPhotoPagerFragment.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryPhotoPagerFragment.java index 44974d27ef9..07827c437f0 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryPhotoPagerFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryPhotoPagerFragment.java @@ -50,7 +50,9 @@ import net.osmand.wiki.Metadata; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class GalleryPhotoPagerFragment extends BaseOsmAndFragment implements DownloadMetadataListener { @@ -79,9 +81,10 @@ public void onCreate(@Nullable Bundle savedInstanceState) { } @Override - public void onMetadataDownloaded(@NonNull WikiImageCard imageCard) { - if (imageCard.getImageUrl().equals(getSelectedImageCard().getImageUrl())) { - Metadata metadata = imageCard.getWikiImage().getMetadata(); + public void onMetadataUpdated(@NonNull Set updatedMediaTagImages) { + ImageCard card = getSelectedImageCard(); + if (card instanceof WikiImageCard wikiImageCard && updatedMediaTagImages.contains(wikiImageCard.getWikiImage().getWikiMediaTag())) { + Metadata metadata = wikiImageCard.getWikiImage().getMetadata(); setMetaData(metadata.getAuthor(), metadata.getDate(), metadata.getLicense()); } } @@ -125,7 +128,7 @@ public void onPageSelected(int position) { boolean shouldPreloadNext = selectedPosition < position; selectedPosition = position; preloadThumbNails(shouldPreloadNext); - updateImageDescriptionRow(getSelectedImageCard()); + updateImageDescriptionRow(getSelectedImageCard(), shouldPreloadNext); } @Override @@ -138,10 +141,18 @@ public void onPageScrollStateChanged(int state) { setupToolbar(view); setupOnBackPressedCallback(); preloadThumbNails(); - updateImageDescriptionRow(getSelectedImageCard()); + updateImageDescriptionRow(getSelectedImageCard(), null); return view; } + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + if (controller != null) { + controller.addMetaDataListener(this); + } + } + private void preloadThumbNails() { preloadThumbNails(true); preloadThumbNails(false); @@ -188,23 +199,60 @@ public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); } - private void updateImageDescriptionRow(@NonNull ImageCard imageCard) { + private Set getImagesToDownloadMetadata(@NonNull WikiImageCard wikiImageCard, @Nullable Boolean preloadNext){ + Set result = new HashSet<>(); + List imageCards = controller.getOnlinePhotoCards(); + if (imageCards.size() <= 1) { + return result; + } + + if (shouldDownloadMetadata(wikiImageCard)) { + result.add(wikiImageCard); + } + if (preloadNext == null) { + addImages(imageCards, result, false, 2); + addImages(imageCards, result, true, 2); + } else { + addImages(imageCards, result, preloadNext, 4); + } + + return result; + } + + private void addImages(@NonNull List imageList, @NonNull Set result, boolean next, int downloadCount) { + int direction = next ? 1 : -1; + for (int i = 1; i <= downloadCount; i++) { + int currentIndex = selectedPosition + (i * direction); + if (currentIndex >= 0 && currentIndex < imageList.size()) { + ImageCard card = imageList.get(currentIndex); + if (card instanceof WikiImageCard wikiImageCard && shouldDownloadMetadata(wikiImageCard)) { + result.add(wikiImageCard); + } + } + } + } + + private boolean shouldDownloadMetadata(@NonNull WikiImageCard wikiImageCard){ + Metadata metadata = wikiImageCard.getWikiImage().getMetadata(); + String date = metadata.getDate(); + String author = metadata.getAuthor(); + String license = metadata.getLicense(); + return !wikiImageCard.isMetaDataDownloaded() && !controller.isMetadataDownloading(wikiImageCard) + && (Algorithms.isEmpty(date) || date.equals("Unknown") + || Algorithms.isEmpty(author) || author.equals("Unknown") + || Algorithms.isEmpty(license) || license.equals("Unknown")); + } + + private void updateImageDescriptionRow(@NonNull ImageCard imageCard, @Nullable Boolean preloadNext) { if (imageCard instanceof WikiImageCard wikiImageCard) { dateView.setVisibility(View.VISIBLE); authorView.setVisibility(View.VISIBLE); licenseView.setVisibility(View.VISIBLE); + controller.addMetaDataListener(this); + controller.downloadWikiMetaData(getImagesToDownloadMetadata(wikiImageCard, preloadNext)); + Metadata metadata = wikiImageCard.getWikiImage().getMetadata(); - String date = metadata.getDate(); - String author = metadata.getAuthor(); - String license = metadata.getLicense(); - if (!wikiImageCard.isMetaDataDownloaded() && (Algorithms.isEmpty(date) || date.equals("Unknown") - || Algorithms.isEmpty(author) || author.equals("Unknown") - || Algorithms.isEmpty(license) || license.equals("Unknown"))) { - controller.addMetaDataListener(this); - controller.downloadWikiMetaData(wikiImageCard); - } else { - setMetaData(metadata.getAuthor(), metadata.getDate(), metadata.getLicense()); - } + setMetaData(metadata.getAuthor(), metadata.getDate(), metadata.getLicense()); } else { dateView.setVisibility(View.INVISIBLE); authorView.setVisibility(View.INVISIBLE); diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/ImageCardsHolder.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/ImageCardsHolder.java index 063dd0db1b5..a8b759bbb40 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/ImageCardsHolder.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/ImageCardsHolder.java @@ -10,7 +10,9 @@ import net.osmand.data.LatLon; import net.osmand.plus.mapcontextmenu.builders.cards.ImageCard; +import net.osmand.plus.wikipedia.WikiImageCard; import net.osmand.util.Algorithms; +import net.osmand.wiki.Metadata; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -21,7 +23,7 @@ public class ImageCardsHolder { private final LatLon latLon; private final Map params; - private final Map> cardsByType = new LinkedHashMap<>(); + private final Map> cardsByType = new LinkedHashMap<>(); public ImageCardsHolder(@NonNull LatLon latLon, @NonNull Map params) { this.latLon = latLon; @@ -52,22 +54,62 @@ public List getMapillaryCards() { private List getCardsWithTypes(@NonNull ImageCardType... types) { List list = new ArrayList<>(); for (ImageCardType type : types) { - List cards = cardsByType.get(type); - if (!Algorithms.isEmpty(cards)) { - list.addAll(cards); + LinkedHashMap images = cardsByType.get(type); + if (images != null) { + List cards = new ArrayList<>(images.values()); + if (!Algorithms.isEmpty(cards)) { + list.addAll(cards); + } } } return list; } public void addCard(@NonNull ImageCardType type, @NonNull ImageCard card) { - List cards = cardsByType.get(type); + LinkedHashMap cards = cardsByType.get(type); + String key; + if (card instanceof WikiImageCard wikiImageCard) { + key = wikiImageCard.getWikiImage().getWikiMediaTag(); + } else { + key = card.getImageUrl(); + } if (cards != null) { - cards.add(card); + cards.put(key, card); } else { - cards = new ArrayList<>(); - cards.add(card); + cards = new LinkedHashMap<>(); + cards.put(key, card); cardsByType.put(type, cards); } } + + public void updateWikiMetadata(@NonNull Map> metadataMap) { + LinkedHashMap wikiImages = cardsByType.get(WIKIMEDIA); + if (wikiImages == null) { + return; + } + for (String key : metadataMap.keySet()) { + ImageCard card = wikiImages.get(key); + Map details = metadataMap.get(key); + if (card instanceof WikiImageCard wikiImageCard && details != null) { + Metadata metadata = wikiImageCard.getWikiImage().getMetadata(); + String date = details.get("date"); + if (!Algorithms.isEmpty(date) && (Algorithms.isEmpty(metadata.getDate()) || metadata.getDate().equals("Unknown"))) { + metadata.setDate(date); + } + String license = details.get("license"); + if (!Algorithms.isEmpty(license) && (Algorithms.isEmpty(metadata.getLicense()) || metadata.getLicense().equals("Unknown"))) { + metadata.setLicense(license); + } + String author = details.get("author"); + if (!Algorithms.isEmpty(author) && (Algorithms.isEmpty(metadata.getAuthor()) || metadata.getAuthor().equals("Unknown"))) { + metadata.setAuthor(author); + } + String description = details.get("description"); + if (!Algorithms.isEmpty(description) && (Algorithms.isEmpty(metadata.getDescription()) || metadata.getDescription().equals("Unknown"))) { + metadata.setDescription(description); + } + wikiImageCard.setMetaDataDownloaded(true); + } + } + } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/tasks/LoadImageMetadataTask.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/tasks/LoadImageMetadataTask.java deleted file mode 100644 index 33da37edd8f..00000000000 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/tasks/LoadImageMetadataTask.java +++ /dev/null @@ -1,95 +0,0 @@ -package net.osmand.plus.mapcontextmenu.gallery.tasks; - -import android.os.AsyncTask; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import net.osmand.PlatformUtil; -import net.osmand.osm.io.NetworkUtils; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.utils.AndroidNetworkUtils; -import net.osmand.plus.wikipedia.WikiImageCard; -import net.osmand.util.Algorithms; -import net.osmand.wiki.Metadata; - -import org.apache.commons.logging.Log; -import org.json.JSONException; -import org.json.JSONObject; - -public class LoadImageMetadataTask extends AsyncTask { - - private static final Log LOG = PlatformUtil.getLog(LoadImageMetadataTask.class); - - private static final String WIKI_MEDIA_ACTION_RAW = "?action=raw"; - private static final String WIKI_MEDIA_BASE_URL = "https://commons.wikimedia.org/wiki/File:"; - private static final String OSMAND_PARSE_URL = "https://osmand.net/search/parse-image-info"; - - private final OsmandApplication app; - private final WikiImageCard imageCard; - private final GetImageWikiMetaDataListener listener; - - public LoadImageMetadataTask(@NonNull OsmandApplication app, @NonNull WikiImageCard imageCard, - @Nullable GetImageWikiMetaDataListener listener) { - this.imageCard = imageCard; - this.listener = listener; - this.app = app; - } - - @Override - protected Void doInBackground(Void... voids) { - String wikiMediaUrl = WIKI_MEDIA_BASE_URL + imageCard.getWikiImage().getWikiMediaTag() + WIKI_MEDIA_ACTION_RAW; - - StringBuilder builder = new StringBuilder(); - String error = NetworkUtils.sendGetRequest(wikiMediaUrl, null, builder); - if (Algorithms.isEmpty(error)) { - String data = builder.toString(); - if (!Algorithms.isEmpty(data)) { - requestWikiDataParsing(data); - } - } else { - LOG.error(error); - } - - return null; - } - - private void requestWikiDataParsing(@NonNull String data) { - AndroidNetworkUtils.sendRequest(app, OSMAND_PARSE_URL, data, null, - "application/json", false, true, (result, error, resultCode) -> { - if (!Algorithms.isEmpty(error)) { - LOG.error(error); - } else if (!Algorithms.isEmpty(result)) { - try { - JSONObject object = new JSONObject(result); - Metadata metadata = imageCard.getWikiImage().getMetadata(); - if (Algorithms.isEmpty(metadata.getAuthor())) { - metadata.setAuthor(object.getString("author")); - } - if (Algorithms.isEmpty(metadata.getDate())) { - metadata.setDate(object.getString("date")); - } - if (Algorithms.isEmpty(metadata.getLicense())) { - metadata.setLicense(object.getString("license")); - } - imageCard.setMetaDataDownloaded(true); - } catch (JSONException e) { - LOG.error(e); - } - } else { - LOG.error("Empty metadata response"); - } - }); - } - - @Override - protected void onPostExecute(Void unused) { - if (listener != null) { - listener.onFinish(imageCard); - } - } - - public interface GetImageWikiMetaDataListener { - void onFinish(@NonNull WikiImageCard wikiImageCard); - } -} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/tasks/LoadImagesMetadataTask.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/tasks/LoadImagesMetadataTask.java new file mode 100644 index 00000000000..a6e93094350 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/tasks/LoadImagesMetadataTask.java @@ -0,0 +1,119 @@ +package net.osmand.plus.mapcontextmenu.gallery.tasks; + +import android.os.AsyncTask; + +import androidx.annotation.NonNull; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import net.osmand.PlatformUtil; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.helpers.LocaleHelper; +import net.osmand.plus.mapcontextmenu.builders.cards.ImageCard; +import net.osmand.plus.mapcontextmenu.gallery.GalleryController; +import net.osmand.plus.mapcontextmenu.gallery.ImageCardsHolder; +import net.osmand.plus.utils.AndroidNetworkUtils; +import net.osmand.plus.wikipedia.WikiImageCard; +import net.osmand.util.Algorithms; + +import org.apache.commons.logging.Log; + +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Type; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +public class LoadImagesMetadataTask extends AsyncTask>> { + + private static final Log LOG = PlatformUtil.getLog(LoadImagesMetadataTask.class); + + private static final String OSMAND_PARSE_URL = "https://osmand.net/search/parse-images-list-info?"; + + private final OsmandApplication app; + private final Set cards; + private Map> resultMap = null; + + public LoadImagesMetadataTask(@NonNull OsmandApplication app, @NonNull Set cards) { + this.cards = cards; + this.app = app; + } + + public record WikiImageInfo(String title, Long pageId, String data) { + } + + @Override + protected Map> doInBackground(Void... voids) { + List data = getData(); + Gson gson = new Gson(); + String jsonData = gson.toJson(data); + String url = ""; + + String deviceId = app.getSettings().BACKUP_DEVICE_ID.get(); + String accessToken = app.getSettings().BACKUP_ACCESS_TOKEN.get(); + String lang; + + LocaleHelper localeHelper = app.getLocaleHelper(); + Locale preferredLocale = localeHelper.getPreferredLocale(); + Locale locale = preferredLocale != null ? preferredLocale : localeHelper.getDefaultLocale(); + lang = locale.getLanguage(); + + try { + if (!Algorithms.isEmpty(lang)) { + url += (url.isEmpty() ? OSMAND_PARSE_URL : "&") + "lang=" + URLEncoder.encode(lang, "UTF-8"); + } + if (!Algorithms.isEmpty(deviceId)) { + url += (url.isEmpty() ? OSMAND_PARSE_URL : "&") + "deviceId=" + URLEncoder.encode(deviceId, "UTF-8"); + } + if (!Algorithms.isEmpty(accessToken)) { + url += (url.isEmpty() ? OSMAND_PARSE_URL : "&") + "accessToken=" + URLEncoder.encode(accessToken, "UTF-8"); + } + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + + AndroidNetworkUtils.sendRequest(app, url, jsonData, null, + "application/json", false, true, (result, error, resultCode) -> { + if (!Algorithms.isEmpty(error)) { + LOG.error(error); + } else if (!Algorithms.isEmpty(result)) { + try { + Type mapType = new TypeToken>>() { + }.getType(); + resultMap = gson.fromJson(result, mapType); + } catch (Exception e) { + LOG.error(e); + } + } else { + LOG.error("Empty metadata response"); + } + }); + + return resultMap; + } + + private List getData() { + List data = new ArrayList<>(); + for (WikiImageCard card : cards) { + String title = card.getWikiImage().getWikiMediaTag(); + long pageId = card.getWikiImage().getMediaId(); + + if (!Algorithms.isEmpty(title) && pageId != -1) { + data.add(new WikiImageInfo(title, pageId, null)); + } + } + return data; + } + + @Override + protected void onPostExecute(Map> result) { + GalleryController controller = (GalleryController) app.getDialogManager().findController(GalleryController.PROCESS_ID); + if (controller != null) { + controller.updateMetadata(result, cards); + } + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/poi/PoiUIFilter.java b/OsmAnd/src/net/osmand/plus/poi/PoiUIFilter.java index 133a0fac7c0..ebccc7fbcef 100644 --- a/OsmAnd/src/net/osmand/plus/poi/PoiUIFilter.java +++ b/OsmAnd/src/net/osmand/plus/poi/PoiUIFilter.java @@ -6,7 +6,6 @@ import static net.osmand.osm.MapPoiTypes.ROUTES; import static net.osmand.osm.MapPoiTypes.ROUTE_ARTICLE; import static net.osmand.osm.MapPoiTypes.ROUTE_ARTICLE_POINT; -import static net.osmand.osm.MapPoiTypes.ROUTE_TRACK; import static net.osmand.osm.MapPoiTypes.WIKI_PLACE; import android.content.Context; @@ -203,10 +202,6 @@ public boolean isRouteArticlePointFilter() { return filterId.startsWith(STD_PREFIX + ROUTE_ARTICLE_POINT); } - public boolean isRouteTrackFilter() { - return filterId.startsWith(STD_PREFIX + ROUTE_TRACK); - } - public boolean isShowPrivateNeeded() { return filterByName != null && filterByName.contains("access:private"); } diff --git a/OsmAnd/src/net/osmand/plus/quickaction/actions/NavStartStopAction.java b/OsmAnd/src/net/osmand/plus/quickaction/actions/NavStartStopAction.java index ba61e92fb51..5b9d57e8367 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/actions/NavStartStopAction.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/actions/NavStartStopAction.java @@ -93,8 +93,11 @@ public int getIconRes(Context context) { if (!helper.isRoutePlanningMode() && !helper.isFollowingMode() && context instanceof MapActivity activity) { return activity.getMapActions().getRouteMode().getIconRes(); + } else if (helper.isPauseNavigation() || helper.isFollowingMode() || helper.isRoutePlanningMode()) { + return helper.getAppMode().getIconRes(); + } else { + return app.getSettings().getApplicationMode().getIconRes(); } - return helper.getAppMode().getIconRes(); } @Override diff --git a/OsmAnd/src/net/osmand/plus/render/TravelRendererHelper.java b/OsmAnd/src/net/osmand/plus/render/TravelRendererHelper.java index ea49370d3ad..c59af72392c 100644 --- a/OsmAnd/src/net/osmand/plus/render/TravelRendererHelper.java +++ b/OsmAnd/src/net/osmand/plus/render/TravelRendererHelper.java @@ -3,10 +3,11 @@ import static net.osmand.IProgress.EMPTY_PROGRESS; import static net.osmand.IndexConstants.BINARY_TRAVEL_GUIDE_MAP_INDEX_EXT; import static net.osmand.IndexConstants.WIKIVOYAGE_INDEX_DIR; +import static net.osmand.osm.MapPoiTypes.ROUTES_PREFIX; import static net.osmand.osm.MapPoiTypes.ROUTE_ARTICLE; import static net.osmand.osm.MapPoiTypes.ROUTE_ARTICLE_POINT; import static net.osmand.osm.MapPoiTypes.ROUTE_TRACK; -import static net.osmand.plus.wikivoyage.data.TravelGpx.ACTIVITY_TYPE; +import static net.osmand.plus.wikivoyage.data.TravelGpx.ROUTE_ACTIVITY_TYPE; import static net.osmand.render.RenderingRulesStorage.LINE_RULES; import static net.osmand.render.RenderingRulesStorage.ORDER_RULES; import static net.osmand.render.RenderingRulesStorage.POINT_RULES; @@ -20,6 +21,8 @@ import net.osmand.StateChangedListener; import net.osmand.core.android.MapRendererContext; import net.osmand.osm.MapPoiTypes; +import net.osmand.osm.PoiCategory; +import net.osmand.osm.PoiType; import net.osmand.plus.OsmandApplication; import net.osmand.plus.poi.PoiUIFilter; import net.osmand.plus.render.RendererRegistry.IRendererLoadedEventListener; @@ -42,6 +45,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeSet; public class TravelRendererHelper implements IRendererLoadedEventListener { @@ -68,7 +72,7 @@ public class TravelRendererHelper implements IRendererLoadedEventListener { private PoiUIFilter routeArticleFilter; private PoiUIFilter routeArticlePointsFilter; - private PoiUIFilter routeTrackFilter; + private Set routeTrackFilters; public interface OnFileVisibilityChangeListener { void fileVisibilityChanged(); @@ -144,12 +148,12 @@ public void updateRouteTypesVisibility() { } boolean showTracks = getRouteTracksProperty().get(); boolean renderedChanged = false; - List routesTypes = resourceManager.searchPoiSubTypesByPrefix(ACTIVITY_TYPE); + List routesTypes = resourceManager.searchPoiSubTypesByPrefix(ROUTE_ACTIVITY_TYPE); for (String type : routesTypes) { CommonPreference pref = getRouteTypeProperty(type); if (renderer != null) { boolean selected = showTracks && pref.get(); - String attrName = type.replace(ACTIVITY_TYPE + "_", ""); + String attrName = type.replace(ROUTE_ACTIVITY_TYPE + "_", ""); renderedChanged |= updateRouteTypeVisibility(renderer, attrName, selected, false); } } @@ -235,11 +239,11 @@ public PoiUIFilter getRouteArticlePointsFilter() { } @Nullable - public PoiUIFilter getRouteTrackFilter() { - if (routeTrackFilter == null && app.getPoiTypes().isInit()) { - updateRouteTrackFilter(); + public Set getRouteTrackFilters() { + if (routeTrackFilters == null) { + updateRouteTrackFilters(); } - return routeTrackFilter; + return routeTrackFilters; } public void updateRouteArticleFilter() { @@ -249,26 +253,38 @@ public void updateRouteArticleFilter() { } public void updateRouteArticlePointsFilter() { - if (!app.getPoiTypes().isInit()) { - return; - } - PoiUIFilter routeArticlePointsFilter = app.getPoiFilters().getFilterById(PoiUIFilter.STD_PREFIX + ROUTE_ARTICLE_POINT); - if (routeArticlePointsFilter != null) { - Set selectedCategories = new HashSet<>(); - List categories = app.getResourceManager().searchPoiSubTypesByPrefix(MapPoiTypes.CATEGORY); - for (String category : categories) { - CommonPreference prop = getRoutePointCategoryProperty(category); - if (prop.get()) { - selectedCategories.add(category.replace('_', ':').toLowerCase()); + if (app.getPoiTypes().isInit()) { + PoiUIFilter routeArticlePointsFilter = app.getPoiFilters().getFilterById(PoiUIFilter.STD_PREFIX + ROUTE_ARTICLE_POINT); + if (routeArticlePointsFilter != null) { + Set selectedCategories = new HashSet<>(); + List categories = app.getResourceManager().searchPoiSubTypesByPrefix(MapPoiTypes.CATEGORY); + for (String category : categories) { + CommonPreference prop = getRoutePointCategoryProperty(category); + if (prop.get()) { + selectedCategories.add(category.replace('_', ':').toLowerCase()); + } } + routeArticlePointsFilter.setFilterByName(TextUtils.join(" ", selectedCategories)); } - routeArticlePointsFilter.setFilterByName(TextUtils.join(" ", selectedCategories)); + this.routeArticlePointsFilter = routeArticlePointsFilter; } - this.routeArticlePointsFilter = routeArticlePointsFilter; } - public void updateRouteTrackFilter() { - routeTrackFilter = app.getPoiFilters().getFilterById(PoiUIFilter.STD_PREFIX + ROUTE_TRACK); + public void updateRouteTrackFilters() { + if (app.getPoiTypes().isInit()) { + routeTrackFilters = new TreeSet<>(); + PoiCategory routes = app.getPoiTypes().getRoutes(); + for (PoiType subType : routes.getPoiTypes()) { + String subTypeKeyName = subType.getKeyName(); + if (subTypeKeyName.startsWith(ROUTES_PREFIX)) { + routeTrackFilters.add(app.getPoiFilters().getFilterById(PoiUIFilter.STD_PREFIX + subTypeKeyName)); + } + } + PoiUIFilter filter = app.getPoiFilters().getFilterById(PoiUIFilter.STD_PREFIX + ROUTE_TRACK); + if (filter != null) { + routeTrackFilters.add(filter); + } + } } public boolean updateRouteTypeVisibility(RenderingRulesStorage storage, String name, boolean selected) { @@ -369,7 +385,7 @@ private boolean updateRouteTypeVisibility(RenderingRulesStorage storage, String public void onRendererLoaded(String name, RenderingRulesStorage rules, InputStream source) { for (Map.Entry> entry : routeTypesProps.entrySet()) { boolean selected = entry.getValue().get(); - String attrName = entry.getKey().replace(ACTIVITY_TYPE + "_", ""); + String attrName = entry.getKey().replace(ROUTE_ACTIVITY_TYPE + "_", ""); updateRouteTypeVisibility(rules, attrName, selected, false); } } diff --git a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java index 9b33f16ed5f..d43b4e25240 100644 --- a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java +++ b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java @@ -1569,9 +1569,10 @@ public Map getBackupIndexes(Map map) { if (file != null && file.isDirectory()) { File[] lf = file.listFiles(); if (lf != null) { + DateFormat dateFormat = getDateFormat(); for (File f : lf) { if (f != null && f.getName().endsWith(IndexConstants.BINARY_MAP_INDEX_EXT)) { - map.put(f.getName(), AndroidUtils.formatDate(context, f.lastModified())); + map.put(f.getName(), dateFormat.format(f.lastModified())); } } } diff --git a/OsmAnd/src/net/osmand/plus/search/dialogs/QuickSearchListFragment.java b/OsmAnd/src/net/osmand/plus/search/dialogs/QuickSearchListFragment.java index 07e5509261d..b0314566fcf 100644 --- a/OsmAnd/src/net/osmand/plus/search/dialogs/QuickSearchListFragment.java +++ b/OsmAnd/src/net/osmand/plus/search/dialogs/QuickSearchListFragment.java @@ -16,7 +16,12 @@ import androidx.fragment.app.FragmentActivity; import net.osmand.IndexConstants; +import net.osmand.PlatformUtil; +import net.osmand.data.Amenity; import net.osmand.data.PointDescription; +import net.osmand.plus.wikivoyage.data.TravelGpx; +import net.osmand.plus.wikivoyage.data.TravelHelper; +import net.osmand.plus.wikivoyage.data.TravelObfHelper; import net.osmand.shared.gpx.GpxFile; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; @@ -43,11 +48,14 @@ import net.osmand.search.core.SearchResult; import net.osmand.util.Algorithms; +import org.apache.commons.logging.Log; + import java.io.File; import java.util.ArrayList; import java.util.List; public abstract class QuickSearchListFragment extends OsmAndListFragment { + private static final Log LOG = PlatformUtil.getLog(QuickSearchListFragment.class); protected OsmandApplication app; private QuickSearchDialogFragment dialogFragment; @@ -175,27 +183,46 @@ public boolean isShowResult() { public void showResult(SearchResult searchResult) { showResult = false; if (searchResult.objectType == ObjectType.GPX_TRACK) { - GPXInfo gpxInfo = (GPXInfo) searchResult.relatedObject; - if (dialogFragment.getSearchType().isTargetPoint()) { - File file = gpxInfo.getFile(); - if (file != null) { - selectTrack(file); - } - } else { - showTrackMenuFragment(gpxInfo); - } + showGpxTrackResult(searchResult); } else if (searchResult.location != null) { - Pair pair = QuickSearchListItem.getPointDescriptionObject(app, searchResult); + showResultWithLocation(searchResult); + } + } - dialogFragment.hideToolbar(); - dialogFragment.hide(); + private void showResultWithLocation(SearchResult searchResult) { + Pair pair = QuickSearchListItem.getPointDescriptionObject(app, searchResult); + dialogFragment.hideToolbar(); + dialogFragment.hide(); + + if (pair.second instanceof Amenity && ((Amenity) pair.second).isRouteTrack()) { + Amenity amenity = (Amenity) pair.second; + TravelHelper travelHelper = app.getTravelHelper(); + TravelGpx travelGpx = travelHelper.searchGpx(amenity.getLocation(), amenity.getRouteId(), amenity.getRef()); + if (travelGpx != null) { + travelHelper.openTrackMenu(travelGpx, getMapActivity(), amenity.getGpxFileName(null), amenity.getLocation(), true); + } else { + LOG.error("showResultWithLocation() searchGpx() travelGpx is null"); + } + } else { showOnMap(getMapActivity(), dialogFragment, searchResult.location.getLatitude(), searchResult.location.getLongitude(), searchResult.preferredZoom, pair.first, pair.second); } } + private void showGpxTrackResult(SearchResult searchResult) { + GPXInfo gpxInfo = (GPXInfo) searchResult.relatedObject; + if (dialogFragment.getSearchType().isTargetPoint()) { + File file = gpxInfo.getFile(); + if (file != null) { + selectTrack(file); + } + } else { + showTrackMenuFragment(gpxInfo); + } + } + private void selectTrack(@NonNull File file) { SelectedGpxFile selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(file.getAbsolutePath()); if (selectedGpxFile != null) { diff --git a/OsmAnd/src/net/osmand/plus/track/helpers/GpxUiHelper.java b/OsmAnd/src/net/osmand/plus/track/helpers/GpxUiHelper.java index 12877fab62b..30b7ac44cbf 100644 --- a/OsmAnd/src/net/osmand/plus/track/helpers/GpxUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/track/helpers/GpxUiHelper.java @@ -629,7 +629,8 @@ public static void saveAndOpenGpx(@NonNull MapActivity mapActivity, @NonNull GpxFile gpxFile, @NonNull WptPt selectedPoint, @Nullable GpxTrackAnalysis analyses, - @Nullable RouteKey routeKey) { + @Nullable RouteKey routeKey, + boolean adjustMapPosition) { SaveGpxHelper.saveGpx(file, gpxFile, errorMessage -> { if (errorMessage == null) { OsmandApplication app = mapActivity.getMyApplication(); @@ -638,7 +639,7 @@ public static void saveAndOpenGpx(@NonNull MapActivity mapActivity, GpxTrackAnalysis trackAnalysis = analyses != null ? analyses : selectedGpxFile.getTrackAnalysis(app); SelectedGpxPoint selectedGpxPoint = new SelectedGpxPoint(selectedGpxFile, selectedPoint); Bundle bundle = new Bundle(); - bundle.putBoolean(TrackMenuFragment.ADJUST_MAP_POSITION, false); + bundle.putBoolean(TrackMenuFragment.ADJUST_MAP_POSITION, adjustMapPosition); TrackMenuFragment.showInstance(mapActivity, selectedGpxFile, selectedGpxPoint, trackAnalysis, routeKey, bundle); } else { diff --git a/OsmAnd/src/net/osmand/plus/views/OsmandMap.java b/OsmAnd/src/net/osmand/plus/views/OsmandMap.java index a3ccc5aa475..c204ee0bc5b 100644 --- a/OsmAnd/src/net/osmand/plus/views/OsmandMap.java +++ b/OsmAnd/src/net/osmand/plus/views/OsmandMap.java @@ -148,15 +148,15 @@ public float getOriginalTextScale() { } public float getMapDensity() { - float scale = app.getSettings().MAP_DENSITY.get(); - return scale;// * getCarDensityScaleCoef(); + return app.getSettings().MAP_DENSITY.get(); } public float getCarDensityScaleCoef() { OsmandMapTileView mapView = app.getOsmandMap().getMapView(); if (mapView.isCarView()) { float carViewDensity = mapView.getCarViewDensity(); - return carViewDensity / 2f + 0.1f; + float density = mapView.getDensity(); + return carViewDensity / density; } return 1f; } diff --git a/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java b/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java index e5a5373bcf4..ab993e105bd 100644 --- a/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java +++ b/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java @@ -106,7 +106,7 @@ public class OsmandMapTileView implements IMapDownloaderCallback { private static final int MAX_ZOOM_LIMIT = 17; private static final long ANIMATION_PREVIEW_TIME = 1500; - private static final float ZOOM_STEP_TO_FIT = 0.1f; + private static final float ZOOM_STEP_TO_FIT = 0.05f; private static final float MARGIN_PERCENT_TO_FIT = 0.8f; private boolean MEASURE_FPS; diff --git a/OsmAnd/src/net/osmand/plus/views/layers/ContextMenuLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/ContextMenuLayer.java index 420641f490c..94e1e84107d 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/ContextMenuLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/ContextMenuLayer.java @@ -1,7 +1,6 @@ package net.osmand.plus.views.layers; import static net.osmand.aidlapi.OsmAndCustomizationConstants.MAP_CONTEXT_MENU_CHANGE_MARKER_POSITION; -import static net.osmand.plus.views.layers.geometry.GeometryWayDrawer.VECTOR_LINE_SCALE_COEF; import android.Manifest; import android.content.Context; @@ -59,6 +58,7 @@ import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.layers.MapSelectionHelper.MapSelectionResult; import net.osmand.plus.views.layers.base.OsmandMapLayer; +import net.osmand.plus.views.layers.geometry.GeometryWayDrawer; import net.osmand.plus.widgets.ctxmenu.ContextMenuAdapter; import net.osmand.plus.widgets.ctxmenu.callback.ItemClickListener; import net.osmand.plus.widgets.ctxmenu.data.ContextMenuItem; @@ -236,7 +236,7 @@ public void onDraw(Canvas canvas, RotatedTileBox box, DrawSettings nightMode) { builder.setPoints(points) .setIsHidden(false) .setLineId(1) - .setLineWidth(outlinePaint.getStrokeWidth() * VECTOR_LINE_SCALE_COEF) + .setLineWidth(outlinePaint.getStrokeWidth() * GeometryWayDrawer.getVectorLineScale(getApplication())) .setFillColor(NativeUtilities.createFColorARGB(outlinePaint.getColor())) .setApproximationEnabled(false) .setBaseOrder(getBaseOrder()); diff --git a/OsmAnd/src/net/osmand/plus/views/layers/DistanceRulerControlLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/DistanceRulerControlLayer.java index 090ff33e787..e5cc0edc1a8 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/DistanceRulerControlLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/DistanceRulerControlLayer.java @@ -398,7 +398,7 @@ private void drawLineBetweenLocationsOpenGl(@NonNull MapRendererView mapRenderer vectorLineBuilder.setBaseOrder(getBaseOrder()) .setIsHidden(false) .setLineId(0) - .setLineWidth(lineAttrs.paint.getStrokeWidth() * GeometryWayDrawer.VECTOR_LINE_SCALE_COEF) + .setLineWidth(lineAttrs.paint.getStrokeWidth() * GeometryWayDrawer.getVectorLineScale(app)) .setPoints(points31) .setEndCapStyle(VectorLine.EndCapStyle.BUTT.swigValue()) .setFillColor(NativeUtilities.createFColorARGB(lineAttrs.paint.getColor())); diff --git a/OsmAnd/src/net/osmand/plus/views/layers/MapSelectionHelper.java b/OsmAnd/src/net/osmand/plus/views/layers/MapSelectionHelper.java index 0e0f17e2e1a..a8c94dcc5e6 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/MapSelectionHelper.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/MapSelectionHelper.java @@ -2,10 +2,13 @@ import static net.osmand.IndexConstants.GPX_FILE_EXT; import static net.osmand.binary.BinaryMapIndexReader.ACCEPT_ALL_POI_TYPE_FILTER; +import static net.osmand.data.Amenity.ROUTE; +import static net.osmand.data.Amenity.ROUTE_ID; import static net.osmand.data.FavouritePoint.DEFAULT_BACKGROUND_TYPE; import static net.osmand.data.MapObject.AMENITY_ID_RIGHT_SHIFT; import static net.osmand.osm.OsmRouteType.HIKING; import static net.osmand.plus.transport.TransportLinesMenu.RENDERING_CATEGORY_TRANSPORT; +import static net.osmand.plus.wikivoyage.data.TravelGpx.TRAVEL_MAP_TO_POI_TAG; import static net.osmand.render.RenderingRuleStorageProperties.UI_CATEGORY_HIDDEN; import static net.osmand.router.network.NetworkRouteSelector.NetworkRouteSelectorFilter; import static net.osmand.router.network.NetworkRouteSelector.RouteKey; @@ -216,7 +219,7 @@ private void selectObjectsFromNative(@NonNull MapSelectionResult result, @NonNul if (renderedObjects != null) { double cosRotateTileSize = Math.cos(Math.toRadians(rc.rotate)) * TILE_SIZE; double sinRotateTileSize = Math.sin(Math.toRadians(rc.rotate)) * TILE_SIZE; - boolean selectedRoutes = false; + boolean isRouteGpxSelected = false; for (RenderedObject renderedObject : renderedObjects) { String routeID = renderedObject.getRouteID(); String fileName = renderedObject.getGpxFileName(); @@ -253,14 +256,14 @@ private void selectObjectsFromNative(@NonNull MapSelectionResult result, @NonNul result.objectLatLon = renderedObject.getLabelLatLon(); } LatLon searchLatLon = result.objectLatLon != null ? result.objectLatLon : result.pointLatLon; - if (isTravelGpx) { - addTravelGpx(result, renderedObject, filter); + if (isTravelGpx && !isRouteGpxSelected) { + isRouteGpxSelected = addTravelGpx(result, filter, renderedObject.getTagValue("ref")); } else { - if (isRoute && !selectedRoutes) { - selectedRoutes = true; + if (isRoute && !isRouteGpxSelected) { NetworkRouteSelectorFilter routeFilter = createRouteFilter(); if (!Algorithms.isEmpty(routeFilter.typeFilter)) { - addRoute(result, tileBox, point, routeFilter); + addOsmRoute(result, tileBox, point, routeFilter); + isRouteGpxSelected = true; } } boolean amenityAdded = addAmenity(result, renderedObject, searchLatLon); @@ -279,7 +282,7 @@ private void selectObjectsFromOpenGl(@NonNull MapSelectionResult result, @NonNul int delta = 20; PointI tl = new PointI((int) point.x - delta, (int) point.y - delta); PointI br = new PointI((int) point.x + delta, (int) point.y + delta); - boolean selectedRoutes = false; + boolean isRouteGpxSelected = false; MapSymbolInformationList symbols = rendererView.getSymbolsIn(new AreaI(tl, br), false); for (int i = 0; i < symbols.size(); i++) { MapSymbolInformation symbolInfo = symbols.get(i); @@ -336,14 +339,18 @@ private void selectObjectsFromOpenGl(@NonNull MapSelectionResult result, @NonNul } if (obfMapObject != null) { Map tags = getTags(obfMapObject.getResolvedAttributes()); - boolean isRoute = !Algorithms.isEmpty(OsmRouteType.getRouteKeys(tags)); - if (isRoute && !selectedRoutes) { - selectedRoutes = true; + boolean isOsmRoute = !Algorithms.isEmpty(OsmRouteType.getRouteKeys(tags)); + if (isOsmRoute && !isRouteGpxSelected) { NetworkRouteSelectorFilter routeFilter = createRouteFilter(); if (!Algorithms.isEmpty(routeFilter.typeFilter)) { - addRoute(result, tileBox, point, routeFilter); + addOsmRoute(result, tileBox, point, routeFilter); + isRouteGpxSelected = true; } } + boolean isTravelGpx = app.getTravelHelper().isTravelGpxTags(tags); + if (isTravelGpx && !isRouteGpxSelected) { + isRouteGpxSelected = addTravelGpx(result, tags.get(ROUTE_ID), null); + } IOnPathMapSymbol onPathMapSymbol = getOnPathMapSymbol(symbolInfo); if (onPathMapSymbol == null) { LatLon latLon = result.objectLatLon; @@ -352,10 +359,10 @@ private void selectObjectsFromOpenGl(@NonNull MapSelectionResult result, @NonNul latLon = l == null ? latLon : l; tags.remove(TAG_POI_LAT_LON); } - amenity = getAmenity(latLon, obfMapObject); + amenity = getAmenity(latLon, obfMapObject, tags); if (amenity != null) { amenity.setMapIconName(getMapIconName(symbolInfo)); - } else if (!isRoute) { + } else if (!isOsmRoute && !isTravelGpx) { addRenderedObject(result, symbolInfo, obfMapObject, tags); } } @@ -433,13 +440,16 @@ private RasterMapSymbol getRasterMapSymbol(@NonNull MapSymbolInformation symbolI return null; } - private Amenity getAmenity(LatLon latLon, ObfMapObject obfMapObject) { + private Amenity getAmenity(LatLon latLon, ObfMapObject obfMapObject, Map tags) { Amenity amenity; List names = getValues(obfMapObject.getCaptionsInAllLanguages()); String caption = obfMapObject.getCaptionInNativeLanguage(); if (!caption.isEmpty()) { names.add(caption); } + if (!Algorithms.isEmpty(tags) && tags.containsKey(TRAVEL_MAP_TO_POI_TAG) && "point".equals(tags.get(ROUTE))) { + names.add(tags.get(TRAVEL_MAP_TO_POI_TAG)); // additional attribute for TravelGpx points (route_id) + } long id = obfMapObject.getId().getId().longValue(); amenity = findAmenity(app, latLon, names, id); if (amenity != null && obfMapObject.getPoints31().size() > 1) { @@ -452,24 +462,24 @@ private Amenity getAmenity(LatLon latLon, ObfMapObject obfMapObject) { return amenity; } - private void addTravelGpx(@NonNull MapSelectionResult result, @NonNull RenderedObject object, @Nullable String filter) { - TravelGpx travelGpx = app.getTravelHelper().searchGpx(result.pointLatLon, filter, object.getTagValue("ref")); + private boolean addTravelGpx(@NonNull MapSelectionResult result, @Nullable String routeId, @Nullable String ref) { + TravelGpx travelGpx = app.getTravelHelper().searchGpx(result.pointLatLon, routeId, ref); if (travelGpx != null && isUniqueGpx(result.selectedObjects, travelGpx)) { WptPt selectedPoint = new WptPt(); selectedPoint.setLat(result.pointLatLon.getLatitude()); selectedPoint.setLon(result.pointLatLon.getLongitude()); SelectedGpxPoint selectedGpxPoint = new SelectedGpxPoint(null, selectedPoint); result.selectedObjects.put(new Pair<>(travelGpx, selectedGpxPoint), mapLayers.getTravelSelectionLayer()); + return true; + } else if (travelGpx == null) { + log.error("addTravelGpx() searchGpx() travelGpx is null"); } + return false; } private boolean isUniqueGpx(@NonNull Map selectedObjects, @NonNull TravelGpx travelGpx) { - String tracksDir = app.getAppPath(IndexConstants.GPX_TRAVEL_DIR).getPath(); - File file = new File(tracksDir, travelGpx.getRouteId() + GPX_FILE_EXT); - if (file.exists()) { - return false; - } + String travelGpxFileName = travelGpx.getGpxFileName() + GPX_FILE_EXT; for (Map.Entry entry : selectedObjects.entrySet()) { if (entry.getKey() instanceof Pair && entry.getValue() instanceof GPXLayer && ((Pair) entry.getKey()).first instanceof TravelGpx) { @@ -478,12 +488,18 @@ private boolean isUniqueGpx(@NonNull Map selectedO return false; } } + if (entry.getKey() instanceof SelectedGpxPoint && entry.getValue() instanceof GPXLayer) { + SelectedGpxPoint selectedGpxPoint = (SelectedGpxPoint) entry.getKey(); + if (selectedGpxPoint.getSelectedGpxFile().getGpxFile().getPath().endsWith(travelGpxFileName)) { + return false; + } + } } return true; } - private void addRoute(@NonNull MapSelectionResult result, @NonNull RotatedTileBox tileBox, @NonNull PointF point, - @NonNull NetworkRouteSelectorFilter selectorFilter) { + private void addOsmRoute(@NonNull MapSelectionResult result, @NonNull RotatedTileBox tileBox, @NonNull PointF point, + @NonNull NetworkRouteSelectorFilter selectorFilter) { int searchRadius = (int) (OsmandMapLayer.getScaledTouchRadius(app, tileBox.getDefaultRadiusPoi()) * 1.5f); LatLon minLatLon = NativeUtilities.getLatLonFromElevatedPixel(view.getMapRenderer(), tileBox, point.x - searchRadius, point.y - searchRadius); @@ -703,13 +719,22 @@ public static Amenity findAmenityByOsmId(@NonNull List amenities, long @Nullable public static Amenity findAmenityByName(@NonNull List amenities, @Nullable List names) { if (!Algorithms.isEmpty(names)) { - for (Amenity amenity : amenities) { - for (String name : names) { - if (name.equals(amenity.getName()) && !amenity.isClosed()) { - return amenity; - } - } - } + return amenities.stream() + .filter(amenity -> !amenity.isClosed()) + .filter(amenity -> names.contains(amenity.getName())) + .findAny() + .orElseGet(() -> + amenities.stream() + .filter(amenity -> !amenity.isClosed()) + .filter(amenity -> amenity.isRoutePoint()) + .filter(amenity -> amenity.getName().isEmpty()) + .filter(amenity -> { + String travelRouteId = amenity.getAdditionalInfo(TRAVEL_MAP_TO_POI_TAG); + return travelRouteId != null && names.contains(travelRouteId); + }) + .findAny() + .orElse(null) + ); } return null; } diff --git a/OsmAnd/src/net/osmand/plus/views/layers/NetworkRouteSelectionLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/NetworkRouteSelectionLayer.java index be9503aa55d..5d7959ef482 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/NetworkRouteSelectionLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/NetworkRouteSelectionLayer.java @@ -154,7 +154,7 @@ private void saveAndOpenGpx(@NonNull GpxFile gpxFile, @NonNull Pair routeTrackFilters; private String routeArticlePointsFilterByName; private boolean fileVisibilityChanged; public CustomMapObjects customObjectsDelegate; @@ -119,7 +120,7 @@ public POIMapLayer(@NonNull Context context) { routeArticlePointsFilterEnabled = travelRendererHelper.getRouteArticlePointsProperty().get(); routeArticleFilter = travelRendererHelper.getRouteArticleFilter(); routeArticlePointsFilter = travelRendererHelper.getRouteArticlePointsFilter(); - routeTrackFilter = travelRendererHelper.getRouteTrackFilter(); + routeTrackFilters = travelRendererHelper.getRouteTrackFilters(); routeArticlePointsFilterByName = routeArticlePointsFilter != null ? routeArticlePointsFilter.getFilterByName() : null; routingHelper.addListener(this); @@ -158,6 +159,7 @@ protected List calculateResult(@NonNull QuadRect latLonBounds, int zoom int z = (int) Math.floor(zoom + Math.log(getMapDensity()) / Math.log(2)); List res = new ArrayList<>(); + Set uniqueRouteIds = new HashSet<>(); PoiFilterUtils.combineStandardPoiFilters(calculatedFilters, app); for (PoiUIFilter filter : calculatedFilters) { List amenities = filter.searchAmenities(latLonBounds.top, latLonBounds.left, @@ -173,24 +175,14 @@ public boolean isCancelled() { return isInterrupted(); } }); - if (filter.isRouteTrackFilter()) { - for (Amenity amenity : amenities) { - boolean hasRoute = false; + for (Amenity amenity : amenities) { + if (amenity.isRouteTrack()) { String routeId = amenity.getRouteId(); - if (!Algorithms.isEmpty(routeId)) { - for (Amenity a : res) { - if (routeId.equals(a.getRouteId())) { - hasRoute = true; - break; - } - } - } - if (!hasRoute) { - res.add(amenity); + if (routeId != null && !uniqueRouteIds.add(routeId)) { + continue; // duplicate } } - } else { - res.addAll(amenities); + res.add(amenity); } } @@ -224,9 +216,8 @@ private Set collectFilters() { calculatedFilters.add(routeArticlePointsFilter); } boolean routeTrackAsPoiFilterEnabled = this.routeTrackAsPoiFilterEnabled; - PoiUIFilter routeTrackFilter = this.routeTrackFilter; - if (routeTrackAsPoiFilterEnabled && routeTrackFilter != null) { - calculatedFilters.add(routeTrackFilter); + if (routeTrackAsPoiFilterEnabled && this.routeTrackFilters != null) { + calculatedFilters.addAll(this.routeTrackFilters); } } return calculatedFilters; @@ -307,7 +298,7 @@ private boolean shouldDraw(int zoom) { && zoom >= START_ZOOM) { return true; } - if (travelRendererHelper.getRouteTracksAsPoiProperty().get() && routeTrackFilter != null) { + if (travelRendererHelper.getRouteTracksAsPoiProperty().get() && routeTrackFilters != null) { return travelRendererHelper.getRouteTracksProperty().get() ? zoom >= START_ZOOM : zoom >= START_ZOOM_ROUTE_TRACK; } @@ -321,7 +312,7 @@ private boolean shouldDraw(@NonNull RotatedTileBox tileBox, @NonNull Amenity ame } else { boolean routeArticle = ROUTE_ARTICLE_POINT.equals(amenity.getSubType()) || ROUTE_ARTICLE.equals(amenity.getSubType()); - boolean routeTrack = ROUTE_TRACK.equals(amenity.getSubType()); + boolean routeTrack = amenity.isRouteTrack(); if (routeArticle) { return tileBox.getZoom() >= START_ZOOM; } else if (routeTrack) { @@ -352,7 +343,7 @@ public void onPrepareBufferImage(Canvas canvas, RotatedTileBox tileBox, DrawSett boolean routeTrackAsPoiFilterEnabled = travelRendererHelper.getRouteTracksAsPoiProperty().get(); PoiUIFilter routeArticleFilter = travelRendererHelper.getRouteArticleFilter(); PoiUIFilter routeArticlePointsFilter = travelRendererHelper.getRouteArticlePointsFilter(); - PoiUIFilter routeTrackFilter = travelRendererHelper.getRouteTrackFilter(); + Set routeTrackFilters = travelRendererHelper.getRouteTrackFilters(); String routeArticlePointsFilterByName = routeArticlePointsFilter != null ? routeArticlePointsFilter.getFilterByName() : null; boolean dataChanged = false; if (this.filters != selectedPoiFilters @@ -363,7 +354,7 @@ public void onPrepareBufferImage(Canvas canvas, RotatedTileBox tileBox, DrawSett || this.routeTrackAsPoiFilterEnabled != routeTrackAsPoiFilterEnabled || this.routeArticleFilter != routeArticleFilter || this.routeArticlePointsFilter != routeArticlePointsFilter - || this.routeTrackFilter != routeTrackFilter + || this.routeTrackFilters != routeTrackFilters || this.fileVisibilityChanged || !Algorithms.stringsEqual(this.routeArticlePointsFilterByName, routeArticlePointsFilterByName)) { this.filters = selectedPoiFilters; @@ -374,7 +365,7 @@ public void onPrepareBufferImage(Canvas canvas, RotatedTileBox tileBox, DrawSett this.routeTrackAsPoiFilterEnabled = routeTrackAsPoiFilterEnabled; this.routeArticleFilter = routeArticleFilter; this.routeArticlePointsFilter = routeArticlePointsFilter; - this.routeTrackFilter = routeTrackFilter; + this.routeTrackFilters = routeTrackFilters; this.routeArticlePointsFilterByName = routeArticlePointsFilterByName; this.fileVisibilityChanged = false; data.clearCache(); @@ -602,26 +593,30 @@ public LatLon getObjectLocation(Object o) { public boolean showMenuAction(@Nullable Object object) { OsmandApplication app = view.getApplication(); MapActivity mapActivity = view.getMapActivity(); - TravelHelper travelHelper = app.getTravelHelper(); if (mapActivity != null && object instanceof Amenity) { + TravelHelper travelHelper = app.getTravelHelper(); Amenity amenity = (Amenity) object; - if (amenity.getSubType().equals(ROUTE_TRACK)) { - TravelGpx travelGpx = travelHelper.searchGpx(amenity.getLocation(), amenity.getRouteId(), amenity.getRef()); - if (travelGpx == null) { + String subType = amenity.getSubType(); + if (amenity.getType().getKeyName().equals(ROUTES)) { + if (subType.equals(ROUTE_ARTICLE)) { + String lang = app.getLanguage(); + lang = amenity.getContentLanguage(Amenity.DESCRIPTION, lang, "en"); + String name = amenity.getGpxFileName(lang); + TravelArticle article = travelHelper.getArticleByTitle(name, lang, true, null); + if (article == null) { + return true; + } + travelHelper.openTrackMenu(article, mapActivity, name, amenity.getLocation(), false); return true; - } - travelHelper.openTrackMenu(travelGpx, mapActivity, amenity.getRouteId(), amenity.getLocation()); - return true; - } else if (amenity.getSubType().equals(ROUTE_ARTICLE)) { - String lang = app.getLanguage(); - lang = amenity.getContentLanguage(Amenity.DESCRIPTION, lang, "en"); - String name = amenity.getName(lang); - TravelArticle article = travelHelper.getArticleByTitle(name, lang, true, null); - if (article == null) { + } else if (amenity.isRouteTrack()) { + TravelGpx travelGpx = travelHelper.searchGpx(amenity.getLocation(), amenity.getRouteId(), amenity.getRef()); + if (travelGpx != null) { + travelHelper.openTrackMenu(travelGpx, mapActivity, amenity.getGpxFileName(null), amenity.getLocation(), false); + } else { + log.error("showMenuAction() searchGpx() travelGpx is null"); + } return true; } - travelHelper.openTrackMenu(article, mapActivity, name, amenity.getLocation()); - return true; } } return false; diff --git a/OsmAnd/src/net/osmand/plus/views/layers/PointLocationLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/PointLocationLayer.java index abe1d058280..750551eca8b 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/PointLocationLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/PointLocationLayer.java @@ -320,6 +320,7 @@ private void updateMarkerState(boolean showHeading) { switch (currentMarkerState) { case MOVE -> { navigationMarker.setVisibility(!showHeading); + locationMarker.setVisibility(false); navigationMarkerWithHeading.setVisibility(showHeading); locationMarkerWithHeading.setVisibility(false); circleColor = showHeading diff --git a/OsmAnd/src/net/osmand/plus/views/layers/TravelSelectionLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/TravelSelectionLayer.java index c0b92cbd4ea..899aef2bc5a 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/TravelSelectionLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/TravelSelectionLayer.java @@ -56,8 +56,13 @@ public PointDescription getObjectName(Object o) { Pair pair = (Pair) o; if (pair.first instanceof TravelGpx && pair.second instanceof SelectedGpxPoint) { TravelGpx travelGpx = (TravelGpx) ((Pair) o).first; - String name = Algorithms.isEmpty(travelGpx.getDescription()) ? travelGpx.getTitle() : travelGpx.getDescription(); - return new PointDescription(PointDescription.POINT_TYPE_GPX, name); + if (!Algorithms.isEmpty(travelGpx.getTitle())) { + return new PointDescription(PointDescription.POINT_TYPE_GPX, travelGpx.getTitle()); + } else if (!Algorithms.isEmpty(travelGpx.getDescription())) { + return new PointDescription(PointDescription.POINT_TYPE_GPX, travelGpx.getDescription()); + } else { + return new PointDescription(PointDescription.POINT_TYPE_GPX, travelGpx.getRouteId()); // nullable + } } } return null; @@ -75,7 +80,8 @@ public boolean showMenuAction(@Nullable Object object) { WptPt wptPt = selectedGpxPoint.getSelectedPoint(); TravelHelper travelHelper = app.getTravelHelper(); - travelHelper.openTrackMenu(travelGpx, mapActivity, travelGpx.getRouteId(), new LatLon(wptPt.getLat(), wptPt.getLon())); + travelHelper.openTrackMenu(travelGpx, mapActivity, travelGpx.getGpxFileName(), + new LatLon(wptPt.getLat(), wptPt.getLon()), false); return true; } } diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWayDrawer.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWayDrawer.java index a860e3faba4..9abeeec3ec2 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWayDrawer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWayDrawer.java @@ -9,6 +9,7 @@ import static net.osmand.plus.views.layers.geometry.GeometryWayStyle.COLORIZATION_GRADIENT; import static net.osmand.plus.views.layers.geometry.GeometryWayStyle.COLORIZATION_NONE; +import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; @@ -34,6 +35,7 @@ import net.osmand.core.jni.VectorLineBuilder; import net.osmand.core.jni.VectorLinesCollection; import net.osmand.data.RotatedTileBox; +import net.osmand.plus.OsmandApplication; import net.osmand.plus.track.Gpx3DLinePositionType; import net.osmand.plus.track.Gpx3DVisualizationType; import net.osmand.plus.utils.NativeUtilities; @@ -49,7 +51,7 @@ public class GeometryWayDrawer { protected static final int LINE_ID = 1; - public static final float VECTOR_LINE_SCALE_COEF = 2.0f; + private static final float VECTOR_LINE_SCALE_COEF = 2.0f; private static final Log log = PlatformUtil.getLog(GeometryWayDrawer.class); private final T context; @@ -97,6 +99,11 @@ public T getContext() { return context; } + public static float getVectorLineScale(@NonNull Context context) { + OsmandApplication app = (OsmandApplication) context.getApplicationContext(); + return VECTOR_LINE_SCALE_COEF + (1 - app.getOsmandMap().getCarDensityScaleCoef()) * VECTOR_LINE_SCALE_COEF; + } + public void drawArrowsOverPath(@NonNull Canvas canvas, @NonNull RotatedTileBox tb, List points, double distPixToFinish) { List arrows = new ArrayList<>(); @@ -214,8 +221,9 @@ protected void buildVectorLine(@NonNull VectorLinesCollection collection, int ba if (line.getLineId() == lineId) { line.setElevationScaleFactor(additionalExaggeration); line.setFillColor(NativeUtilities.createFColorARGB(color)); - line.setLineWidth(width * VECTOR_LINE_SCALE_COEF); - line.setOutlineWidth(outlineWidth * VECTOR_LINE_SCALE_COEF); + float vectorLineScale = getVectorLineScale(context.getApp()); + line.setLineWidth(width * vectorLineScale); + line.setOutlineWidth(outlineWidth * vectorLineScale); line.setPoints(points); setupColorization(line, colorizationScheme, colorizationMapping, outlineColorizationMapping, @@ -235,7 +243,7 @@ protected void buildVectorLine(@NonNull VectorLinesCollection collection, int ba line.setSurfaceLineVisibility(linePositionType == BOTTOM || linePositionType == TOP_BOTTOM); } line.setFillColor(new FColorARGB(a, r, g, b)); - line.setOutlineWidth(width * VECTOR_LINE_SCALE_COEF / 2.0f); + line.setOutlineWidth(width * getVectorLineScale(context.getApp()) / 2.0f); if (wallColorType == Gpx3DWallColorType.NONE) { line.setColorizationScheme(COLORIZATION_GRADIENT); @@ -263,11 +271,12 @@ protected void buildVectorLine(@NonNull VectorLinesCollection collection, int ba } } VectorLineBuilder builder = new VectorLineBuilder(); + float vectorLineScale = getVectorLineScale(context.getApp()); builder.setPoints(points) .setIsHidden(false) .setLineId(lineId) - .setLineWidth(width * VECTOR_LINE_SCALE_COEF) - .setOutlineWidth(outlineWidth * VECTOR_LINE_SCALE_COEF) + .setLineWidth(width * vectorLineScale) + .setOutlineWidth(outlineWidth * vectorLineScale) .setApproximationEnabled(approximationEnabled) .setBaseOrder(baseOrder); if (dashPattern != null) { @@ -302,7 +311,7 @@ protected void buildVectorLine(@NonNull VectorLinesCollection collection, int ba builder.setElevationScaleFactor(additionalExaggeration) .setColorizationScheme(colorizationScheme) .setHeights(heights) - .setOutlineWidth(width * VECTOR_LINE_SCALE_COEF / 2.0f); + .setOutlineWidth(width * vectorLineScale / 2.0f); if (wallColorType == NONE) { builder.setNearOutlineColor(new FColorARGB(0, r, g, b)); builder.setFarOutlineColor(new FColorARGB(0, r, g, b)); diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/RouteGeometryWay.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/RouteGeometryWay.java index 50f9e31b4f2..18d6304208d 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/geometry/RouteGeometryWay.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/RouteGeometryWay.java @@ -335,11 +335,12 @@ public void buildActionArrows(@NonNull List actionPoints, int custo int y = MapUtils.get31TileNumberY(point.location.getLatitude()); points.add(new PointI(x, y)); } + float vectorLineScale = GeometryWayDrawer.getVectorLineScale(getContext().getApp()) / 2.0f; if (lineIdx < initialLinesCount) { VectorLine vectorLine = actionLinesCollection.getLines().get(lineIdx); vectorLine.setPoints(points); vectorLine.setIsHidden(false); - vectorLine.setLineWidth(customWidth); + vectorLine.setLineWidth(customWidth * vectorLineScale); vectorLine.setFillColor(NativeUtilities.createFColorARGB(arrowColor)); lineIdx++; } else { @@ -347,7 +348,7 @@ public void buildActionArrows(@NonNull List actionPoints, int custo vectorLineBuilder.setBaseOrder(baseOrder--) .setIsHidden(false) .setLineId((int) actionLinesCollection.getLines().size()) - .setLineWidth(customWidth) + .setLineWidth(customWidth * vectorLineScale) .setPoints(points) .setEndCapStyle(VectorLine.EndCapStyle.ARROW.ordinal()) .setFillColor(NativeUtilities.createFColorARGB(arrowColor)); diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgets/SpeedometerWidget.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgets/SpeedometerWidget.java index 510c8f1feea..284f0e2abab 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgets/SpeedometerWidget.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgets/SpeedometerWidget.java @@ -583,7 +583,13 @@ private int getSpeedLimitColor(boolean nightMode) { } private int getSpeedTextColor(boolean speedExceed) { - return app.getColor(speedExceed ? R.color.text_color_negative : lastNightMode ? R.color.widgettext_night : R.color.widgettext_day); + int colorId; + if (speedExceed) { + colorId = lastNightMode ? R.color.speedometer_text_speed_exceed_night : R.color.speedometer_text_speed_exceed_day; + } else { + colorId = lastNightMode ? R.color.widgettext_night : R.color.widgettext_day; + } + return app.getColor(colorId); } @NonNull diff --git a/OsmAnd/src/net/osmand/plus/wikipedia/WikiAlgorithms.java b/OsmAnd/src/net/osmand/plus/wikipedia/WikiAlgorithms.java index 8042bf07c8d..8bf1ab88bad 100644 --- a/OsmAnd/src/net/osmand/plus/wikipedia/WikiAlgorithms.java +++ b/OsmAnd/src/net/osmand/plus/wikipedia/WikiAlgorithms.java @@ -72,10 +72,9 @@ public static Pair getWikiParams(String key, String value) { return new Pair<>(text, url); } - @Nullable public static String formatWikiDate(@Nullable String date) { - if (date == null) { - return null; + if (Algorithms.isEmpty(date)) { + return ""; } String cleanDate = date.startsWith("+") ? date.substring(1) : date; cleanDate = cleanDate.endsWith("Z") ? cleanDate.substring(0, cleanDate.length() - 1) : cleanDate; diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelArticle.java b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelArticle.java index 0272ad2f932..218a591c8a3 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelArticle.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelArticle.java @@ -127,6 +127,27 @@ public String getRouteId() { return routeId; } + public boolean hasOsmRouteId() { + String routeId = getRouteId(); + return routeId != null && routeId.startsWith(Amenity.ROUTE_ID_OSM_PREFIX); + } + + @NonNull + public String getGpxFileName() { + String gpxFileName = !Algorithms.isEmpty(title) ? title : routeId; + if (gpxFileName != null) { + return gpxFileName + .replace('/', '_') + .replace('\'', '_') + .replace('\"', '_') + .replace('\r', '_') + .replace('\n', '_'); + } else { + LOG.error("Empty travel article in " + this.file); + return "Travel Article File"; // @NonNull + } + } + public String getRouteSource() { return routeSource; } diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelDbHelper.java b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelDbHelper.java index 6d4d4d40df7..efd1655a980 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelDbHelper.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelDbHelper.java @@ -1,5 +1,7 @@ package net.osmand.plus.wikivoyage.data; +import static net.osmand.IndexConstants.GPX_FILE_EXT; + import android.text.TextUtils; import androidx.annotation.NonNull; @@ -203,7 +205,6 @@ public void saveOrRemoveArticle(@NonNull TravelArticle article, boolean save) { } } - public List getExistingTravelBooks() { return existingTravelBooks; } @@ -713,6 +714,11 @@ public String formatTravelBookName(File tb) { return nm.substring(0, nm.indexOf('.')).replace('_', ' '); } + @Override + public boolean isTravelGpxTags(@NonNull Map tags) { + return false; // stub + } + @Nullable @Override public TravelGpx searchGpx(@NonNull LatLon location, @Nullable String fileName, @Nullable String ref) { @@ -721,15 +727,13 @@ public TravelGpx searchGpx(@NonNull LatLon location, @Nullable String fileName, @Override public void openTrackMenu(@NonNull TravelArticle article, @NonNull MapActivity mapActivity, - @NonNull String gpxFileName, @NonNull LatLon location) { - + @NonNull String gpxFileName, @NonNull LatLon location, boolean adjustMapPosition) { } @NonNull @Override public String getGPXName(@NonNull TravelArticle article) { - return article.getTitle().replace('/', '_').replace('\'', '_') - .replace('\"', '_') + IndexConstants.GPX_FILE_EXT; + return article.getGpxFileName() + GPX_FILE_EXT; } @NonNull diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelGpx.java b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelGpx.java index a724a764404..9f3e8cf3596 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelGpx.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelGpx.java @@ -1,6 +1,7 @@ package net.osmand.plus.wikivoyage.data; import static net.osmand.osm.MapPoiTypes.ROUTE_TRACK; +import static net.osmand.plus.wikivoyage.data.TravelObfHelper.WPT_EXTRA_TAGS; import static net.osmand.shared.gpx.GpxUtilities.POINT_ELEVATION; import net.osmand.shared.gpx.primitives.WptPt; @@ -11,11 +12,15 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + import net.osmand.data.Amenity; import net.osmand.util.Algorithms; -import java.util.HashMap; +import java.lang.reflect.Type; import java.util.Map; +import java.util.Set; import net.osmand.shared.gpx.GpxTrackAnalysis; @@ -27,9 +32,13 @@ public class TravelGpx extends TravelArticle { public static final String MAX_ELEVATION = "max_ele"; public static final String MIN_ELEVATION = "min_ele"; public static final String AVERAGE_ELEVATION = "avg_ele"; - public static final String ROUTE_RADIUS = "route_radius"; + public static final String START_ELEVATION = "start_ele"; + public static final String ELE_GRAPH = "ele_graph"; + public static final String ROUTE_BBOX_RADIUS = "route_bbox_radius"; public static final String USER = "user"; - public static final String ACTIVITY_TYPE = "route_activity_type"; + public static final String ROUTE_TYPE = "route_type"; + public static final String ROUTE_ACTIVITY_TYPE = "route_activity_type"; + public static final String TRAVEL_MAP_TO_POI_TAG = "route_id"; public String user; public String activityType; @@ -62,6 +71,8 @@ public GpxTrackAnalysis getAnalysis() { return analysis; } + private static final Set doNotSaveWptTags = Set.of("route_id", "route_name"); + @NonNull @Override public WptPt createWptPt(@NonNull Amenity amenity, @Nullable String lang) { @@ -72,26 +83,28 @@ public WptPt createWptPt(@NonNull Amenity amenity, @Nullable String lang) { for (String obfTag : amenity.getAdditionalInfoKeys()) { String value = amenity.getAdditionalInfo(obfTag); if (!Algorithms.isEmpty(value)) { - String gpxTag = allowedPointObfToGpxTags.get(obfTag); - if (gpxTag != null) { - wptPt.getExtensionsToWrite().put(gpxTag, value); - } if (OBF_POINTS_GROUPS_CATEGORY.equals(obfTag)) { wptPt.setCategory(value); + } else if ("name".equals(obfTag)) { + wptPt.setName(value); + } else if ("description".equals(obfTag)) { + wptPt.setDesc(value); + } else if ("note".equals(obfTag)) { + wptPt.setComment(value); + } else if ("colour".equals(obfTag) && amenity.getAdditionalInfoKeys().contains("color")) { + // ignore "colour" if "color" exists + } else if (WPT_EXTRA_TAGS.equals(obfTag)) { + Gson gson = new Gson(); + Type type = new TypeToken>() {}.getType(); + wptPt.getExtensionsToWrite().putAll(gson.fromJson(value, type)); + } else if (!doNotSaveWptTags.contains(obfTag)) { + wptPt.getExtensionsToWrite().put(obfTag, value); } } } return wptPt; } - private final static Map allowedPointObfToGpxTags = new HashMap<>(); - - static { - allowedPointObfToGpxTags.put("color", "color"); - allowedPointObfToGpxTags.put("gpx_icon", "icon"); - allowedPointObfToGpxTags.put("gpx_bg", "background"); - } - @NonNull @Override public String getPointFilterString() { @@ -101,6 +114,6 @@ public String getPointFilterString() { @NonNull @Override public String getMainFilterString() { - return ROUTE_TRACK; + return ROUTE_TRACK; // considered together with ROUTES_PREFIX } } diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelHelper.java b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelHelper.java index 91ab8552c18..7c7c948ce4c 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelHelper.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelHelper.java @@ -61,11 +61,13 @@ interface GpxReadCallback { @NonNull ArrayList getArticleLangs(@NonNull TravelArticleIdentifier articleId); + boolean isTravelGpxTags(@NonNull Map tags); + @Nullable TravelGpx searchGpx(@NonNull LatLon location, @Nullable String fileName, @Nullable String ref); void openTrackMenu(@NonNull TravelArticle article, @NonNull MapActivity mapActivity, - @NonNull String gpxFileName, @NonNull LatLon location); + @NonNull String gpxFileName, @NonNull LatLon location, boolean adjustMapPosition); @NonNull String getGPXName(@NonNull TravelArticle article); @@ -80,5 +82,4 @@ void openTrackMenu(@NonNull TravelArticle article, @NonNull MapActivity mapActiv String getWikivoyageFileName(); void saveOrRemoveArticle(@NonNull TravelArticle article, boolean save); - } diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelObfHelper.java b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelObfHelper.java index ded6432c219..39b01ab0b4c 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelObfHelper.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelObfHelper.java @@ -2,23 +2,29 @@ import static net.osmand.IndexConstants.GPX_FILE_EXT; import static net.osmand.data.Amenity.REF; +import static net.osmand.data.Amenity.ROUTE; import static net.osmand.data.Amenity.ROUTE_ID; +import static net.osmand.osm.MapPoiTypes.ROUTES_PREFIX; import static net.osmand.osm.MapPoiTypes.ROUTE_ARTICLE; import static net.osmand.osm.MapPoiTypes.ROUTE_TRACK; import static net.osmand.osm.MapPoiTypes.ROUTE_TRACK_POINT; import static net.osmand.plus.wikivoyage.data.PopularArticles.ARTICLES_PER_PAGE; -import static net.osmand.plus.wikivoyage.data.TravelGpx.ACTIVITY_TYPE; +import static net.osmand.plus.wikivoyage.data.TravelGpx.ROUTE_ACTIVITY_TYPE; import static net.osmand.plus.wikivoyage.data.TravelGpx.AVERAGE_ELEVATION; import static net.osmand.plus.wikivoyage.data.TravelGpx.DIFF_ELEVATION_DOWN; import static net.osmand.plus.wikivoyage.data.TravelGpx.DIFF_ELEVATION_UP; import static net.osmand.plus.wikivoyage.data.TravelGpx.DISTANCE; +import static net.osmand.plus.wikivoyage.data.TravelGpx.ELE_GRAPH; import static net.osmand.plus.wikivoyage.data.TravelGpx.MAX_ELEVATION; import static net.osmand.plus.wikivoyage.data.TravelGpx.MIN_ELEVATION; -import static net.osmand.plus.wikivoyage.data.TravelGpx.ROUTE_RADIUS; +import static net.osmand.plus.wikivoyage.data.TravelGpx.ROUTE_BBOX_RADIUS; +import static net.osmand.plus.wikivoyage.data.TravelGpx.ROUTE_TYPE; +import static net.osmand.plus.wikivoyage.data.TravelGpx.START_ELEVATION; import static net.osmand.plus.wikivoyage.data.TravelGpx.USER; import static net.osmand.shared.gpx.GpxUtilities.PointsGroup.OBF_POINTS_GROUPS_BACKGROUNDS; import static net.osmand.shared.gpx.GpxUtilities.PointsGroup.OBF_POINTS_GROUPS_COLORS; import static net.osmand.shared.gpx.GpxUtilities.PointsGroup.OBF_POINTS_GROUPS_DELIMITER; +import static net.osmand.shared.gpx.GpxUtilities.PointsGroup.OBF_POINTS_GROUPS_EMPTY_NAME_STUB; import static net.osmand.shared.gpx.GpxUtilities.PointsGroup.OBF_POINTS_GROUPS_ICONS; import static net.osmand.shared.gpx.GpxUtilities.PointsGroup.OBF_POINTS_GROUPS_NAMES; import static net.osmand.shared.gpx.GpxUtilities.PointsGroup.OBF_POINTS_GROUPS_PREFIX; @@ -26,8 +32,6 @@ import static net.osmand.shared.gpx.GpxUtilities.TRAVEL_GPX_CONVERT_FIRST_LETTER; import static net.osmand.shared.gpx.GpxUtilities.TRAVEL_GPX_CONVERT_MULT_1; import static net.osmand.shared.gpx.GpxUtilities.TRAVEL_GPX_CONVERT_MULT_2; -import static net.osmand.shared.gpx.primitives.GpxExtensions.OBF_GPX_EXTENSION_TAG_PREFIX; -import static net.osmand.util.Algorithms.capitalizeFirstLetter; import android.os.AsyncTask; import android.text.TextUtils; @@ -36,12 +40,16 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + import net.osmand.Collator; import net.osmand.binary.BinaryMapPoiReaderAdapter; import net.osmand.IndexConstants; import net.osmand.OsmAndCollator; import net.osmand.PlatformUtil; import net.osmand.ResultMatcher; +import net.osmand.plus.Version; import net.osmand.plus.shared.SharedUtil; import net.osmand.binary.BinaryMapDataObject; import net.osmand.binary.BinaryMapIndexReader; @@ -62,8 +70,8 @@ import net.osmand.search.core.SearchPhrase.NameStringMatcher; import net.osmand.search.core.SearchSettings; import net.osmand.shared.gpx.GpxFile; -import net.osmand.shared.gpx.GpxHelper; import net.osmand.shared.gpx.GpxUtilities; +import net.osmand.shared.gpx.RouteActivityHelper; import net.osmand.shared.gpx.primitives.Track; import net.osmand.shared.gpx.primitives.TrkSegment; import net.osmand.shared.gpx.primitives.WptPt; @@ -76,6 +84,7 @@ import java.io.File; import java.io.IOException; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -88,7 +97,9 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; +import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import gnu.trove.set.TLongSet; @@ -113,6 +124,16 @@ public class TravelObfHelper implements TravelHelper { private final List> foundAmenities = new ArrayList<>(); public volatile int requestNumber = 0; + // Do not clutter GPX with tags that are always generated. + private static final Set doNotSaveAmenityGpxTags = Set.of( + "date", "distance", "route_name", "route_bbox_radius", + "avg_ele", "min_ele", "max_ele", "start_ele", "ele_graph", "diff_ele_up", "diff_ele_down", + "avg_speed", "min_speed", "max_speed", "time_moving", "time_moving_no_gaps", "time_span", "time_span_no_gaps" + ); + + public static final String WPT_EXTRA_TAGS = "wpt_extra_tags"; + private static final String METADATA_EXTRA_TAGS = "metadata_extra_tags"; + private static final String EXTENSIONS_EXTRA_TAGS = "extensions_extra_tags"; public TravelObfHelper(OsmandApplication app) { this.app = app; @@ -206,8 +227,15 @@ public int compare(Pair article1, Pair article2) { return popularArticles; } + @Override + public boolean isTravelGpxTags(@NonNull Map tags) { + return tags.containsKey(ROUTE_ID) && "segment".equals(tags.get(ROUTE)); + } + @Nullable public synchronized TravelGpx searchGpx(@NonNull LatLon location, @Nullable String filter, @Nullable String ref) { + final String lcFilter = filter != null ? filter.toLowerCase() : null; + final String lcSearchRef = ref != null ? ref.toLowerCase() : null; List> foundAmenities = new ArrayList<>(); int searchRadius = ARTICLE_SEARCH_RADIUS; TravelGpx travelGpx = null; @@ -221,15 +249,23 @@ public synchronized TravelGpx searchGpx(@NonNull LatLon location, @Nullable Stri } for (Pair foundGpx : foundAmenities) { Amenity amenity = foundGpx.second; - if ((Algorithms.objectEquals(amenity.getRouteId(), filter) - || Algorithms.objectEquals(amenity.getName(), filter)) - && Algorithms.objectEquals(amenity.getRef(), ref)) { + final String aRef = amenity.getRef(); + final String aName = amenity.getName(); + final String aRouteId = amenity.getRouteId(); + final String lcRef = aRef != null ? aRef.toLowerCase() : null; + final String lcName = aName != null ? aName.toLowerCase() : null; + final String lcRouteId = aRouteId != null ? aRouteId.toLowerCase() : null; + if ((Algorithms.objectEquals(lcRouteId, lcFilter) || Algorithms.objectEquals(lcName, lcFilter)) + && (lcSearchRef == null || Algorithms.objectEquals(lcRef, lcSearchRef))) { travelGpx = getTravelGpx(foundGpx.first, amenity); break; } } searchRadius *= 2; } while (travelGpx == null && searchRadius < MAX_SEARCH_RADIUS); + if (travelGpx == null) { + LOG.error(String.format("searchGpx(%s, %s, %s) failed", location, filter, ref)); + } return travelGpx; } @@ -257,7 +293,7 @@ public boolean isCancelled() { private TravelArticle cacheTravelArticles(File file, Amenity amenity, String lang, boolean readPoints, @Nullable GpxReadCallback callback) { TravelArticle article = null; Map articles; - if (ROUTE_TRACK.equals(amenity.getSubType())) { + if (amenity.isRouteTrack()) { articles = readRoutePoint(file, amenity); } else { articles = readArticles(file, amenity); @@ -282,41 +318,40 @@ private TravelGpx getTravelGpx(File file, Amenity amenity) { TravelGpx travelGpx = new TravelGpx(); travelGpx.file = file; String title = amenity.getName("en"); - travelGpx.title = createTitle(Algorithms.isEmpty(title) ? amenity.getName() : title); + travelGpx.title = Algorithms.isEmpty(title) ? amenity.getName() : title; travelGpx.lat = amenity.getLocation().getLatitude(); travelGpx.lon = amenity.getLocation().getLongitude(); travelGpx.description = Algorithms.emptyIfNull(amenity.getTagContent(Amenity.DESCRIPTION)); travelGpx.routeId = Algorithms.emptyIfNull(amenity.getTagContent(Amenity.ROUTE_ID)); travelGpx.user = Algorithms.emptyIfNull(amenity.getTagContent(USER)); - travelGpx.activityType = Algorithms.emptyIfNull(amenity.getTagContent(ACTIVITY_TYPE)); + travelGpx.activityType = Algorithms.emptyIfNull(amenity.getTagContent(ROUTE_ACTIVITY_TYPE)); travelGpx.ref = Algorithms.emptyIfNull(amenity.getRef()); - try { - travelGpx.totalDistance = Float.parseFloat(Algorithms.emptyIfNull(amenity.getTagContent(DISTANCE))); - travelGpx.diffElevationUp = Double.parseDouble(Algorithms.emptyIfNull(amenity.getTagContent(DIFF_ELEVATION_UP))); - travelGpx.diffElevationDown = Double.parseDouble(Algorithms.emptyIfNull(amenity.getTagContent(DIFF_ELEVATION_DOWN))); - travelGpx.maxElevation = Double.parseDouble(Algorithms.emptyIfNull(amenity.getTagContent(MAX_ELEVATION))); - travelGpx.minElevation = Double.parseDouble(Algorithms.emptyIfNull(amenity.getTagContent(MIN_ELEVATION))); - travelGpx.avgElevation = Double.parseDouble(Algorithms.emptyIfNull(amenity.getTagContent(AVERAGE_ELEVATION))); - String radius = amenity.getTagContent(ROUTE_RADIUS); - if (radius != null) { - travelGpx.routeRadius = MapUtils.convertCharToDist(radius.charAt(0), TRAVEL_GPX_CONVERT_FIRST_LETTER, - TRAVEL_GPX_CONVERT_FIRST_DIST, TRAVEL_GPX_CONVERT_MULT_1, TRAVEL_GPX_CONVERT_MULT_2); - } - } catch (NumberFormatException e) { - LOG.debug(e.getMessage(), e); + travelGpx.totalDistance = Algorithms.parseFloatSilently(amenity.getTagContent(DISTANCE), 0); + travelGpx.diffElevationUp = Algorithms.parseDoubleSilently(amenity.getTagContent(DIFF_ELEVATION_UP), 0); + travelGpx.diffElevationDown = Algorithms.parseDoubleSilently(amenity.getTagContent(DIFF_ELEVATION_DOWN), 0); + travelGpx.minElevation = Algorithms.parseDoubleSilently(amenity.getTagContent(MIN_ELEVATION), 0); + travelGpx.avgElevation = Algorithms.parseDoubleSilently(amenity.getTagContent(AVERAGE_ELEVATION), 0); + travelGpx.maxElevation = Algorithms.parseDoubleSilently(amenity.getTagContent(MAX_ELEVATION), 0); + String radius = amenity.getTagContent(ROUTE_BBOX_RADIUS); + if (radius != null) { + travelGpx.routeRadius = MapUtils.convertCharToDist(radius.charAt(0), TRAVEL_GPX_CONVERT_FIRST_LETTER, + TRAVEL_GPX_CONVERT_FIRST_DIST, TRAVEL_GPX_CONVERT_MULT_1, TRAVEL_GPX_CONVERT_MULT_2); } return travelGpx; } @NonNull - public static SearchPoiTypeFilter getSearchFilter(String... filterSubcategory) { + public static SearchPoiTypeFilter getSearchFilter(String... filterSubcategories) { return new SearchPoiTypeFilter() { @Override public boolean accept(PoiCategory type, String subcategory) { - for (String filterSubcategory : filterSubcategory) { - if (subcategory.equals(filterSubcategory)) { + for (String filter : filterSubcategories) { + if (subcategory.equals(filter)) { return true; } + if (ROUTE_TRACK.equals(filter) && subcategory.startsWith(ROUTES_PREFIX)) { + return true; // include routes:routes_xxx with routes:route_track filter + } } return false; } @@ -694,7 +729,7 @@ private TravelArticle getCachedArticle(@NonNull TravelArticleIdentifier articleI @Override public void openTrackMenu(@NonNull TravelArticle article, @NonNull MapActivity mapActivity, - @NonNull String gpxFileName, @NonNull LatLon latLon) { + @NonNull String gpxFileName, @NonNull LatLon latLon, boolean adjustMapPosition) { GpxReadCallback callback = new GpxReadCallback() { @Override public void onGpxFileReading() { @@ -709,7 +744,7 @@ public void onGpxFileRead(@Nullable GpxFile gpxFile) { String name = gpxFileName.endsWith(GPX_FILE_EXT) ? gpxFileName : gpxFileName + GPX_FILE_EXT; File file = new File(FileUtils.getTempDir(app), name); - GpxUiHelper.saveAndOpenGpx(mapActivity, file, gpxFile, wptPt, article.getAnalysis(), null); + GpxUiHelper.saveAndOpenGpx(mapActivity, file, gpxFile, wptPt, article.getAnalysis(), null, adjustMapPosition); } } }; @@ -1037,8 +1072,7 @@ public ArrayList getArticleLangs(@NonNull TravelArticleIdentifier articl @NonNull @Override public String getGPXName(@NonNull TravelArticle article) { - return article.getTitle().replace('/', '_').replace('\'', '_') - .replace('\"', '_') + GPX_FILE_EXT; + return article.getGpxFileName() + GPX_FILE_EXT; } @NonNull @@ -1070,15 +1104,10 @@ public void saveOrRemoveArticle(@NonNull TravelArticle article, boolean save) { } } - @Nullable - private synchronized GpxFile buildGpxFile(@NonNull List readers, TravelArticle article) { - List segmentList = new ArrayList<>(); - Map gpxFileExtensions = new HashMap<>(); - List pointList = new ArrayList<>(); - List pgNames = new ArrayList<>(); - List pgIcons = new ArrayList<>(); - List pgColors = new ArrayList<>(); - List pgBackgrounds = new ArrayList<>(); + private void fetchSegmentsAndPoints(List readers, TravelArticle article, + List segmentList, List pointList, + Map gpxFileExtensions, List pgNames, + List pgIcons, List pgColors, List pgBackgrounds) { for (BinaryMapIndexReader reader : readers) { try { if (article.file != null && !article.file.equals(reader.getFile())) { @@ -1087,75 +1116,17 @@ private synchronized GpxFile buildGpxFile(@NonNull List re if (article instanceof TravelGpx) { BinaryMapIndexReader.SearchRequest sr = BinaryMapIndexReader.buildSearchRequest( 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, 15, null, - new ResultMatcher() { - @Override - public boolean publish(BinaryMapDataObject object) { - if (object.getPointsLength() > 1) { - if (object.getTagValue(REF).equals(article.ref) - && (object.getTagValue(ROUTE_ID).equals(article.routeId) - || createTitle(object.getName()).equals(article.getTitle()))) { - segmentList.add(object); - } - } - return false; - } - - @Override - public boolean isCancelled() { - return false; - } - }); + matchSegmentsByRefTitleRouteId(article, segmentList)); if (article.routeRadius >= 0) { sr.setBBoxRadius(article.lat, article.lon, article.routeRadius); } reader.searchMapIndex(sr); } - BinaryMapIndexReader.SearchRequest pointRequest = BinaryMapIndexReader.buildSearchPoiRequest( 0, 0, Algorithms.emptyIfNull(article.title), 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, getSearchFilter(article.getMainFilterString(), article.getPointFilterString()), - new ResultMatcher() { - @Override - public boolean publish(Amenity amenity) { - if (amenity.getRouteId().equals(article.getRouteId())) { - if (ROUTE_TRACK.equals(amenity.getSubType())) { - for (String key : amenity.getAdditionalInfoKeys()) { - if (key.startsWith(OBF_GPX_EXTENSION_TAG_PREFIX)) { - String tag = key.replaceFirst(OBF_GPX_EXTENSION_TAG_PREFIX, ""); - String val = amenity.getAdditionalInfo(key); - gpxFileExtensions.put(tag, val); - } else if (key.startsWith(OBF_POINTS_GROUPS_PREFIX)) { - final String delimiter = OBF_POINTS_GROUPS_DELIMITER; - String joinedValues = amenity.getAdditionalInfo(key); - List values = Arrays.asList(joinedValues.split(delimiter)); - if (OBF_POINTS_GROUPS_NAMES.equals(key)) { - pgNames.addAll(values); - } else if (OBF_POINTS_GROUPS_ICONS.equals(key)) { - pgIcons.addAll(values); - } else if (OBF_POINTS_GROUPS_COLORS.equals(key)) { - pgColors.addAll(values); - } else if (OBF_POINTS_GROUPS_BACKGROUNDS.equals(key)) { - pgBackgrounds.addAll(values); - } - } - } - } else if (ROUTE_TRACK_POINT.equals(amenity.getSubType())) { - pointList.add(amenity); - } else { - String amenityLang = amenity.getTagSuffix(Amenity.LANG_YES + ":"); - if (Algorithms.stringsEqual(article.lang, amenityLang)) { - pointList.add(amenity); - } - } - } - return false; - } - - @Override - public boolean isCancelled() { - return false; - } - }, null); + matchPointsAndTags(article, pointList, gpxFileExtensions, pgNames, pgIcons, pgColors, pgBackgrounds), + null); if (article.routeRadius >= 0) { pointRequest.setBBoxRadius(article.lat, article.lon, article.routeRadius); } @@ -1171,9 +1142,135 @@ public boolean isCancelled() { LOG.error(e.getMessage()); } } - GpxFile gpxFile = null; - String description = article.getDescription(); - String title = FileUtils.isValidFileName(description) ? description : article.getTitle(); + } + + @NonNull + private ResultMatcher matchPointsAndTags(TravelArticle article, List pointList, Map gpxFileExtensions, List pgNames, List pgIcons, List pgColors, List pgBackgrounds) { + return new ResultMatcher() { + boolean isAlreadyProcessed = false; + @Override + public boolean publish(Amenity amenity) { + if (amenity.getRouteId().equals(article.getRouteId())) { + if (amenity.isRouteTrack()) { + if (!isAlreadyProcessed) { + isAlreadyProcessed = true; + reconstructGpxTagsFromAmenityType(amenity, gpxFileExtensions); + for (String tag : amenity.getAdditionalInfoKeys()) { + String value = amenity.getAdditionalInfo(tag); + if (tag.startsWith(OBF_POINTS_GROUPS_PREFIX)) { + final String delimiter = OBF_POINTS_GROUPS_DELIMITER; + List values = Arrays.asList(value.split(delimiter)); + if (OBF_POINTS_GROUPS_NAMES.equals(tag)) { + pgNames.addAll(values); + } else if (OBF_POINTS_GROUPS_ICONS.equals(tag)) { + pgIcons.addAll(values); + } else if (OBF_POINTS_GROUPS_COLORS.equals(tag)) { + pgColors.addAll(values); + } else if (OBF_POINTS_GROUPS_BACKGROUNDS.equals(tag)) { + pgBackgrounds.addAll(values); + } + } else if (!doNotSaveAmenityGpxTags.contains(tag)) { + gpxFileExtensions.put(tag, value); + } + } + } + } else if (ROUTE_TRACK_POINT.equals(amenity.getSubType())) { + pointList.add(amenity); + } else { + String amenityLang = amenity.getTagSuffix(Amenity.LANG_YES + ":"); + if (Algorithms.stringsEqual(article.lang, amenityLang)) { + pointList.add(amenity); + } + } + } + return false; + } + @Override + public boolean isCancelled() { + return false; + } + }; + } + + private void reconstructGpxTagsFromAmenityType(Amenity amenity, Map gpxFileExtensions) { + if (amenity.isRouteTrack() && amenity.getSubType() != null) { + String subType = amenity.getSubType(); + if (subType.startsWith(ROUTES_PREFIX)) { + String osmValue = amenity.getType().getPoiTypeByKeyName(subType).getOsmValue(); + if (!Algorithms.isEmpty(osmValue)) { + if (amenity.hasOsmRouteId() || !"other".equals(osmValue)) { + gpxFileExtensions.put(ROUTE_TYPE, osmValue); // do not litter gpx with default route_type + } + RouteActivityHelper helper = app.getRouteActivityHelper(); + for (String key : amenity.getAdditionalInfoKeys()) { + if (key.startsWith(ROUTE_ACTIVITY_TYPE + "_")) { + String activityType = amenity.getAdditionalInfo(key); + if (!activityType.isEmpty() && helper.findRouteActivity(activityType) != null) { + gpxFileExtensions.put(GpxUtilities.ACTIVITY_TYPE, activityType); // osmand:activity in gpx + break; + } + } + } + } + } + } + } + + @NonNull + private ResultMatcher matchSegmentsByRefTitleRouteId( + TravelArticle article, List segmentList) { + return new ResultMatcher() { + @Override + public boolean publish(BinaryMapDataObject object) { + if (object.getPointsLength() > 1) { + if (object.getTagValue(REF).equals(article.ref) + && (object.getTagValue(ROUTE_ID).equals(article.routeId) + || object.getName().equals(article.getTitle()))) { + segmentList.add(object); + } + } + return false; + } + @Override + public boolean isCancelled() { + return false; + } + }; + } + + @Nullable + private synchronized GpxFile buildGpxFile(@NonNull List readers, TravelArticle article) { + List segmentList = new ArrayList<>(); + Map gpxFileExtensions = new TreeMap<>(); + List pointList = new ArrayList<>(); + List pgNames = new ArrayList<>(); + List pgIcons = new ArrayList<>(); + List pgColors = new ArrayList<>(); + List pgBackgrounds = new ArrayList<>(); + + fetchSegmentsAndPoints(readers, article, segmentList, pointList, gpxFileExtensions, + pgNames, pgIcons, pgColors, pgBackgrounds); + + GpxFile gpxFile; + if (article instanceof TravelGpx) { + gpxFile = new GpxFile(Version.getFullVersion(app)); + gpxFile.getMetadata().setName(Objects.requireNonNullElse(article.title, article.routeId)); // path is name + if (!Algorithms.isEmpty(article.title) && article.hasOsmRouteId()) { + gpxFileExtensions.putIfAbsent("name", article.title); + } + if (!Algorithms.isEmpty(article.description)) { + gpxFile.getMetadata().setDesc(article.description); + } + } else { + String description = article.getDescription(); + String title = FileUtils.isValidFileName(description) ? description : article.getTitle(); + gpxFile = new GpxFile(title, article.getLang(), article.getContent()); + } + + if (!Algorithms.isEmpty(article.getImageTitle())) { + gpxFile.getMetadata().setLink(TravelArticle.getImageUrl(article.getImageTitle(), false)); + } + if (!segmentList.isEmpty()) { boolean hasAltitude = false; Track track = new Track(); @@ -1185,38 +1282,49 @@ public boolean isCancelled() { point.setLon(MapUtils.get31LongitudeX(segment.getPoint31XTile(i))); trkSegment.getPoints().add(point); } - String ele_graph = segment.getTagValue("ele_graph"); + String ele_graph = segment.getTagValue(ELE_GRAPH); if (!Algorithms.isEmpty(ele_graph)) { hasAltitude = true; List heightRes = KMapAlgorithms.INSTANCE.decodeIntHeightArrayGraph(ele_graph, 3); - double startEle = 0; - try { - startEle = Double.parseDouble(segment.getTagValue("start_ele")); - } catch (NumberFormatException e) { - LOG.debug(e.getMessage(), e); - } + double startEle = Algorithms.parseDoubleSilently(segment.getTagValue(START_ELEVATION), 0); KMapAlgorithms.INSTANCE.augmentTrkSegmentWithAltitudes(trkSegment, heightRes, startEle); } track.getSegments().add(trkSegment); } - gpxFile = new GpxFile(title, article.getLang(), article.getContent()); - if (!Algorithms.isEmpty(article.getImageTitle())) { - gpxFile.getMetadata().setLink(TravelArticle.getImageUrl(article.getImageTitle(), false)); - } gpxFile.setTracks(new ArrayList<>()); gpxFile.getTracks().add(track); - gpxFile.setRef(article.ref); + if (!(article instanceof TravelGpx)) { + gpxFile.setRef(article.ref); + } gpxFile.setHasAltitude(hasAltitude); - gpxFile.getExtensionsToWrite().putAll(gpxFileExtensions); + if (gpxFileExtensions.containsKey(GpxUtilities.ACTIVITY_TYPE)) { + final String activityType = gpxFileExtensions.get(GpxUtilities.ACTIVITY_TYPE); + gpxFile.getMetadata().getExtensionsToWrite().put(GpxUtilities.ACTIVITY_TYPE, activityType); + + // cleanup type and activity tags + gpxFileExtensions.remove(ROUTE_TYPE); + gpxFileExtensions.remove(ROUTE_ACTIVITY_TYPE); + gpxFileExtensions.remove(ROUTE_ACTIVITY_TYPE + "_" + activityType); + gpxFileExtensions.remove(GpxUtilities.ACTIVITY_TYPE); // moved to the metadata + } + + Gson gson = new Gson(); + Type type = new TypeToken>() {}.getType(); + if (gpxFileExtensions.containsKey(EXTENSIONS_EXTRA_TAGS)) { + gpxFile.getExtensionsToWrite() + .putAll(gson.fromJson(gpxFileExtensions.get(EXTENSIONS_EXTRA_TAGS), type)); + gpxFileExtensions.remove(EXTENSIONS_EXTRA_TAGS); + } + if (gpxFileExtensions.containsKey(METADATA_EXTRA_TAGS)) { + gpxFile.getMetadata().getExtensionsToWrite() + .putAll(gson.fromJson(gpxFileExtensions.get(METADATA_EXTRA_TAGS), type)); + gpxFileExtensions.remove(METADATA_EXTRA_TAGS); + } + + gpxFile.getExtensionsToWrite().putAll(gpxFileExtensions); // finally } reconstructPointsGroups(gpxFile, pgNames, pgIcons, pgColors, pgBackgrounds); // create groups before points if (!pointList.isEmpty()) { - if (gpxFile == null) { - gpxFile = new GpxFile(title, article.getLang(), article.getContent()); - if (!Algorithms.isEmpty(article.getImageTitle())) { - gpxFile.getMetadata().setLink(TravelArticle.getImageUrl(article.getImageTitle(), false)); - } - } for (Amenity wayPoint : pointList) { gpxFile.addPoint(article.createWptPt(wayPoint, article.getLang())); } @@ -1234,18 +1342,15 @@ private void reconstructPointsGroups(GpxFile gpxFile, List pgNames, List String icon = pgIcons.get(i); String background = pgBackgrounds.get(i); int color = KAlgorithms.INSTANCE.parseColor(pgColors.get(i)); - if (name.isEmpty()) name = GpxFile.DEFAULT_WPT_GROUP_NAME; // follow current default + if (name.isEmpty() || OBF_POINTS_GROUPS_EMPTY_NAME_STUB.equals(name)) { + name = GpxFile.DEFAULT_WPT_GROUP_NAME; // follow current default + } GpxUtilities.PointsGroup pg = new GpxUtilities.PointsGroup(name, icon, background, color); gpxFile.addPointsGroup(pg); } } } - @NonNull - public String createTitle(@NonNull String name) { - return capitalizeFirstLetter(GpxHelper.INSTANCE.getGpxTitle(name)); - } - private class GpxFileReader extends AsyncTask { private final TravelArticle article;