receivers = pm.queryBroadcastReceivers(outIntent, 0);
+ if (receivers != null)
+ for (ResolveInfo receiver : receivers) {
+ if ((packageName != null) && !packageName.equals(receiver.activityInfo.applicationInfo.packageName))
+ continue;
+ ComponentName cn = new ComponentName(receiver.activityInfo.applicationInfo.packageName,
+ receiver.activityInfo.name);
+ outIntent = new Intent(action);
+ if (data != null)
+ outIntent.setData(data);
+ if (extras != null)
+ outIntent.putExtras(extras);
+ outIntent.setComponent(cn);
+ context.sendOrderedBroadcast (outIntent,
+ receiverPermission,
+ resultReceiver,
+ null, // scheduler,
+ AndroidTransport.RESULT_INTERNAL_ERROR, // initialCode,
+ null, // initialData,
+ null);
+ }
+ }
+}
diff --git a/android/sdk/src/main/java/app/organicmaps/sdk/traffxml/AndroidTransport.java b/android/sdk/src/main/java/app/organicmaps/sdk/traffxml/AndroidTransport.java
new file mode 100644
index 000000000..93c1f4b0c
--- /dev/null
+++ b/android/sdk/src/main/java/app/organicmaps/sdk/traffxml/AndroidTransport.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright © 2019–2020 traffxml.org.
+ *
+ * Relicensed to CoMaps by the original author.
+ */
+
+package app.organicmaps.sdk.traffxml;
+
+public class AndroidTransport {
+ /**
+ * Intent to poll a peer for its capabilities.
+ *
+ * This is a broadcast intent and must be sent as an explicit broadcast.
+ */
+ public static final String ACTION_TRAFF_GET_CAPABILITIES = "org.traffxml.traff.GET_CAPABILITIES";
+
+ /**
+ * Intent to send a heartbeat to a peer.
+ *
+ *
This is a broadcast intent and must be sent as an explicit broadcast.
+ */
+ public static final String ACTION_TRAFF_HEARTBEAT = "org.traffxml.traff.GET_HEARTBEAT";
+
+ /**
+ * Intent to poll a source for information.
+ *
+ *
This is a broadcast intent and must be sent as an explicit broadcast.
+ *
+ *
Polling is a legacy feature on Android and deprecated in TraFF 0.8 (rather than polling, TraFF 0.8
+ * applications query the content provider). Therefore, poll operations are subscriptionless, and the
+ * source should either reply with all messages it currently holds, or ignore the request.
+ */
+ @Deprecated
+ public static final String ACTION_TRAFF_POLL = "org.traffxml.traff.POLL";
+
+ /**
+ * Intent for a push feed.
+ *
+ *
This is a broadcast intent. It can be used in different forms:
+ *
+ *
As of TraFF 0.8, it must be sent as an explicit broadcast and include the
+ * {@link #EXTRA_SUBSCRIPTION_ID} extra. The intent data must be a URI to the content provider from which
+ * the messages can be retrieved. The {@link #EXTRA_FEED} extra is not supported. The feed is part of a
+ * subscription and will contain only changes over feeds sent previously as part of the same
+ * subscription.
+ *
+ *
Legacy applications omit the {@link #EXTRA_SUBSCRIPTION_ID} extra and may send it as an implicit
+ * broadcast. If an application supports both legacy transport and TraFF 0.8 or later, it must include
+ * the {@link #EXTRA_PACKAGE} extra. The feed is sent in the {@link #EXTRA_FEED} extra, as legacy
+ * applications may not support content providers. If sent as a response to a subscriptionless poll, the
+ * source should include all messages it holds, else the set of messages included is at the discretion of
+ * the source.
+ *
+ *
Future applications may reintroduce unsolicited push operations for certain scenarios.
+ */
+ public static final String ACTION_TRAFF_PUSH = "org.traffxml.traff.FEED";
+
+ /**
+ * Intent for a subscription request.
+ *
+ *
This is a broadcast intent and must be sent as an explicit broadcast.
+ *
+ *
The filter list must be specified in the {@link #EXTRA_FILTER_LIST} extra.
+ *
+ *
The sender must indicate its package name in the {@link #EXTRA_PACKAGE} extra.
+ */
+ public static final String ACTION_TRAFF_SUBSCRIBE = "org.traffxml.traff.SUBSCRIBE";
+
+ /**
+ * Intent for a subscription change request,
+ *
+ *
This is a broadcast intent and must be sent as an explicit broadcast.
+ *
+ *
This intent must have {@link #EXTRA_SUBSCRIPTION_ID} set to the ID of an existing subscription between
+ * the calling consumer and the source which receives the broadcast.
+ *
+ *
The new filter list must be specified in the {@link #EXTRA_FILTER_LIST} extra.
+ */
+ public static final String ACTION_TRAFF_SUBSCRIPTION_CHANGE = "org.traffxml.traff.SUBSCRIPTION_CHANGE";
+
+ /**
+ * Intent for an unsubscribe request,
+ *
+ *
This is a broadcast intent and must be sent as an explicit broadcast.
+ *
+ *
This intent must have {@link #EXTRA_SUBSCRIPTION_ID} set to the ID of an existing subscription between
+ * the calling consumer and the source which receives the broadcast. It signals that the consumer is no
+ * longer interested in receiving messages related to that subscription, and that the source should stop
+ * sending updates. Unsubscribing from a nonexistent subscription is a no-op.
+ */
+ public static final String ACTION_TRAFF_UNSUBSCRIBE = "org.traffxml.traff.UNSUBSCRIBE";
+
+ /**
+ * Name for the column which holds the message data.
+ */
+ public static final String COLUMN_DATA = "data";
+
+ /**
+ * Schema for TraFF content URIs.
+ */
+ public static final String CONTENT_SCHEMA = "content";
+
+ /**
+ * String representations of TraFF result codes
+ */
+ public static final String[] ERROR_STRINGS = {
+ "unknown (0)",
+ "invalid request (1)",
+ "subscription rejected by the source (2)",
+ "requested area not covered (3)",
+ "requested area partially covered (4)",
+ "subscription ID not recognized by the source (5)",
+ "unknown (6)",
+ "source reported an internal error (7)"
+ };
+
+ /**
+ * Extra which contains the capabilities of the peer.
+ *
+ *
This is a String extra. It contains a {@code capabilities} XML element.
+ */
+ public static final String EXTRA_CAPABILITIES = "capabilities";
+
+ /**
+ * Extra which contains a TraFF feed.
+ *
+ *
This is a String extra. It contains a {@code feed} XML element.
+ *
+ *
The sender should be careful to keep the size of this extra low, as Android has a 1 MByte limit on all
+ * pending Binder transactions. However, there is no feedback to the sender about the capacity still
+ * available, or whether a request exceeds that limit. Therefore, senders should keep the size if each
+ * feed significantly below that limit. If necessary, they should split up a feed into multiple smaller
+ * ones and send them with a delay in between.
+ *
+ *
This mechanism is deprecated since TraFF 0.8 and peers are no longer required to support it. Peers
+ * which support TraFF 0.8 must rely on content providers for message transport.
+ */
+ @Deprecated
+ public static final String EXTRA_FEED = "feed";
+
+ /**
+ * Extra which contains a filter list.
+ *
+ *
This is a String extra. It contains a {@code filter_list} XML element.
+ */
+ public static final String EXTRA_FILTER_LIST = "filter_list";
+
+ /**
+ * Extra which contains the package name of the app sending it.
+ *
+ *
This is a String extra.
+ */
+ public static final String EXTRA_PACKAGE = "package";
+
+ /**
+ * Extra which contains a subscription ID.
+ *
+ *
This is a String extra.
+ */
+ public static final String EXTRA_SUBSCRIPTION_ID = "subscription_id";
+
+ /**
+ * Extra which contains the timeout duration for a subscription.
+ *
+ *
This is an integer extra.
+ */
+ public static final String EXTRA_TIMEOUT = "timeout";
+
+ /**
+ * The MIME type for TraFF content providers.
+ */
+ public static final String MIME_TYPE_TRAFF = "vnd.android.cursor.dir/org.traffxml.message";
+
+ /**
+ * The operation completed successfully.
+ */
+ public static final int RESULT_OK = -1;
+
+ /**
+ * An internal error prevented the recipient from fulfilling the request.
+ */
+ public static final int RESULT_INTERNAL_ERROR = 7;
+
+ /**
+ * A nonexistent operation was attempted, or an operation was attempted with incomplete or otherwise
+ * invalid data.
+ */
+ public static final int RESULT_INVALID = 1;
+
+ /**
+ * The subscription was rejected, and no messages will be sent.
+ */
+ public static final int RESULT_SUBSCRIPTION_REJECTED = 2;
+
+ /**
+ * The subscription was rejected because the source will never provide messages matching the selection.
+ */
+ public static final int RESULT_NOT_COVERED = 3;
+
+ /**
+ * The subscription was accepted but the source can only provide messages for parts of the selection.
+ */
+ public static final int RESULT_PARTIALLY_COVERED = 4;
+
+ /**
+ * The request failed because it refers to a subscription which does not exist between the source and
+ * consumer involved.
+ */
+ public static final int RESULT_SUBSCRIPTION_UNKNOWN = 5;
+
+ /**
+ * The request failed because the aggregator does not accept unsolicited push requests from the sensor.
+ */
+ public static final int RESULT_PUSH_REJECTED = 6;
+
+ public static String formatTraffError(int code) {
+ if ((code < 0) || (code >= ERROR_STRINGS.length))
+ return String.format("unknown (%d)", code);
+ else
+ return ERROR_STRINGS[code];
+ }
+}
diff --git a/android/sdk/src/main/java/app/organicmaps/sdk/traffxml/SourceImpl.java b/android/sdk/src/main/java/app/organicmaps/sdk/traffxml/SourceImpl.java
new file mode 100644
index 000000000..6fa6a18c4
--- /dev/null
+++ b/android/sdk/src/main/java/app/organicmaps/sdk/traffxml/SourceImpl.java
@@ -0,0 +1,70 @@
+package app.organicmaps.sdk.traffxml;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+
+/**
+ * Abstract superclass for TraFF source implementations.
+ */
+public abstract class SourceImpl extends BroadcastReceiver
+{
+ /**
+ * Creates a new instance.
+ *
+ * @param context The application context
+ */
+ public SourceImpl(Context context, long nativeManager)
+ {
+ super();
+ this.context = context;
+ this.nativeManager = nativeManager;
+ }
+
+ protected Context context;
+
+ /**
+ * The native `TraffSourceManager` instance.
+ */
+ protected long nativeManager;
+
+ /**
+ * Subscribes to a traffic source.
+ *
+ * @param filterList The filter list in XML format
+ */
+ public abstract void subscribe(String filterList);
+
+ /**
+ * Changes an existing traffic subscription.
+ *
+ * @param filterList The filter list in XML format
+ */
+ public abstract void changeSubscription(String filterList);
+
+ /**
+ * Unsubscribes from a traffic source we are subscribed to.
+ */
+ public abstract void unsubscribe();
+
+ /**
+ * Forwards a newly received TraFF feed to the traffic module for processing.
+ *
+ * Called when a TraFF feed is received. This is a wrapper around {@link #onFeedReceivedImpl(long, String)}.
+ *
+ * @param feed The TraFF feed
+ */
+ protected void onFeedReceived(String feed)
+ {
+ onFeedReceivedImpl(nativeManager, feed);
+ }
+
+ /**
+ * Forwards a newly received TraFF feed to the traffic module for processing.
+ *
+ * Called when a TraFF feed is received.
+ *
+ * @param nativeManager The native `TraffSourceManager` instance
+ * @param feed The TraFF feed
+ */
+ protected static native void onFeedReceivedImpl(long nativeManager, String feed);
+}
diff --git a/android/sdk/src/main/java/app/organicmaps/sdk/traffxml/SourceImplV0_7.java b/android/sdk/src/main/java/app/organicmaps/sdk/traffxml/SourceImplV0_7.java
new file mode 100644
index 000000000..cff6d0d75
--- /dev/null
+++ b/android/sdk/src/main/java/app/organicmaps/sdk/traffxml/SourceImplV0_7.java
@@ -0,0 +1,127 @@
+package app.organicmaps.sdk.traffxml;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.Manifest;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import app.organicmaps.sdk.util.log.Logger;
+
+/**
+ * Implementation for a TraFF 0.7 source.
+ */
+public class SourceImplV0_7 extends SourceImpl
+{
+ private PackageManager pm;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context The application context
+ */
+ public SourceImplV0_7(Context context, long nativeManager)
+ {
+ super(context, nativeManager);
+ // TODO Auto-generated constructor stub
+ }
+
+ /**
+ * Subscribes to a traffic source.
+ *
+ * @param filterList The filter list in XML format
+ */
+ @Override
+ public void subscribe(String filterList)
+ {
+ IntentFilter traffFilter07 = new IntentFilter();
+ traffFilter07.addAction(AndroidTransport.ACTION_TRAFF_PUSH);
+
+ this.context.registerReceiver(this, traffFilter07);
+
+ // Broadcast a poll intent to all TraFF 0.7-only receivers
+ Intent outIntent = new Intent(AndroidTransport.ACTION_TRAFF_POLL);
+ pm = this.context.getPackageManager();
+ List receivers07 = pm.queryBroadcastReceivers(outIntent, 0);
+ List receivers08 = pm.queryBroadcastReceivers(new Intent(AndroidTransport.ACTION_TRAFF_GET_CAPABILITIES), 0);
+ if (receivers07 != null)
+ {
+ /*
+ * Get receivers which support only TraFF 0.7 and poll them.
+ * If there are no TraFF 0.7 sources at the moment, we register the receiver nonetheless.
+ * That way, if any new sources are added during the session, we get any messages they send.
+ */
+ if (receivers08 != null)
+ receivers07.removeAll(receivers08);
+ for (ResolveInfo receiver : receivers07)
+ {
+ ComponentName cn = new ComponentName(receiver.activityInfo.applicationInfo.packageName,
+ receiver.activityInfo.name);
+ outIntent = new Intent(AndroidTransport.ACTION_TRAFF_POLL);
+ outIntent.setComponent(cn);
+ this.context.sendBroadcast(outIntent, Manifest.permission.ACCESS_COARSE_LOCATION);
+ }
+ }
+ }
+
+ /**
+ * Changes an existing traffic subscription.
+ *
+ * This implementation does nothing, as TraFF 0.7 does not support subscriptions.
+ *
+ * @param filterList The filter list in XML format
+ */
+ @Override
+ public void changeSubscription(String filterList)
+ {
+ // NOP
+ }
+
+ /**
+ * Unsubscribes from a traffic source we are subscribed to.
+ */
+ @Override
+ public void unsubscribe()
+ {
+ this.context.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ if (intent == null)
+ return;
+
+ if (intent.getAction().equals(AndroidTransport.ACTION_TRAFF_PUSH))
+ {
+ /* 0.7 feed */
+ String packageName = intent.getStringExtra(AndroidTransport.EXTRA_PACKAGE);
+ /*
+ * If the feed comes from a TraFF 0.8+ source, skip it (this may happen with “bilingual”
+ * TraFF 0.7/0.8 sources). That ensures the only way to get information from such sources is
+ * through a TraFF 0.8 subscription. Fetching the list from scratch each time ensures that
+ * apps installed during runtime get considered.)
+ */
+ if (packageName != null)
+ {
+ for (ResolveInfo info : pm.queryBroadcastReceivers(new Intent(AndroidTransport.ACTION_TRAFF_GET_CAPABILITIES), 0))
+ if (packageName.equals(info.resolvePackageName))
+ return;
+ }
+ String feed = intent.getStringExtra(AndroidTransport.EXTRA_FEED);
+ if (feed == null)
+ {
+ Logger.w(this.getClass().getSimpleName(), "empty feed, ignoring");
+ }
+ else
+ {
+ onFeedReceived(feed);
+ }
+ }
+ }
+
+}
diff --git a/android/sdk/src/main/java/app/organicmaps/sdk/traffxml/SourceImplV0_8.java b/android/sdk/src/main/java/app/organicmaps/sdk/traffxml/SourceImplV0_8.java
new file mode 100644
index 000000000..2518525ea
--- /dev/null
+++ b/android/sdk/src/main/java/app/organicmaps/sdk/traffxml/SourceImplV0_8.java
@@ -0,0 +1,240 @@
+package app.organicmaps.sdk.traffxml;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentFilter.MalformedMimeTypeException;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import app.organicmaps.sdk.util.log.Logger;
+
+/**
+ * Implementation for a TraFF 0.8 source.
+ */
+public class SourceImplV0_8 extends SourceImpl
+{
+
+ private String packageName;
+ private String subscriptionId = null;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context The application context
+ * @param packageName The package name for the source
+ */
+ public SourceImplV0_8(Context context, long nativeManager, String packageName)
+ {
+ super(context, nativeManager);
+ this.packageName = packageName;
+ }
+
+ /**
+ * Subscribes to a traffic source.
+ *
+ * @param filterList The filter list in XML format
+ */
+ @Override
+ public void subscribe(String filterList)
+ {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(AndroidTransport.ACTION_TRAFF_PUSH);
+ filter.addDataScheme(AndroidTransport.CONTENT_SCHEMA);
+ try
+ {
+ filter.addDataType(AndroidTransport.MIME_TYPE_TRAFF);
+ }
+ catch (MalformedMimeTypeException e)
+ {
+ // as long as the constant is a well-formed MIME type, this exception never gets thrown
+ // TODO revisit logging
+ e.printStackTrace();
+ }
+
+ context.registerReceiver(this, filter);
+
+ Bundle extras = new Bundle();
+ extras.putString(AndroidTransport.EXTRA_PACKAGE, context.getPackageName());
+ extras.putString(AndroidTransport.EXTRA_FILTER_LIST, filterList);
+ AndroidConsumer.sendTraffIntent(context, AndroidTransport.ACTION_TRAFF_SUBSCRIBE, null,
+ extras, packageName, Manifest.permission.ACCESS_COARSE_LOCATION, this);
+ }
+
+ /**
+ * Changes an existing traffic subscription.
+ *
+ * @param filterList The filter list in XML format
+ */
+ @Override
+ public void changeSubscription(String filterList)
+ {
+ Bundle extras = new Bundle();
+ extras.putString(AndroidTransport.EXTRA_SUBSCRIPTION_ID, subscriptionId);
+ extras.putString(AndroidTransport.EXTRA_FILTER_LIST, filterList);
+ AndroidConsumer.sendTraffIntent(context, AndroidTransport.ACTION_TRAFF_SUBSCRIPTION_CHANGE, null,
+ extras, packageName, Manifest.permission.ACCESS_COARSE_LOCATION, this);
+ }
+
+ /**
+ * Unsubscribes from a traffic source we are subscribed to.
+ */
+ @Override
+ public void unsubscribe()
+ {
+ Bundle extras = new Bundle();
+ extras.putString(AndroidTransport.EXTRA_SUBSCRIPTION_ID, subscriptionId);
+ AndroidConsumer.sendTraffIntent(this.context, AndroidTransport.ACTION_TRAFF_UNSUBSCRIBE, null,
+ extras, packageName, Manifest.permission.ACCESS_COARSE_LOCATION, this);
+
+ this.context.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ if (intent == null)
+ return;
+
+ if (intent.getAction().equals(AndroidTransport.ACTION_TRAFF_PUSH))
+ {
+ Uri uri = intent.getData();
+ if (uri != null)
+ {
+ /* 0.8 feed */
+ String subscriptionId = intent.getStringExtra(AndroidTransport.EXTRA_SUBSCRIPTION_ID);
+ if (subscriptionId.equals(this.subscriptionId))
+ fetchMessages(context, uri);
+ }
+ else
+ {
+ Logger.w(this.getClass().getSimpleName(), "no URI in feed, ignoring");
+ } // uri != null
+ } else if (intent.getAction().equals(AndroidTransport.ACTION_TRAFF_SUBSCRIBE)) {
+ if (this.getResultCode() != AndroidTransport.RESULT_OK) {
+ Bundle extras = this.getResultExtras(true);
+ if (extras != null)
+ Logger.e(this.getClass().getSimpleName(), String.format("subscription to %s failed, %s",
+ extras.getString(AndroidTransport.EXTRA_PACKAGE), AndroidTransport.formatTraffError(this.getResultCode())));
+ else
+ Logger.e(this.getClass().getSimpleName(), String.format("subscription failed, %s",
+ AndroidTransport.formatTraffError(this.getResultCode())));
+ if (this.getResultCode() == AndroidTransport.RESULT_INTERNAL_ERROR)
+ Logger.e(this.getClass().getSimpleName(), "Make sure the TraFF source app has at least coarse location permission, even when running in background");
+ return;
+ }
+ Bundle extras = this.getResultExtras(true);
+ String data = this.getResultData();
+ String packageName = extras.getString(AndroidTransport.EXTRA_PACKAGE);
+ if (!this.packageName.equals(packageName))
+ return;
+ String subscriptionId = extras.getString(AndroidTransport.EXTRA_SUBSCRIPTION_ID);
+ if (subscriptionId == null) {
+ Logger.e(this.getClass().getSimpleName(),
+ String.format("subscription to %s failed: no subscription ID returned", packageName));
+ return;
+ } else if (packageName == null) {
+ Logger.e(this.getClass().getSimpleName(), "subscription failed: no package name");
+ return;
+ } else if (data == null) {
+ Logger.w(this.getClass().getSimpleName(),
+ String.format("subscription to %s successful (ID: %s) but no content URI was supplied. "
+ + "This is an issue with the source and may result in delayed message retrieval.",
+ packageName, subscriptionId));
+ this.subscriptionId = subscriptionId;
+ return;
+ }
+ Logger.d(this.getClass().getSimpleName(),
+ "subscription to " + packageName + " successful, ID: " + subscriptionId);
+ this.subscriptionId = subscriptionId;
+ fetchMessages(context, Uri.parse(data));
+ } else if (intent.getAction().equals(AndroidTransport.ACTION_TRAFF_SUBSCRIPTION_CHANGE)) {
+ if (this.getResultCode() != AndroidTransport.RESULT_OK) {
+ Bundle extras = this.getResultExtras(true);
+ if (extras != null)
+ Logger.e(this.getClass().getSimpleName(),
+ String.format("subscription change for %s failed: %s",
+ extras.getString(AndroidTransport.EXTRA_SUBSCRIPTION_ID),
+ AndroidTransport.formatTraffError(this.getResultCode())));
+ else
+ Logger.e(this.getClass().getSimpleName(),
+ String.format("subscription change failed: %s",
+ AndroidTransport.formatTraffError(this.getResultCode())));
+ return;
+ }
+ Bundle extras = intent.getExtras();
+ String data = this.getResultData();
+ String subscriptionId = extras.getString(AndroidTransport.EXTRA_SUBSCRIPTION_ID);
+ if (subscriptionId == null) {
+ Logger.w(this.getClass().getSimpleName(),
+ "subscription change successful but the source did not specify the subscription ID. "
+ + "This is an issue with the source and may result in delayed message retrieval. "
+ + "URI: " + data);
+ return;
+ } else if (!subscriptionId.equals(this.subscriptionId)) {
+ return;
+ } else if (data == null) {
+ Logger.w(this.getClass().getSimpleName(),
+ String.format("subscription change for %s successful but no content URI was supplied. "
+ + "This is an issue with the source and may result in delayed message retrieval.",
+ subscriptionId));
+ return;
+ }
+ Logger.d(this.getClass().getSimpleName(),
+ "subscription change for " + subscriptionId + " successful");
+ fetchMessages(context, Uri.parse(data));
+ } else if (intent.getAction().equals(AndroidTransport.ACTION_TRAFF_UNSUBSCRIBE)) {
+ String subscriptionId = intent.getStringExtra(AndroidTransport.EXTRA_SUBSCRIPTION_ID);
+ if (subscriptionId.equals(this.subscriptionId))
+ this.subscriptionId = null;
+ // TODO is there anything to do here? (Comment below is from Navit)
+ /*
+ * If we ever unsubscribe for reasons other than that we are shutting down or got a feed for
+ * a subscription we don’t recognize, or if we start keeping a persistent list of
+ * subscriptions, we need to delete the subscription from our list. Until then, there is
+ * nothing to do here: either the subscription isn’t in the list, or we are about to shut
+ * down and the whole list is about to get discarded.
+ */
+ } else if (intent.getAction().equals(AndroidTransport.ACTION_TRAFF_HEARTBEAT)) {
+ String subscriptionId = intent.getStringExtra(AndroidTransport.EXTRA_SUBSCRIPTION_ID);
+ if (subscriptionId.equals(this.subscriptionId)) {
+ Logger.d(this.getClass().getSimpleName(),
+ String.format("got a heartbeat from %s for subscription %s; sending result",
+ intent.getStringExtra(AndroidTransport.EXTRA_PACKAGE), subscriptionId));
+ this.setResult(AndroidTransport.RESULT_OK, null, null);
+ }
+ } // intent.getAction()
+ // TODO Auto-generated method stub
+
+ }
+
+ /**
+ * Fetches TraFF messages from a content provider.
+ *
+ * @param context The context to use for the content resolver
+ * @param uri The content provider URI
+ */
+ private void fetchMessages(Context context, Uri uri) {
+ try {
+ Cursor cursor = context.getContentResolver().query(uri, new String[] {AndroidTransport.COLUMN_DATA}, null, null, null);
+ if (cursor == null)
+ return;
+ if (cursor.getCount() < 1) {
+ cursor.close();
+ return;
+ }
+ StringBuilder builder = new StringBuilder("\n");
+ while (cursor.moveToNext())
+ builder.append(cursor.getString(cursor.getColumnIndex(AndroidTransport.COLUMN_DATA))).append("\n");
+ builder.append("");
+ cursor.close();
+ onFeedReceived(builder.toString());
+ } catch (Exception e) {
+ Logger.w(this.getClass().getSimpleName(),
+ String.format("Unable to fetch messages from %s", uri.toString()), e);
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/android/sdk/src/main/java/app/organicmaps/sdk/traffxml/Version.java b/android/sdk/src/main/java/app/organicmaps/sdk/traffxml/Version.java
new file mode 100644
index 000000000..c2603b2ec
--- /dev/null
+++ b/android/sdk/src/main/java/app/organicmaps/sdk/traffxml/Version.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright © 2019–2020 traffxml.org.
+ *
+ * Relicensed to CoMaps by the original author.
+ */
+
+package app.organicmaps.sdk.traffxml;
+
+/**
+ * Constants for versions.
+ */
+public class Version {
+ /** Version 0.7: introduced transport on Android. */
+ public static final int V0_7 = 7;
+
+ /** Version 0.8: introduced subscriptions and HTTP transport. */
+ public static final int V0_8 = 8;
+}
diff --git a/android/sdk/src/main/java/app/organicmaps/sdk/util/Config.java b/android/sdk/src/main/java/app/organicmaps/sdk/util/Config.java
index bc06fce5c..b6f7f0b4e 100644
--- a/android/sdk/src/main/java/app/organicmaps/sdk/util/Config.java
+++ b/android/sdk/src/main/java/app/organicmaps/sdk/util/Config.java
@@ -70,6 +70,16 @@ public final class Config
* True if the first start animation has been seen.
*/
private static final String KEY_MISC_FIRST_START_DIALOG_SEEN = "FirstStartDialogSeen";
+
+ /**
+ * Whether feeds from legacy TraFF applications (TraFF 0.7, Android transport) are enabled.
+ */
+ private static final String KEY_TRAFFIC_LEGACY_ENABLED = "TrafficLegacyEnabled";
+
+ /**
+ * TraFF (0.8+) applications from which to request traffic data.
+ */
+ private static final String KEY_TRAFFIC_APPS = "TrafficApps";
private Config() {}
@@ -403,6 +413,63 @@ public static void setTransliteration(boolean value)
nativeSetTransliteration(value);
}
+ public static boolean getTrafficHttpEnabled()
+ {
+ return nativeGetTrafficHttpEnabled();
+ }
+
+ public static void setTrafficHttpEnabled(boolean value)
+ {
+ nativeSetTrafficHttpEnabled(value);
+ }
+
+ public static String getTrafficHttpUrl()
+ {
+ return nativeGetTrafficHttpUrl();
+ }
+
+ public static void setTrafficHttpUrl(String value)
+ {
+ nativeSetTrafficHttpUrl(value);
+ }
+
+ public static String[] getTrafficApps()
+ {
+ String appString = getString(KEY_TRAFFIC_APPS, "");
+ if (appString.length() == 0)
+ return new String[0];
+ return appString.split(",");
+ }
+
+ public static void setTrafficApps(String[] value)
+ {
+ String valueString = "";
+ for (int i = 0; i < value.length; i++)
+ {
+ valueString = valueString + value[i];
+ if ((i + 1) < value.length)
+ valueString = valueString + ",";
+ }
+ setString(KEY_TRAFFIC_APPS, valueString);
+ applyTrafficApps(value);
+ }
+
+ public static boolean getTrafficLegacyEnabled()
+ {
+ return getBool(KEY_TRAFFIC_LEGACY_ENABLED, false);
+ }
+
+ public static void setTrafficLegacyEnabled(boolean value)
+ {
+ setBool(KEY_TRAFFIC_LEGACY_ENABLED, value);
+ applyTrafficLegacyEnabled(value);
+ }
+
+ public static boolean isNY()
+ {
+ return getBool("NY");
+ }
+
@NonNull
public static String getDonateUrl()
{
@@ -548,4 +615,10 @@ public static void setAnnounceStreets(boolean enabled)
private static native void nativeSetAlternativeMapLanguageHandling(int value);
private static native boolean nativeGetTransliteration();
private static native void nativeSetTransliteration(boolean value);
+ private static native boolean nativeGetTrafficHttpEnabled();
+ private static native void nativeSetTrafficHttpEnabled(boolean value);
+ private static native String nativeGetTrafficHttpUrl();
+ private static native void nativeSetTrafficHttpUrl(String value);
+ private static native void applyTrafficApps(String[] value);
+ private static native void applyTrafficLegacyEnabled(boolean value);
}
diff --git a/android/sdk/src/main/res/values-af/types_strings.xml b/android/sdk/src/main/res/values-af/types_strings.xml
index 7a5cf0ee5..4cc8bfa49 100644
--- a/android/sdk/src/main/res/values-af/types_strings.xml
+++ b/android/sdk/src/main/res/values-af/types_strings.xml
@@ -359,7 +359,24 @@
Tonnel
Bushalte
+
Pad in aanbou
+ Pad in aanbou
+ Pad in aanbou
+ Pad in aanbou
+ Pad in aanbou
+ Pad in aanbou
+ Pad in aanbou
+ Pad in aanbou
+ Pad in aanbou
+ Pad in aanbou
+ Pad in aanbou
+ Pad in aanbou
+ Pad in aanbou
+ Pad in aanbou
+ Pad in aanbou
+ Pad in aanbou
+ Pad in aanbou
Fietspad
Brug
diff --git a/android/sdk/src/main/res/values-ar/types_strings.xml b/android/sdk/src/main/res/values-ar/types_strings.xml
index 099e41382..9696b951d 100644
--- a/android/sdk/src/main/res/values-ar/types_strings.xml
+++ b/android/sdk/src/main/res/values-ar/types_strings.xml
@@ -380,7 +380,24 @@
نفق
موقف حافلات
+
طريق تحت الإنشاء
+ طريق تحت الإنشاء
+ طريق تحت الإنشاء
+ طريق تحت الإنشاء
+ طريق تحت الإنشاء
+ طريق تحت الإنشاء
+ طريق تحت الإنشاء
+ طريق تحت الإنشاء
+ طريق تحت الإنشاء
+ طريق تحت الإنشاء
+ طريق تحت الإنشاء
+ طريق تحت الإنشاء
+ طريق تحت الإنشاء
+ طريق تحت الإنشاء
+ طريق تحت الإنشاء
+ طريق تحت الإنشاء
+ طريق تحت الإنشاء
مسار للدراجات
جسر
diff --git a/android/sdk/src/main/res/values-az/types_strings.xml b/android/sdk/src/main/res/values-az/types_strings.xml
index fd4f9fed8..775844b63 100644
--- a/android/sdk/src/main/res/values-az/types_strings.xml
+++ b/android/sdk/src/main/res/values-az/types_strings.xml
@@ -370,7 +370,24 @@
Tunel
Avtobus dayanacağı
+
Tikili Yol
+ Tikili Yol
+ Tikili Yol
+ Tikili Yol
+ Tikili Yol
+ Tikili Yol
+ Tikili Yol
+ Tikili Yol
+ Tikili Yol
+ Tikili Yol
+ Tikili Yol
+ Tikili Yol
+ Tikili Yol
+ Tikili Yol
+ Tikili Yol
+ Tikili Yol
+ Tikili Yol
Velosiped yolu
Körpü
diff --git a/android/sdk/src/main/res/values-bg/types_strings.xml b/android/sdk/src/main/res/values-bg/types_strings.xml
index 06fc1e3ed..da11056df 100644
--- a/android/sdk/src/main/res/values-bg/types_strings.xml
+++ b/android/sdk/src/main/res/values-bg/types_strings.xml
@@ -956,7 +956,24 @@
Виетнамска
Пътека за юздечки
Пътека за юздечки
+
Път в процес на изграждане
+ Път в процес на изграждане
+ Път в процес на изграждане
+ Път в процес на изграждане
+ Път в процес на изграждане
+ Път в процес на изграждане
+ Път в процес на изграждане
+ Път в процес на изграждане
+ Път в процес на изграждане
+ Път в процес на изграждане
+ Път в процес на изграждане
+ Път в процес на изграждане
+ Път в процес на изграждане
+ Път в процес на изграждане
+ Път в процес на изграждане
+ Път в процес на изграждане
+ Път в процес на изграждане
Жилищна улица
Пътека
Пътека
diff --git a/android/sdk/src/main/res/values-ca/types_strings.xml b/android/sdk/src/main/res/values-ca/types_strings.xml
index 8d4f131a0..9c5b751df 100644
--- a/android/sdk/src/main/res/values-ca/types_strings.xml
+++ b/android/sdk/src/main/res/values-ca/types_strings.xml
@@ -935,7 +935,24 @@
Centre comercial
Desfibril·lador
Carretera
+
Via en construcció
+ Via en construcció
+ Via en construcció
+ Via en construcció
+ Via en construcció
+ Via en construcció
+ Via en construcció
+ Via en construcció
+ Via en construcció
+ Via en construcció
+ Via en construcció
+ Via en construcció
+ Via en construcció
+ Via en construcció
+ Via en construcció
+ Via en construcció
+ Via en construcció
Continent
Quiosc
€
diff --git a/android/sdk/src/main/res/values-cs/types_strings.xml b/android/sdk/src/main/res/values-cs/types_strings.xml
index 11a843f26..1127305f3 100644
--- a/android/sdk/src/main/res/values-cs/types_strings.xml
+++ b/android/sdk/src/main/res/values-cs/types_strings.xml
@@ -327,7 +327,24 @@
Tunel
Autobusová zastávka
+
Silnice v rekonstrukci
+ Silnice v rekonstrukci
+ Silnice v rekonstrukci
+ Silnice v rekonstrukci
+ Silnice v rekonstrukci
+ Silnice v rekonstrukci
+ Silnice v rekonstrukci
+ Silnice v rekonstrukci
+ Silnice v rekonstrukci
+ Silnice v rekonstrukci
+ Silnice v rekonstrukci
+ Silnice v rekonstrukci
+ Silnice v rekonstrukci
+ Silnice v rekonstrukci
+ Silnice v rekonstrukci
+ Silnice v rekonstrukci
+ Silnice v rekonstrukci
Most
diff --git a/android/sdk/src/main/res/values-da/types_strings.xml b/android/sdk/src/main/res/values-da/types_strings.xml
index 5176aca32..3bd536cd2 100644
--- a/android/sdk/src/main/res/values-da/types_strings.xml
+++ b/android/sdk/src/main/res/values-da/types_strings.xml
@@ -317,7 +317,24 @@
Tunnel
Busstoppested
+
Vej under anlæggelse
+ Vej under anlæggelse
+ Vej under anlæggelse
+ Vej under anlæggelse
+ Vej under anlæggelse
+ Vej under anlæggelse
+ Vej under anlæggelse
+ Vej under anlæggelse
+ Vej under anlæggelse
+ Vej under anlæggelse
+ Vej under anlæggelse
+ Vej under anlæggelse
+ Vej under anlæggelse
+ Vej under anlæggelse
+ Vej under anlæggelse
+ Vej under anlæggelse
+ Vej under anlæggelse
Bro
diff --git a/android/sdk/src/main/res/values-de/types_strings.xml b/android/sdk/src/main/res/values-de/types_strings.xml
index f8238a939..aef37da73 100644
--- a/android/sdk/src/main/res/values-de/types_strings.xml
+++ b/android/sdk/src/main/res/values-de/types_strings.xml
@@ -417,7 +417,24 @@
Tunnel
Bushaltestelle
+
Straße im Bau
+ Straße im Bau
+ Straße im Bau
+ Straße im Bau
+ Straße im Bau
+ Straße im Bau
+ Straße im Bau
+ Straße im Bau
+ Straße im Bau
+ Straße im Bau
+ Straße im Bau
+ Straße im Bau
+ Straße im Bau
+ Straße im Bau
+ Straße im Bau
+ Straße im Bau
+ Straße im Bau
Radweg
Brücke
diff --git a/android/sdk/src/main/res/values-el/types_strings.xml b/android/sdk/src/main/res/values-el/types_strings.xml
index 9ec8dcba5..3657c3d9e 100644
--- a/android/sdk/src/main/res/values-el/types_strings.xml
+++ b/android/sdk/src/main/res/values-el/types_strings.xml
@@ -335,7 +335,24 @@
Σήραγγα
Στάση λεωφορείου
+
Οδός υπό κατασκευή
+ Οδός υπό κατασκευή
+ Οδός υπό κατασκευή
+ Οδός υπό κατασκευή
+ Οδός υπό κατασκευή
+ Οδός υπό κατασκευή
+ Οδός υπό κατασκευή
+ Οδός υπό κατασκευή
+ Οδός υπό κατασκευή
+ Οδός υπό κατασκευή
+ Οδός υπό κατασκευή
+ Οδός υπό κατασκευή
+ Οδός υπό κατασκευή
+ Οδός υπό κατασκευή
+ Οδός υπό κατασκευή
+ Οδός υπό κατασκευή
+ Οδός υπό κατασκευή
Γέφυρα
diff --git a/android/sdk/src/main/res/values-es-rMX/types_strings.xml b/android/sdk/src/main/res/values-es-rMX/types_strings.xml
index fb4916e55..ef0f8710d 100644
--- a/android/sdk/src/main/res/values-es-rMX/types_strings.xml
+++ b/android/sdk/src/main/res/values-es-rMX/types_strings.xml
@@ -57,7 +57,24 @@
Puente
Túnel
+
Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
Puente
diff --git a/android/sdk/src/main/res/values-es/types_strings.xml b/android/sdk/src/main/res/values-es/types_strings.xml
index 5bf8a01c6..eed7b763d 100644
--- a/android/sdk/src/main/res/values-es/types_strings.xml
+++ b/android/sdk/src/main/res/values-es/types_strings.xml
@@ -385,7 +385,24 @@
Túnel
Parada de bus
+
Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
+ Vía en construcción
Ciclovía
Puente
diff --git a/android/sdk/src/main/res/values-et/types_strings.xml b/android/sdk/src/main/res/values-et/types_strings.xml
index 07deaa4b5..c5803de42 100644
--- a/android/sdk/src/main/res/values-et/types_strings.xml
+++ b/android/sdk/src/main/res/values-et/types_strings.xml
@@ -382,7 +382,24 @@
Tunnel
Bussipeatus
+
Ehitusjärgus tee
+ Ehitusjärgus tee
+ Ehitusjärgus tee
+ Ehitusjärgus tee
+ Ehitusjärgus tee
+ Ehitusjärgus tee
+ Ehitusjärgus tee
+ Ehitusjärgus tee
+ Ehitusjärgus tee
+ Ehitusjärgus tee
+ Ehitusjärgus tee
+ Ehitusjärgus tee
+ Ehitusjärgus tee
+ Ehitusjärgus tee
+ Ehitusjärgus tee
+ Ehitusjärgus tee
+ Ehitusjärgus tee
Jalgrattatee
Jalgrattasild
diff --git a/android/sdk/src/main/res/values-eu/types_strings.xml b/android/sdk/src/main/res/values-eu/types_strings.xml
index ceb4e4263..e112c8a39 100644
--- a/android/sdk/src/main/res/values-eu/types_strings.xml
+++ b/android/sdk/src/main/res/values-eu/types_strings.xml
@@ -373,7 +373,24 @@
Tunela
Autobus geltokia
+
Eraikitzen ari diren errepidea
+ Eraikitzen ari diren errepidea
+ Eraikitzen ari diren errepidea
+ Eraikitzen ari diren errepidea
+ Eraikitzen ari diren errepidea
+ Eraikitzen ari diren errepidea
+ Eraikitzen ari diren errepidea
+ Eraikitzen ari diren errepidea
+ Eraikitzen ari diren errepidea
+ Eraikitzen ari diren errepidea
+ Eraikitzen ari diren errepidea
+ Eraikitzen ari diren errepidea
+ Eraikitzen ari diren errepidea
+ Eraikitzen ari diren errepidea
+ Eraikitzen ari diren errepidea
+ Eraikitzen ari diren errepidea
+ Eraikitzen ari diren errepidea
Bidegorri edo bizikleta-bidea
Zubia
diff --git a/android/sdk/src/main/res/values-fa/types_strings.xml b/android/sdk/src/main/res/values-fa/types_strings.xml
index 5ff2035ee..656cf590e 100644
--- a/android/sdk/src/main/res/values-fa/types_strings.xml
+++ b/android/sdk/src/main/res/values-fa/types_strings.xml
@@ -244,7 +244,24 @@
تونل
حمل و نقل
+
جاده در دست ساخت است
+ جاده در دست ساخت است
+ جاده در دست ساخت است
+ جاده در دست ساخت است
+ جاده در دست ساخت است
+ جاده در دست ساخت است
+ جاده در دست ساخت است
+ جاده در دست ساخت است
+ جاده در دست ساخت است
+ جاده در دست ساخت است
+ جاده در دست ساخت است
+ جاده در دست ساخت است
+ جاده در دست ساخت است
+ جاده در دست ساخت است
+ جاده در دست ساخت است
+ جاده در دست ساخت است
+ جاده در دست ساخت است
پل
diff --git a/android/sdk/src/main/res/values-fi/types_strings.xml b/android/sdk/src/main/res/values-fi/types_strings.xml
index e0fa7245b..eb775e21d 100644
--- a/android/sdk/src/main/res/values-fi/types_strings.xml
+++ b/android/sdk/src/main/res/values-fi/types_strings.xml
@@ -375,7 +375,24 @@
Tunneli
Bussipysäkki
+
Rakenteilla oleva tie
+ Rakenteilla oleva tie
+ Rakenteilla oleva tie
+ Rakenteilla oleva tie
+ Rakenteilla oleva tie
+ Rakenteilla oleva tie
+ Rakenteilla oleva tie
+ Rakenteilla oleva tie
+ Rakenteilla oleva tie
+ Rakenteilla oleva tie
+ Rakenteilla oleva tie
+ Rakenteilla oleva tie
+ Rakenteilla oleva tie
+ Rakenteilla oleva tie
+ Rakenteilla oleva tie
+ Rakenteilla oleva tie
+ Rakenteilla oleva tie
Pyörätie
Silta
diff --git a/android/sdk/src/main/res/values-fr/types_strings.xml b/android/sdk/src/main/res/values-fr/types_strings.xml
index 94f7d1c03..d1d93e878 100644
--- a/android/sdk/src/main/res/values-fr/types_strings.xml
+++ b/android/sdk/src/main/res/values-fr/types_strings.xml
@@ -370,7 +370,24 @@
Tunnel
Arrêt de bus
+
Route en construction
+ Route en construction
+ Route en construction
+ Route en construction
+ Route en construction
+ Route en construction
+ Route en construction
+ Route en construction
+ Route en construction
+ Route en construction
+ Route en construction
+ Route en construction
+ Route en construction
+ Route en construction
+ Route en construction
+ Route en construction
+ Route en construction
Piste cyclable
Pont
diff --git a/android/sdk/src/main/res/values-gl/types_strings.xml b/android/sdk/src/main/res/values-gl/types_strings.xml
index a07fca348..7bed81337 100644
--- a/android/sdk/src/main/res/values-gl/types_strings.xml
+++ b/android/sdk/src/main/res/values-gl/types_strings.xml
@@ -367,7 +367,24 @@
Puente
Túnel
Parada de autobús
+
Vía en construción
+ Vía en construción
+ Vía en construción
+ Vía en construción
+ Vía en construción
+ Vía en construción
+ Vía en construción
+ Vía en construción
+ Vía en construción
+ Vía en construción
+ Vía en construción
+ Vía en construción
+ Vía en construción
+ Vía en construción
+ Vía en construción
+ Vía en construción
+ Vía en construción
Ciclovía
Puente
Ciclovía
diff --git a/android/sdk/src/main/res/values-gsw/types_strings.xml b/android/sdk/src/main/res/values-gsw/types_strings.xml
index 3ea62239b..04c5a508d 100644
--- a/android/sdk/src/main/res/values-gsw/types_strings.xml
+++ b/android/sdk/src/main/res/values-gsw/types_strings.xml
@@ -333,7 +333,24 @@
Brugg
Tunnel
Bushaltistell
+
Strass im Bau
+ Strass im Bau
+ Strass im Bau
+ Strass im Bau
+ Strass im Bau
+ Strass im Bau
+ Strass im Bau
+ Strass im Bau
+ Strass im Bau
+ Strass im Bau
+ Strass im Bau
+ Strass im Bau
+ Strass im Bau
+ Strass im Bau
+ Strass im Bau
+ Strass im Bau
+ Strass im Bau
Veloweg
Brugg
Veloweg
diff --git a/android/sdk/src/main/res/values-hi/types_strings.xml b/android/sdk/src/main/res/values-hi/types_strings.xml
index 9cc4513fd..055d40fe6 100644
--- a/android/sdk/src/main/res/values-hi/types_strings.xml
+++ b/android/sdk/src/main/res/values-hi/types_strings.xml
@@ -263,7 +263,24 @@
राजमार्ग
बस स्टॉप
+
निर्माणाधीन सड़क
+ निर्माणाधीन सड़क
+ निर्माणाधीन सड़क
+ निर्माणाधीन सड़क
+ निर्माणाधीन सड़क
+ निर्माणाधीन सड़क
+ निर्माणाधीन सड़क
+ निर्माणाधीन सड़क
+ निर्माणाधीन सड़क
+ निर्माणाधीन सड़क
+ निर्माणाधीन सड़क
+ निर्माणाधीन सड़क
+ निर्माणाधीन सड़क
+ निर्माणाधीन सड़क
+ निर्माणाधीन सड़क
+ निर्माणाधीन सड़क
+ निर्माणाधीन सड़क
साइकिल मार्ग
साइकिल मार्ग
उत्थापक
diff --git a/android/sdk/src/main/res/values-hu/types_strings.xml b/android/sdk/src/main/res/values-hu/types_strings.xml
index ecdcc5f3f..b194a1251 100644
--- a/android/sdk/src/main/res/values-hu/types_strings.xml
+++ b/android/sdk/src/main/res/values-hu/types_strings.xml
@@ -329,7 +329,24 @@
Alagút
Buszmegálló
+
Útépítés
+ Útépítés
+ Útépítés
+ Útépítés
+ Útépítés
+ Útépítés
+ Útépítés
+ Útépítés
+ Útépítés
+ Útépítés
+ Útépítés
+ Útépítés
+ Útépítés
+ Útépítés
+ Útépítés
+ Útépítés
+ Útépítés
Híd
diff --git a/android/sdk/src/main/res/values-in/types_strings.xml b/android/sdk/src/main/res/values-in/types_strings.xml
index ca7c5bc74..7acde82a3 100644
--- a/android/sdk/src/main/res/values-in/types_strings.xml
+++ b/android/sdk/src/main/res/values-in/types_strings.xml
@@ -323,7 +323,24 @@
Terowongan
Halte bus
+
Jalan sedang dibangun
+ Jalan sedang dibangun
+ Jalan sedang dibangun
+ Jalan sedang dibangun
+ Jalan sedang dibangun
+ Jalan sedang dibangun
+ Jalan sedang dibangun
+ Jalan sedang dibangun
+ Jalan sedang dibangun
+ Jalan sedang dibangun
+ Jalan sedang dibangun
+ Jalan sedang dibangun
+ Jalan sedang dibangun
+ Jalan sedang dibangun
+ Jalan sedang dibangun
+ Jalan sedang dibangun
+ Jalan sedang dibangun
Menjembatani
diff --git a/android/sdk/src/main/res/values-is/types_strings.xml b/android/sdk/src/main/res/values-is/types_strings.xml
index 0ce70b142..40860f438 100644
--- a/android/sdk/src/main/res/values-is/types_strings.xml
+++ b/android/sdk/src/main/res/values-is/types_strings.xml
@@ -1046,7 +1046,24 @@
Snjóleikjagarður
Þráðlaust net
Setskýli
+
Vegur í byggingu
+ Vegur í byggingu
+ Vegur í byggingu
+ Vegur í byggingu
+ Vegur í byggingu
+ Vegur í byggingu
+ Vegur í byggingu
+ Vegur í byggingu
+ Vegur í byggingu
+ Vegur í byggingu
+ Vegur í byggingu
+ Vegur í byggingu
+ Vegur í byggingu
+ Vegur í byggingu
+ Vegur í byggingu
+ Vegur í byggingu
+ Vegur í byggingu
Svæði fyrir fótgangandi
Svæði fyrir fótgangandi
Sögulegur skriðdreki
diff --git a/android/sdk/src/main/res/values-it/types_strings.xml b/android/sdk/src/main/res/values-it/types_strings.xml
index 25a08eeca..1cb54d3cf 100644
--- a/android/sdk/src/main/res/values-it/types_strings.xml
+++ b/android/sdk/src/main/res/values-it/types_strings.xml
@@ -382,7 +382,24 @@
Galleria
Fermata dell\'autobus
+
Strada in costruzione
+ Strada in costruzione
+ Strada in costruzione
+ Strada in costruzione
+ Strada in costruzione
+ Strada in costruzione
+ Strada in costruzione
+ Strada in costruzione
+ Strada in costruzione
+ Strada in costruzione
+ Strada in costruzione
+ Strada in costruzione
+ Strada in costruzione
+ Strada in costruzione
+ Strada in costruzione
+ Strada in costruzione
+ Strada in costruzione
Pista ciclabile
Ponte
diff --git a/android/sdk/src/main/res/values-iw/types_strings.xml b/android/sdk/src/main/res/values-iw/types_strings.xml
index d46c06469..d9f68278f 100644
--- a/android/sdk/src/main/res/values-iw/types_strings.xml
+++ b/android/sdk/src/main/res/values-iw/types_strings.xml
@@ -382,7 +382,24 @@
מִנהָרָה
תחנת אוטובוס
+
כביש בבניה
+ כביש בבניה
+ כביש בבניה
+ כביש בבניה
+ כביש בבניה
+ כביש בבניה
+ כביש בבניה
+ כביש בבניה
+ כביש בבניה
+ כביש בבניה
+ כביש בבניה
+ כביש בבניה
+ כביש בבניה
+ כביש בבניה
+ כביש בבניה
+ כביש בבניה
+ כביש בבניה
שביל אופניים
גשר לאופניים
diff --git a/android/sdk/src/main/res/values-ja/types_strings.xml b/android/sdk/src/main/res/values-ja/types_strings.xml
index 50557c2c2..7f5990df1 100644
--- a/android/sdk/src/main/res/values-ja/types_strings.xml
+++ b/android/sdk/src/main/res/values-ja/types_strings.xml
@@ -356,7 +356,24 @@
トンネル
バス停
+
工事中の道路
+ 工事中の道路
+ 工事中の道路
+ 工事中の道路
+ 工事中の道路
+ 工事中の道路
+ 工事中の道路
+ 工事中の道路
+ 工事中の道路
+ 工事中の道路
+ 工事中の道路
+ 工事中の道路
+ 工事中の道路
+ 工事中の道路
+ 工事中の道路
+ 工事中の道路
+ 工事中の道路
自転車道
自転車道(橋)
diff --git a/android/sdk/src/main/res/values-ko/types_strings.xml b/android/sdk/src/main/res/values-ko/types_strings.xml
index e62739e1a..9d1c1912d 100644
--- a/android/sdk/src/main/res/values-ko/types_strings.xml
+++ b/android/sdk/src/main/res/values-ko/types_strings.xml
@@ -330,7 +330,24 @@
터널
버스 정류장
+
공사 중 도로
+ 공사 중 도로
+ 공사 중 도로
+ 공사 중 도로
+ 공사 중 도로
+ 공사 중 도로
+ 공사 중 도로
+ 공사 중 도로
+ 공사 중 도로
+ 공사 중 도로
+ 공사 중 도로
+ 공사 중 도로
+ 공사 중 도로
+ 공사 중 도로
+ 공사 중 도로
+ 공사 중 도로
+ 공사 중 도로
다리
diff --git a/android/sdk/src/main/res/values-kw/types_strings.xml b/android/sdk/src/main/res/values-kw/types_strings.xml
index fe985615b..206dc602d 100644
--- a/android/sdk/src/main/res/values-kw/types_strings.xml
+++ b/android/sdk/src/main/res/values-kw/types_strings.xml
@@ -286,7 +286,24 @@
Pons
Kowfordh
Kyttrinva
+
Fordh y\'n Drehevyans
+ Fordh y\'n Drehevyans
+ Fordh y\'n Drehevyans
+ Fordh y\'n Drehevyans
+ Fordh y\'n Drehevyans
+ Fordh y\'n Drehevyans
+ Fordh y\'n Drehevyans
+ Fordh y\'n Drehevyans
+ Fordh y\'n Drehevyans
+ Fordh y\'n Drehevyans
+ Fordh y\'n Drehevyans
+ Fordh y\'n Drehevyans
+ Fordh y\'n Drehevyans
+ Fordh y\'n Drehevyans
+ Fordh y\'n Drehevyans
+ Fordh y\'n Drehevyans
+ Fordh y\'n Drehevyans
Hyns rag Diwrosow
Pons
Hyns rag Diwrosow
diff --git a/android/sdk/src/main/res/values-lt/types_strings.xml b/android/sdk/src/main/res/values-lt/types_strings.xml
index 1b44e94f8..5f73fdd1b 100644
--- a/android/sdk/src/main/res/values-lt/types_strings.xml
+++ b/android/sdk/src/main/res/values-lt/types_strings.xml
@@ -466,7 +466,24 @@
Tiltas
Tunelis
Stotelė
+
Tiesiamas kelias
+ Tiesiamas kelias
+ Tiesiamas kelias
+ Tiesiamas kelias
+ Tiesiamas kelias
+ Tiesiamas kelias
+ Tiesiamas kelias
+ Tiesiamas kelias
+ Tiesiamas kelias
+ Tiesiamas kelias
+ Tiesiamas kelias
+ Tiesiamas kelias
+ Tiesiamas kelias
+ Tiesiamas kelias
+ Tiesiamas kelias
+ Tiesiamas kelias
+ Tiesiamas kelias
Dviračių takas
Tiltas
Dviračių takas
diff --git a/android/sdk/src/main/res/values-mr/types_strings.xml b/android/sdk/src/main/res/values-mr/types_strings.xml
index b8628957d..967171b06 100644
--- a/android/sdk/src/main/res/values-mr/types_strings.xml
+++ b/android/sdk/src/main/res/values-mr/types_strings.xml
@@ -290,7 +290,24 @@
बोगदा
बस थांबा
+
बांधकामाधीन रस्ता
+ बांधकामाधीन रस्ता
+ बांधकामाधीन रस्ता
+ बांधकामाधीन रस्ता
+ बांधकामाधीन रस्ता
+ बांधकामाधीन रस्ता
+ बांधकामाधीन रस्ता
+ बांधकामाधीन रस्ता
+ बांधकामाधीन रस्ता
+ बांधकामाधीन रस्ता
+ बांधकामाधीन रस्ता
+ बांधकामाधीन रस्ता
+ बांधकामाधीन रस्ता
+ बांधकामाधीन रस्ता
+ बांधकामाधीन रस्ता
+ बांधकामाधीन रस्ता
+ बांधकामाधीन रस्ता
सायकल वाट
पूल
diff --git a/android/sdk/src/main/res/values-nb/types_strings.xml b/android/sdk/src/main/res/values-nb/types_strings.xml
index c3b4a5905..36fdb7d5b 100644
--- a/android/sdk/src/main/res/values-nb/types_strings.xml
+++ b/android/sdk/src/main/res/values-nb/types_strings.xml
@@ -337,7 +337,24 @@
Tunnel
Busstopp
+
Veikonstruksjon
+ Veikonstruksjon
+ Veikonstruksjon
+ Veikonstruksjon
+ Veikonstruksjon
+ Veikonstruksjon
+ Veikonstruksjon
+ Veikonstruksjon
+ Veikonstruksjon
+ Veikonstruksjon
+ Veikonstruksjon
+ Veikonstruksjon
+ Veikonstruksjon
+ Veikonstruksjon
+ Veikonstruksjon
+ Veikonstruksjon
+ Veikonstruksjon
Sykkelvei
Bru
diff --git a/android/sdk/src/main/res/values-nl/types_strings.xml b/android/sdk/src/main/res/values-nl/types_strings.xml
index 1659fc12c..9b69c0f26 100644
--- a/android/sdk/src/main/res/values-nl/types_strings.xml
+++ b/android/sdk/src/main/res/values-nl/types_strings.xml
@@ -145,7 +145,7 @@
GFTE afval
Drankverpakkingen
Restaurant
- Caravantoilet cassette-leegpunt
+ Camperverzorgingsplaats
School
Schuilplaats
@@ -167,7 +167,7 @@
Sigarettenautomaat
Koffieautomaat
Condoomautomaat
- Drankautomaat
+ Drankenautomaat
Etenswarenautomaat
Krantenautomaat
Parkeerautomaat
@@ -180,7 +180,7 @@
Dierenarts
Vuilnisbak
Afvalcontainer
- Overslagstation voor afval
+ Afvaloverslagstation
Waterpunt
Waterpunt
Barrière
@@ -238,7 +238,7 @@
Brouwerij
Cateraar
Timmerman
- Banketbakkerij
+ Snoepmakerij
Elektricien
Elektronica-reparatie
Tuinarchitect
@@ -279,7 +279,7 @@
Koffie
Pannenkoek
Kroatisch
- Curry\'s
+ Curry
Delicatessen
Diner
Donuts
@@ -292,7 +292,7 @@
Georgisch
Duits
Grieks
- Gegrilde gerechten
+ Grill
Heuriger
Hotdogs
Hongaars
@@ -353,7 +353,7 @@
Ingang
Hoofdingang
- Uitgang (enig)
+ Uitgang (alleen uitgang)
Gratis
Medisch laboratorium
Fysiotherapeut
@@ -373,13 +373,30 @@
Ruiterpad
Tunnel
- Speciale busweg
+ Busbaan
Brug
Tunnel
Bushalte
- Baan in aanbouw
+
+ Weg in aanbouw
+ Weg in aanbouw
+ Weg in aanbouw
+ Weg in aanbouw
+ Weg in aanbouw
+ Weg in aanbouw
+ Weg in aanbouw
+ Weg in aanbouw
+ Weg in aanbouw
+ Weg in aanbouw
+ Weg in aanbouw
+ Weg in aanbouw
+ Weg in aanbouw
+ Weg in aanbouw
+ Weg in aanbouw
+ Weg in aanbouw
+ Weg in aanbouw
Fietspad
Fietsbrug
@@ -396,7 +413,7 @@
Voetgangerstunnel
Voorde
- Straat
+ Woonerf
Brug
@@ -407,7 +424,7 @@
Snelwegtunnel
Afrit
- Snelwegoprit
+ Snelwegoprit/afrit
Brug
@@ -416,7 +433,7 @@
Moeilijk of slecht zichtbaar pad
- Zeer moeilijk of niet te onderscheiden spoor
+ Zeer moeilijk of niet te onderscheiden pad
Fiets- & voetpad
Fiets- & voetpad
@@ -491,27 +508,27 @@
Brug
Tunnel
- Straat
- Straat
+ Bos-/veldweg
+ Bos-/veldweg
Brug
- Straat
- Straat
+ Bos-/veldweg
+ Bos-/veldweg
Tunnel
Verkeerslichten
- Straat
+ Autoweg
Brug
Tunnel
- Straat
+ Autowegoprit/afrit
Brug
Tunnel
- Straat
- Straat
+ Weg
+ Weg
Brug
@@ -525,12 +542,12 @@
Hoofdweg
Straat
Secundaire weg
- Straat
+ Serviceweg
Tertiaire weg
- Trappen
- Straat
- Straat
- Straat
+ Trap
+ Bos-/veldweg
+ Autoweg
+ Weg
Historisch object
Historische vliegtuigen
@@ -546,7 +563,7 @@
Vesting
Heuvelfort
Kremlin
- Manoir
+ Landhuis
Paleis
Japans kasteel
Landhuis
@@ -566,7 +583,7 @@
Historische mijn
Monument
Schandpaal
- Ruïne
+ Historische ruïne
Historisch schip
Historische tank
Graftombe
@@ -575,7 +592,7 @@
Wegkruis
Kruisbeeld
- Schipbreuk
+ Schipwrak
Internet
Draadloos internet
Kruispunt
@@ -590,35 +607,35 @@
Christelijke begraafplaats
Commercieel gebied
Bouwplaats
- Educatieve voorzieningen
+ Onderwijsgebied
Landbouwgrond
Boerenerf
- Bloemenbed
+ Bloembed
Bos
Naaldbos
Loofbos
Gemengd bos
Garages
Gazon
- Kassen
+ Kas
Industrieterrein
Stortplaats
Weiland
Militair terrein
Boomgaard
Steengroeve
- Spoorwegfaciliteiten
+ Spoorwegterrein
Recreatiezone
Woonwijk
- Detailhandel
- Zoutpoel
+ Detailhandelsgebied
+ Zoutpan
Dorpspark
Wijngaard
- Ontspanning
+ Recreatie
Openbare grond
Hondenuitlaatplek
Fitnesscentrum
- Fitness-Station
+ Fitness-station
Danszaal
Tuin
Tuin
@@ -629,9 +646,9 @@
Natuurreservaat
Zitplaatsen buiten
Park
- Park
+ Privépark
Park
- Park
+ Privépark
Picknicktafel
Sportveld
Speeltuin
@@ -643,18 +660,18 @@
Yoga
Stadion
Zwembad
- Privé zwembad
+ Privézwembad
Parcours
Parcours
Waterpark
Strandresort
- Kunstmatige constructies
+ Kunstmatige constructie
Golfbreker
- Steengroeve
+ Steenmannetje
Fabrieksschoorsteen
- Bospad
+ Cutline
Meetpunt
- Vlaggenpaal
+ Vlaggenmast
Vuurtoren
Mast
Pier
@@ -680,7 +697,7 @@
Waterput
Windmolen
Fabriek
- Militair
+ Militair object
Bunker
Bergpas
Natuur
@@ -695,9 +712,9 @@
Zandstrand
Kiezelstrand
Kaap
- Grot
+ Grotingang
Klif
- Klif
+ Aardhelling
Baanlichaam
Kustlijn
Woestijn
@@ -707,8 +724,8 @@
Heide
Hete bron
Meer
- Slotkamer
- Plas
+ Schutkolk
+ Vijver
Reservoir
Waterbassin
Rivier
@@ -739,7 +756,7 @@
NGO-kantoor
Mobiele provider
Alleen biologisch
- o.a. biologisch
+ Biologische opties
Stad
Hoofdstad
Stad
@@ -770,72 +787,72 @@
Zee
Plein
Deelstaat
- Deelstaat
+ Staat
Buitenwijk
Stad
Dorp
- Electriciteit
+ Elektriciteit
Zonne-generator
- Wind generator
- Gasturbine elektriciteitscentrale
+ Windgenerator
+ Gascentrale
Waterkrachtcentrale
Hoogspanningsleiding
Ondergrondse hoogspanningsleiding
Laag-/middelspanningsleiding
Energiecentrale
- Kolen elektriciteitscentrale
- Gasturbine elektriciteitscentrale
+ Kolencentrale
+ Gascentrale
Waterkrachtcentrale
Zonne-energiecentrale
Windkrachtcentrale
Transformatorstation
- Elektriciteitsmast
+ Hoogspanningsmast
Openbaar vervoer
- Platform
+ Perron
Spoorweg
Voormalig treinspoor
- Treinspoor in aanbouw
+ Spoor in aanbouw
Spoorwegovergang
Ongebruikt spoor
- Tunnel
- Kabelspoorbuur
+ Kabelspoor
+ Kabelspoorbrug
Kabelspoortunnel
Station
Spoorwegovergang
Lightrail
- Lightrail-brug
- Lightrail-tunnel
+ Lightrailbrug
+ Lightrailtunnel
Monorail
- Monorail-brug
- Monorail-tunnel
- Smalspoorbaan
+ Monorailbrug
+ Monorailtunnel
+ Smalspoor
Smalspoorbrug
Smalspoortunnel
Perron
- Museumspoorbaan
- Museumspoorbaanbrug
- Museumspoorbaantunnel
- Spoorlijn
- Hogesnelheidstrein
+ Museumspoorweg
+ Museumspoorbrug
+ Museumspoortunnel
+ Spoor
+ Hogesnelheidsspoor
Toeristische spoorlijn
Spoor
- Secundaire spoorwegen
+ Secundair spoor
- Spoorwegen
+ Dienstspoor
Zijspoor
Hulpspoor
- Treinspoorbrug
- Treinspoorbrug
- Treinspoorbrug
- Treinspoorbrug
- Treinspoorbrug
- Treinspoorbrug
- Treinspoorbrug
- Treinspoorbrug
+ Spoorbrug
+ Spoorbrug
+ Spoorbrug
+ Spoorbrug
+ Spoorbrug
+ Spoorbrug
+ Spoorbrug
+ Spoorbrug
Spoortunnel
Spoortunnel
Spoortunnel
@@ -845,12 +862,12 @@
Spoortunnel
Spoortunnel
Station
- Tunnel
- Station
+ Kabelspoorstation
+ Lightrailstation
S-bahn station
Station
Station
- Station
+ Monorailstation
Metrostation
Metrostation
Metrostation
@@ -965,124 +982,124 @@
Metrostation
Metrostation
Metrostation
- Metro
+ Metrolijn
Metrobrug
Metrotunnel
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Metro ingang
- Tram
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Metro-ingang
+ Tramspoor
Trambrug
Tramtunnel
Tramhalte
@@ -1093,27 +1110,27 @@
Bakkerij
Badkamerinrichting
Schoonheidssalon
- Drank
+ Drankenwinkel
Fietsenwinkel
- Boekbinder
+ Wedkantoor
Boekwinkel
Slager
Coffeeshop
- Autohandelaar
- Auto-onderdelen
- Auto reparatie
+ Autodealer
+ Auto-onderdelenwinkel
+ Autogarage
Bandenreparatie
- Caravan en camper verkoper
+ Caravan- en camperdealer
Tapijtenwinkel
Drogisterij
Chocolaterie
Kledingwinkel
- Koffieverkoop
+ Koffiewinkel
Computerwinkel
Snoepwinkel
- Buurtwinkel
+ Gemakswinkel
Kopieerwinkel
- Schoonheidsmiddelen
+ Cosmeticawinkel
Gordijnenwinkel
Delicatessenwinkel
Warenhuis
@@ -1123,21 +1140,21 @@
Erotiekwinkel
Stoffenwinkel
Boerderijwinkel
- Modeaccessoires
+ Modeaccessoirewinkel
Bloemist
- Begrafenisondernemer
+ Uitvaartondernemer
Meubelwinkel
Tuincentrum
Gaswinkel
Cadeauwinkel
Groentenwinkel
- Boodschappen
+ Kruidenierswinkel
Kapper
- IJzerwaren
+ IJzerwarenwinkel
Natuurvoedingswinkel
- Winkel met hoortoestellen
+ Hoortoestellenwinkel
Kruidenwinkel
- HiFi-audio
+ Hifiwinkel
Huishoudwinkel
Juwelier
Kiosk
@@ -1145,18 +1162,18 @@
Wasserette
Winkelcentrum
Massagesalon
- Mobiele telefoonwinkel
+ Mobiele-telefoonwinkel
Geldschieter
Motorzaak
Motorfietsreparatie
Muziekwinkel
Muziekinstrumentenwinkel
- Kiosk
+ Krantenkiosk
Opticien
- Outdooruitrusting
+ Buitensportwinkel
Afhaalpunt
Pastawinkel
- Banketbakker
+ Banketbakkerij
Pandjeshuis
Dierenwinkel
Huisdierverzorging
@@ -1166,10 +1183,10 @@
Visboer
Tweedehandswinkel
Schoenenwinkel
- Sportartikelen
+ Sportwinkel
Kantoorboekhandel
Supermarkt
- Tattoosalon
+ Tatoeagestudio
Theewinkel
Kaartjesverkoop
Speelgoedwinkel
@@ -1184,27 +1201,27 @@
Witgoedwinkel
Kunstwinkel
- Babyspullenwinkel
+ Babywinkel
Tassenwinkel
Beddenwinkel
Boetiek
Kringloopwinkel
Kaaswinkel
- Kunst en ambacht
- Zuivelproducten
+ Kunst- en hobbywinkel
+ Zuivelwinkel
Elektronische benodigdhedenwinkel
Viswinkel
Interieurdecoratiewinkel
- Loten
- Medische benodigdheden
+ Loterijwinkel
+ Medische benodigdhedenwinkel
Voedingssupplementenwinkel
Verfwinkel
Parfumerie
- Naaibenodigdheden
+ Naai- en fourniturenwinkel
Opslagverhuur
- Tabakszaak
- Handelsbenodigdheden
- Horlogezaak
+ Tabakswinkel
+ Technische groothandel
+ Horlogewinkel
Groothandel
Sport
American football
@@ -1214,18 +1231,18 @@
Honkbal
Basketbal
Beachvolleybal
- Bowling
+ Bowls
Schaken
Cricket
Curling
- Paardesport
+ Paardensport
Golf
Gymnastiek
Handbal
Diverse sporten
Duiken
- schieten
+ Schietsport
Skateboarden
Skiën
Voetbal
@@ -1236,57 +1253,57 @@
Bowlen
Bowlen
Padel
- Futsal
+ Zaalvoetbal
IJshockey
Veldhockey
Badminton
- Baskische peloton
+ Pelota
Tourisme
Aquarium
Berghut
- Appartement
+ Vakantieappartement
Kunstwerk
- Kunstwerk
+ Architectonisch kunstwerk
Schilderij
- Kunstwerk
+ Sculptuur
Standbeeld
- Toeristische attractie
- Prettocht
+ Bezienswaardigheid
+ Attractie
Dierenverblijf
Botsauto\'s
- Reuzenrad
+ Draaimolen
Historische attractie
Doolhof
Achtbaan
Waterglijbaan
Attractie
- Kampeerterrein
- Caravan site
+ Camping
+ Caravan- en camperplaats
Vakantiehuis
- Galerij
- Gasthuis
+ Galerie
+ Pension
Jeugdherberg
Hotel
Toeristische informatie
Informatiebord
Wegwijzer
Toeristische kaart
- Toeristische informatie
+ Toeristeninformatiepunt
Bezoekerscentrum
Museum
- Picnicplaats
+ Picknickplaats
Complex
Attractiepark
Uitkijkpunt
- Wildernishut
+ Zelfverzorgingshut
Dierentuin
Kinderboerderij
- Snelheidsmatigende maatregel
+ Snelheidsremmende maatregel
Verkeersdrempel
- Verkeersheuvel
+ Verkeersdrempel
Waterweg
Kanaal
Kanaal
@@ -1309,37 +1326,37 @@
Waterval
Stuw
Rolstoel
- Gedeeltelijk uitgerust voor gehandicapten
- Niet uitgerust voor gehandicapten
- Uitgerust voor gehandicapten
+ Gedeeltelijk rolstoeltoegankelijk
+ Niet rolstoeltoegankelijk
+ Volledig rolstoeltoegankelijk
Sleeplift (J)
- Loopband
- Sleeplift
+ Lopende band (skilift)
+ Pannenkoeklift
Touwlift
Sleeplift (T-beugel)
- Ski-afdaling
- Ski-afdaling
- Moeilijke afdaling
- Moeilijke afdaling
- Makkelijke afdaling
- Makkelijke afdaling
- Afdaling voor experts
- Afdaling voor experts
- Ski-afdaling
- Gemiddelde afdaling
- Gemiddelde afdaling
- Beginnersafdaling
- Beginnersafdaling
+ Skipiste
+ Skipiste
+ Moeilijke skipiste
+ Moeilijke skipiste
+ Makkelijke skipiste
+ Makkelijke skipiste
+ Zeer moeilijke skipiste
+ Zeer moeilijke skipiste
+ Freeride ski-afdaling
+ Gemiddelde skipiste
+ Gemiddelde skipiste
+ Beginnersskipiste
+ Beginnersskipiste
Langlaufroute
Sleebaan
Sleebaan
Sneeuwpark
Sneeuwwandelpad
Pisteverbinding
- Skitourspoor
+ Toerskiroute
Evenementenlocatie
Veiling
- Verzamelobjecten
+ Verzamelaarswinkel
Zelfbediening beschikbaar
Alleen zelfbediening
Gedeeltelijke zelfbediening
@@ -1354,7 +1371,7 @@
Sporthal
Bubbelthee
€
- Stroommast
+ Elektriciteitsmast
Paal
Fish and Chips
Reservoir
@@ -1362,7 +1379,7 @@
Reuzenrad
Afschotbeschutting
Braakliggend terrein
- Greenfield
+ Bouwgrond
Generator
Hackerspace
Foodcourt
@@ -1387,7 +1404,7 @@
Overdekte fietsenstalling
Post- en pakketpunt
Escaperoom
- Boswachterkantoor
+ Boswachterskantoor
Dierenasiel
Klinket
Beveiligingskantoor
@@ -1403,7 +1420,7 @@
Observatorium
Carpool
Reddingsboei
- Muziekpodium
+ Muziekkoepel
Sportcentrum
Sportcentrum
Sportcentrum
@@ -1437,14 +1454,14 @@
Sportcentrum
Zwemcentrum
Telecommunicatiewinkel
- Leegstand bedrijfspand
+ Leegstaand bedrijfspand
Gaarkeuken
Voedselbank
Verzamelpunt
Yogastudio
Ligbed
Betaalkantoor
- Speelhal
+ Indoor speeltuin
Zeilschool
Vliegschool
Bijscholing
@@ -1483,7 +1500,7 @@
Huis entree
Garage entree
Dienstingang
- Ingang (enig)
+ Ingang (alleen ingang)
Nooduitgang
Kerstboom
Schoonheidssalon
@@ -1493,13 +1510,13 @@
Geïsoleerde gevaarsboei
Sferische geïsoleerde gevaarsboei
Nagelsalon
- Voedsel delen
- Giftenkist
+ Voedseldeelpunt
+ Weggeefkast
Watertappunt|waterkraan
- Miniatuur spoorweg
- Miniatuur spoorbrug
- Miniatuur spoortunnel
- Spoorweg-keerpunt
+ Miniatuurspoorweg
+ Miniatuurspoorbrug
+ Miniatuurspoortunnel
+ Spoordraaischijf
Tactiele kaart
Ongebruikte spoorbrug
Ongebruikte spoortunnel
@@ -1514,8 +1531,8 @@
Four Square
Sportcentrum
Sportcentrum
- jeu de boules
+ Jeu de boules
Sportcentrum
Pickleball
- Niet biologisch
+ Geen biologische opties
diff --git a/android/sdk/src/main/res/values-pl/types_strings.xml b/android/sdk/src/main/res/values-pl/types_strings.xml
index 18294752c..9a0806436 100644
--- a/android/sdk/src/main/res/values-pl/types_strings.xml
+++ b/android/sdk/src/main/res/values-pl/types_strings.xml
@@ -380,7 +380,24 @@
Tunel
Przystanek autobusowy
+
Droga w trakcie budowy
+ Droga w trakcie budowy
+ Droga w trakcie budowy
+ Droga w trakcie budowy
+ Droga w trakcie budowy
+ Droga w trakcie budowy
+ Droga w trakcie budowy
+ Droga w trakcie budowy
+ Droga w trakcie budowy
+ Droga w trakcie budowy
+ Droga w trakcie budowy
+ Droga w trakcie budowy
+ Droga w trakcie budowy
+ Droga w trakcie budowy
+ Droga w trakcie budowy
+ Droga w trakcie budowy
+ Droga w trakcie budowy
Droga rowerowa
Most drogowy dla rowerów
diff --git a/android/sdk/src/main/res/values-pt-rBR/types_strings.xml b/android/sdk/src/main/res/values-pt-rBR/types_strings.xml
index 8bcfffa2a..b28cadd25 100644
--- a/android/sdk/src/main/res/values-pt-rBR/types_strings.xml
+++ b/android/sdk/src/main/res/values-pt-rBR/types_strings.xml
@@ -333,7 +333,24 @@
Túnel
Ponto de ônibus
+
Via em construção
+ Via em construção
+ Via em construção
+ Via em construção
+ Via em construção
+ Via em construção
+ Via em construção
+ Via em construção
+ Via em construção
+ Via em construção
+ Via em construção
+ Via em construção
+ Via em construção
+ Via em construção
+ Via em construção
+ Via em construção
+ Via em construção
Ciclovia
Ponte para ciclistas
diff --git a/android/sdk/src/main/res/values-pt/types_strings.xml b/android/sdk/src/main/res/values-pt/types_strings.xml
index 95d457e82..dd7b42fd1 100644
--- a/android/sdk/src/main/res/values-pt/types_strings.xml
+++ b/android/sdk/src/main/res/values-pt/types_strings.xml
@@ -368,7 +368,24 @@
Túnel
Paragem de autocarros
+
Estrada em construção
+ Estrada em construção
+ Estrada em construção
+ Estrada em construção
+ Estrada em construção
+ Estrada em construção
+ Estrada em construção
+ Estrada em construção
+ Estrada em construção
+ Estrada em construção
+ Estrada em construção
+ Estrada em construção
+ Estrada em construção
+ Estrada em construção
+ Estrada em construção
+ Estrada em construção
+ Estrada em construção
Ciclovia
Ponte
diff --git a/android/sdk/src/main/res/values-ro/types_strings.xml b/android/sdk/src/main/res/values-ro/types_strings.xml
index 8cce5160e..a566ed311 100644
--- a/android/sdk/src/main/res/values-ro/types_strings.xml
+++ b/android/sdk/src/main/res/values-ro/types_strings.xml
@@ -322,7 +322,24 @@
Tunel
Stație de autobuz
+
Drum în construcție
+ Drum în construcție
+ Drum în construcție
+ Drum în construcție
+ Drum în construcție
+ Drum în construcție
+ Drum în construcție
+ Drum în construcție
+ Drum în construcție
+ Drum în construcție
+ Drum în construcție
+ Drum în construcție
+ Drum în construcție
+ Drum în construcție
+ Drum în construcție
+ Drum în construcție
+ Drum în construcție
Pod
diff --git a/android/sdk/src/main/res/values-ru/types_strings.xml b/android/sdk/src/main/res/values-ru/types_strings.xml
index a498d2f31..b9f104442 100644
--- a/android/sdk/src/main/res/values-ru/types_strings.xml
+++ b/android/sdk/src/main/res/values-ru/types_strings.xml
@@ -386,7 +386,24 @@
Тоннель
Остановка
+
Строящаяся дорога
+ Строящаяся дорога
+ Строящаяся дорога
+ Строящаяся дорога
+ Строящаяся дорога
+ Строящаяся дорога
+ Строящаяся дорога
+ Строящаяся дорога
+ Строящаяся дорога
+ Строящаяся дорога
+ Строящаяся дорога
+ Строящаяся дорога
+ Строящаяся дорога
+ Строящаяся дорога
+ Строящаяся дорога
+ Строящаяся дорога
+ Строящаяся дорога
Велодорожка
Мост
diff --git a/android/sdk/src/main/res/values-sk/types_strings.xml b/android/sdk/src/main/res/values-sk/types_strings.xml
index c20f3af01..3fe46e90f 100644
--- a/android/sdk/src/main/res/values-sk/types_strings.xml
+++ b/android/sdk/src/main/res/values-sk/types_strings.xml
@@ -374,7 +374,24 @@
Tunel
Autobusová zastávka
+
Cesta vo výstavbe
+ Cesta vo výstavbe
+ Cesta vo výstavbe
+ Cesta vo výstavbe
+ Cesta vo výstavbe
+ Cesta vo výstavbe
+ Cesta vo výstavbe
+ Cesta vo výstavbe
+ Cesta vo výstavbe
+ Cesta vo výstavbe
+ Cesta vo výstavbe
+ Cesta vo výstavbe
+ Cesta vo výstavbe
+ Cesta vo výstavbe
+ Cesta vo výstavbe
+ Cesta vo výstavbe
+ Cesta vo výstavbe
Cyklocesta
Most
diff --git a/android/sdk/src/main/res/values-sl/types_strings.xml b/android/sdk/src/main/res/values-sl/types_strings.xml
index 1ccc079f5..6fda6fedb 100644
--- a/android/sdk/src/main/res/values-sl/types_strings.xml
+++ b/android/sdk/src/main/res/values-sl/types_strings.xml
@@ -498,7 +498,24 @@
Most
Predor
Avtobusno postajališče
+
Cesta v gradnji
+ Cesta v gradnji
+ Cesta v gradnji
+ Cesta v gradnji
+ Cesta v gradnji
+ Cesta v gradnji
+ Cesta v gradnji
+ Cesta v gradnji
+ Cesta v gradnji
+ Cesta v gradnji
+ Cesta v gradnji
+ Cesta v gradnji
+ Cesta v gradnji
+ Cesta v gradnji
+ Cesta v gradnji
+ Cesta v gradnji
+ Cesta v gradnji
Kolesarska steza
Most
Kolesarska steza
diff --git a/android/sdk/src/main/res/values-sr/types_strings.xml b/android/sdk/src/main/res/values-sr/types_strings.xml
index 85f564cfb..48577eb07 100644
--- a/android/sdk/src/main/res/values-sr/types_strings.xml
+++ b/android/sdk/src/main/res/values-sr/types_strings.xml
@@ -383,7 +383,24 @@
Тунел
Аутобуско стајалиште
+
Пут у изградњи
+ Пут у изградњи
+ Пут у изградњи
+ Пут у изградњи
+ Пут у изградњи
+ Пут у изградњи
+ Пут у изградњи
+ Пут у изградњи
+ Пут у изградњи
+ Пут у изградњи
+ Пут у изградњи
+ Пут у изградњи
+ Пут у изградњи
+ Пут у изградњи
+ Пут у изградњи
+ Пут у изградњи
+ Пут у изградњи
Бициклистичка стаза
Мост
diff --git a/android/sdk/src/main/res/values-sv/types_strings.xml b/android/sdk/src/main/res/values-sv/types_strings.xml
index 3e1aa59d5..7ba2636d8 100644
--- a/android/sdk/src/main/res/values-sv/types_strings.xml
+++ b/android/sdk/src/main/res/values-sv/types_strings.xml
@@ -325,7 +325,24 @@
Tunnel
Busshållplats
+
Väg under uppförande
+ Väg under uppförande
+ Väg under uppförande
+ Väg under uppförande
+ Väg under uppförande
+ Väg under uppförande
+ Väg under uppförande
+ Väg under uppförande
+ Väg under uppförande
+ Väg under uppförande
+ Väg under uppförande
+ Väg under uppförande
+ Väg under uppförande
+ Väg under uppförande
+ Väg under uppförande
+ Väg under uppförande
+ Väg under uppförande
Cykelbro
diff --git a/android/sdk/src/main/res/values-sw/types_strings.xml b/android/sdk/src/main/res/values-sw/types_strings.xml
index d8e1e523d..95b44a2ce 100644
--- a/android/sdk/src/main/res/values-sw/types_strings.xml
+++ b/android/sdk/src/main/res/values-sw/types_strings.xml
@@ -131,7 +131,24 @@
Daraja
Mtaro
+
Barabara inatengenezwa
+ Barabara inatengenezwa
+ Barabara inatengenezwa
+ Barabara inatengenezwa
+ Barabara inatengenezwa
+ Barabara inatengenezwa
+ Barabara inatengenezwa
+ Barabara inatengenezwa
+ Barabara inatengenezwa
+ Barabara inatengenezwa
+ Barabara inatengenezwa
+ Barabara inatengenezwa
+ Barabara inatengenezwa
+ Barabara inatengenezwa
+ Barabara inatengenezwa
+ Barabara inatengenezwa
+ Barabara inatengenezwa
Daraja
diff --git a/android/sdk/src/main/res/values-ta/types_strings.xml b/android/sdk/src/main/res/values-ta/types_strings.xml
index 05dcd1825..9bb5e3f0a 100644
--- a/android/sdk/src/main/res/values-ta/types_strings.xml
+++ b/android/sdk/src/main/res/values-ta/types_strings.xml
@@ -386,7 +386,24 @@
பாலம்
சுரங்கப்பாதை
பேருந்து நிறுத்தம்
+
சாலை கட்டுமானத்தில் உள்ளது
+ சாலை கட்டுமானத்தில் உள்ளது
+ சாலை கட்டுமானத்தில் உள்ளது
+ சாலை கட்டுமானத்தில் உள்ளது
+ சாலை கட்டுமானத்தில் உள்ளது
+ சாலை கட்டுமானத்தில் உள்ளது
+ சாலை கட்டுமானத்தில் உள்ளது
+ சாலை கட்டுமானத்தில் உள்ளது
+ சாலை கட்டுமானத்தில் உள்ளது
+ சாலை கட்டுமானத்தில் உள்ளது
+ சாலை கட்டுமானத்தில் உள்ளது
+ சாலை கட்டுமானத்தில் உள்ளது
+ சாலை கட்டுமானத்தில் உள்ளது
+ சாலை கட்டுமானத்தில் உள்ளது
+ சாலை கட்டுமானத்தில் உள்ளது
+ சாலை கட்டுமானத்தில் உள்ளது
+ சாலை கட்டுமானத்தில் உள்ளது
சைக்கிள் பாதை
பாலம்
சைக்கிள் பாதை
diff --git a/android/sdk/src/main/res/values-th/types_strings.xml b/android/sdk/src/main/res/values-th/types_strings.xml
index 202ca4b7a..5291511e8 100644
--- a/android/sdk/src/main/res/values-th/types_strings.xml
+++ b/android/sdk/src/main/res/values-th/types_strings.xml
@@ -331,7 +331,24 @@
อุโมงค์
ป้ายรถเมล์
+
ทางกำลังอยู่ในการก่อสร้าง
+ ทางกำลังอยู่ในการก่อสร้าง
+ ทางกำลังอยู่ในการก่อสร้าง
+ ทางกำลังอยู่ในการก่อสร้าง
+ ทางกำลังอยู่ในการก่อสร้าง
+ ทางกำลังอยู่ในการก่อสร้าง
+ ทางกำลังอยู่ในการก่อสร้าง
+ ทางกำลังอยู่ในการก่อสร้าง
+ ทางกำลังอยู่ในการก่อสร้าง
+ ทางกำลังอยู่ในการก่อสร้าง
+ ทางกำลังอยู่ในการก่อสร้าง
+ ทางกำลังอยู่ในการก่อสร้าง
+ ทางกำลังอยู่ในการก่อสร้าง
+ ทางกำลังอยู่ในการก่อสร้าง
+ ทางกำลังอยู่ในการก่อสร้าง
+ ทางกำลังอยู่ในการก่อสร้าง
+ ทางกำลังอยู่ในการก่อสร้าง
สะพาน
diff --git a/android/sdk/src/main/res/values-tr/types_strings.xml b/android/sdk/src/main/res/values-tr/types_strings.xml
index 3fb63a9d9..d0f9d0c1b 100644
--- a/android/sdk/src/main/res/values-tr/types_strings.xml
+++ b/android/sdk/src/main/res/values-tr/types_strings.xml
@@ -376,7 +376,24 @@
Tünel
Otobüs Durağı
+
Yapım Aşamasında Yol
+ Yapım Aşamasında Yol
+ Yapım Aşamasında Yol
+ Yapım Aşamasında Yol
+ Yapım Aşamasında Yol
+ Yapım Aşamasında Yol
+ Yapım Aşamasında Yol
+ Yapım Aşamasında Yol
+ Yapım Aşamasında Yol
+ Yapım Aşamasında Yol
+ Yapım Aşamasında Yol
+ Yapım Aşamasında Yol
+ Yapım Aşamasında Yol
+ Yapım Aşamasında Yol
+ Yapım Aşamasında Yol
+ Yapım Aşamasında Yol
+ Yapım Aşamasında Yol
Bisiklet Yolu
Köprü
diff --git a/android/sdk/src/main/res/values-uk/types_strings.xml b/android/sdk/src/main/res/values-uk/types_strings.xml
index b2297ed76..ad54caf50 100644
--- a/android/sdk/src/main/res/values-uk/types_strings.xml
+++ b/android/sdk/src/main/res/values-uk/types_strings.xml
@@ -381,7 +381,24 @@
Тунель
Зупинка
+
Дорога, що будується
+ Дорога, що будується
+ Дорога, що будується
+ Дорога, що будується
+ Дорога, що будується
+ Дорога, що будується
+ Дорога, що будується
+ Дорога, що будується
+ Дорога, що будується
+ Дорога, що будується
+ Дорога, що будується
+ Дорога, що будується
+ Дорога, що будується
+ Дорога, що будується
+ Дорога, що будується
+ Дорога, що будується
+ Дорога, що будується
Велодоріжка
Міст
diff --git a/android/sdk/src/main/res/values-vi/types_strings.xml b/android/sdk/src/main/res/values-vi/types_strings.xml
index 128f8f60f..d35888d66 100644
--- a/android/sdk/src/main/res/values-vi/types_strings.xml
+++ b/android/sdk/src/main/res/values-vi/types_strings.xml
@@ -329,7 +329,24 @@
Đường hầm
Bến xe buýt
+
Đường đang thi công
+ Đường đang thi công
+ Đường đang thi công
+ Đường đang thi công
+ Đường đang thi công
+ Đường đang thi công
+ Đường đang thi công
+ Đường đang thi công
+ Đường đang thi công
+ Đường đang thi công
+ Đường đang thi công
+ Đường đang thi công
+ Đường đang thi công
+ Đường đang thi công
+ Đường đang thi công
+ Đường đang thi công
+ Đường đang thi công
Cầu
diff --git a/android/sdk/src/main/res/values-zh-rHK/types_strings.xml b/android/sdk/src/main/res/values-zh-rHK/types_strings.xml
index ece4baf03..2f0dc49e2 100644
--- a/android/sdk/src/main/res/values-zh-rHK/types_strings.xml
+++ b/android/sdk/src/main/res/values-zh-rHK/types_strings.xml
@@ -379,7 +379,24 @@
隧道
巴士站
+
在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
自行車道
橋
diff --git a/android/sdk/src/main/res/values-zh-rTW/types_strings.xml b/android/sdk/src/main/res/values-zh-rTW/types_strings.xml
index 73032d099..1d996d812 100644
--- a/android/sdk/src/main/res/values-zh-rTW/types_strings.xml
+++ b/android/sdk/src/main/res/values-zh-rTW/types_strings.xml
@@ -626,7 +626,24 @@
橋樑
隧道
公車站
+
施工中道路
+ 施工中道路
+ 施工中道路
+ 施工中道路
+ 施工中道路
+ 施工中道路
+ 施工中道路
+ 施工中道路
+ 施工中道路
+ 施工中道路
+ 施工中道路
+ 施工中道路
+ 施工中道路
+ 施工中道路
+ 施工中道路
+ 施工中道路
+ 施工中道路
單車道
橋樑
單車道
diff --git a/android/sdk/src/main/res/values-zh/types_strings.xml b/android/sdk/src/main/res/values-zh/types_strings.xml
index da409ba11..b5afc1da0 100644
--- a/android/sdk/src/main/res/values-zh/types_strings.xml
+++ b/android/sdk/src/main/res/values-zh/types_strings.xml
@@ -380,7 +380,24 @@
隧道
公交站
+
在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
+ 在建道路
自行车道
桥
diff --git a/android/sdk/src/main/res/values/types_strings.xml b/android/sdk/src/main/res/values/types_strings.xml
index feb7aedc5..5dfd855c3 100644
--- a/android/sdk/src/main/res/values/types_strings.xml
+++ b/android/sdk/src/main/res/values/types_strings.xml
@@ -456,7 +456,24 @@
Tunnel
Bus Stop
+
Road Under Construction
+ Road Under Construction
+ Road Under Construction
+ Road Under Construction
+ Road Under Construction
+ Road Under Construction
+ Road Under Construction
+ Road Under Construction
+ Road Under Construction
+ Road Under Construction
+ Road Under Construction
+ Road Under Construction
+ Road Under Construction
+ Road Under Construction
+ Road Under Construction
+ Road Under Construction
+ Road Under Construction
Cycle Path
Bridge
diff --git a/data/mapcss-mapping.csv b/data/mapcss-mapping.csv
index 2b5a43e86..7f0470359 100644
--- a/data/mapcss-mapping.csv
+++ b/data/mapcss-mapping.csv
@@ -916,22 +916,22 @@ leisure|sports_centre|sport|soccer;[leisure=sports_centre][sport=soccer];;name;i
leisure|sports_centre|sport|four_square;[leisure=sports_centre][sport=four_square];;name;int_name;787;
leisure|sports_centre|sport|boules;[leisure=sports_centre][sport=boules];;name;int_name;788;
leisure|sports_centre|sport|pickleball;[leisure=sports_centre][sport=pickleball];;name;int_name;789;
-deprecated|deprecated;790;x
-deprecated|deprecated;791;x
-deprecated|deprecated;792;x
-deprecated|deprecated;793;x
-deprecated|deprecated;794;x
-deprecated|deprecated;795;x
-deprecated|deprecated;796;x
-deprecated|deprecated;797;x
-deprecated|deprecated;798;x
-deprecated|deprecated;799;x
-deprecated|deprecated;800;x
-deprecated|deprecated;801;x
-deprecated|deprecated;802;x
-deprecated|deprecated;803;x
-deprecated|deprecated;804;x
-deprecated|deprecated;805;x
+highway|construction|motorway;[highway=construction][construction=motorway];;name;int_name;790;
+highway|construction|motorway_link;[highway=construction][construction=motorway_link];;name;int_name;791;
+highway|construction|trunk;[highway=construction][construction=trunk];;name;int_name;792;
+highway|construction|trunk_link;[highway=construction][construction=trunk_link];;name;int_name;793;
+highway|construction|primary;[highway=construction][construction=primary];;name;int_name;794;
+highway|construction|primary_link;[highway=construction][construction=primary_link];;name;int_name;795;
+highway|construction|secondary;[highway=construction][construction=secondary];;name;int_name;796;
+highway|construction|secondary_link;[highway=construction][construction=secondary_link];;name;int_name;797;
+highway|construction|tertiary;[highway=construction][construction=tertiary];;name;int_name;798;
+highway|construction|tertiary_link;[highway=construction][construction=tertiary_link];;name;int_name;799;
+highway|construction|residential;[highway=construction][construction=residential];;name;int_name;800;
+highway|construction|unclassified;[highway=construction][construction=unclassified];;name;int_name;801;
+highway|construction|service;[highway=construction][construction=service];;name;int_name;802;
+highway|construction|living_street;[highway=construction][construction=living_street];;name;int_name;803;
+highway|construction|road;[highway=construction][construction=road];;name;int_name;804;
+highway|construction|track;[highway=construction][construction=track];;name;int_name;805;
deprecated|deprecated;806;x
deprecated|deprecated;807;x
deprecated|deprecated;808;x
diff --git a/data/styles/default/include/priorities_3_FG.prio.txt b/data/styles/default/include/priorities_3_FG.prio.txt
index 7fe56ebeb..9befbf7a1 100644
--- a/data/styles/default/include/priorities_3_FG.prio.txt
+++ b/data/styles/default/include/priorities_3_FG.prio.txt
@@ -293,6 +293,22 @@ highway-track-tunnel # line z15- (also has line::
=== 190
highway-construction # line z13- (also has pathtext z15-)
+highway-construction-living_street # line z13- (also has pathtext z15-)
+highway-construction-motorway # line z13- (also has pathtext z15-)
+highway-construction-motorway_link # line z13- (also has pathtext z15-)
+highway-construction-primary # line z13- (also has pathtext z15-)
+highway-construction-primary_link # line z13- (also has pathtext z15-)
+highway-construction-residential # line z13- (also has pathtext z15-)
+highway-construction-road # line z13- (also has pathtext z15-)
+highway-construction-secondary # line z13- (also has pathtext z15-)
+highway-construction-secondary_link # line z13- (also has pathtext z15-)
+highway-construction-service # line z13- (also has pathtext z15-)
+highway-construction-tertiary # line z13- (also has pathtext z15-)
+highway-construction-tertiary_link # line z13- (also has pathtext z15-)
+highway-construction-track # line z13- (also has pathtext z15-)
+highway-construction-trunk # line z13- (also has pathtext z15-)
+highway-construction-trunk_link # line z13- (also has pathtext z15-)
+highway-construction-unclassified # line z13- (also has pathtext z15-)
leisure-track # line z15- (also has caption z16-)
railway-abandoned # line z16-
railway-construction # line z15-
diff --git a/data/styles/default/include/priorities_4_overlays.prio.txt b/data/styles/default/include/priorities_4_overlays.prio.txt
index 1d898111d..337f1166d 100644
--- a/data/styles/default/include/priorities_4_overlays.prio.txt
+++ b/data/styles/default/include/priorities_4_overlays.prio.txt
@@ -838,6 +838,22 @@ attraction-maze # icon z16- (also has captio
attraction-roller_coaster # icon z16- (also has caption(optional) z16-)
attraction-water_slide # icon z17- (also has caption(optional) z17-)
highway-construction # pathtext z15- (also has line z13-)
+highway-construction-living_street # pathtext z15- (also has line z13-)
+highway-construction-motorway # pathtext z15- (also has line z13-)
+highway-construction-motorway_link # pathtext z15- (also has line z13-)
+highway-construction-primary # pathtext z15- (also has line z13-)
+highway-construction-primary_link # pathtext z15- (also has line z13-)
+highway-construction-residential # pathtext z15- (also has line z13-)
+highway-construction-road # pathtext z15- (also has line z13-)
+highway-construction-secondary # pathtext z15- (also has line z13-)
+highway-construction-secondary_link # pathtext z15- (also has line z13-)
+highway-construction-service # pathtext z15- (also has line z13-)
+highway-construction-tertiary # pathtext z15- (also has line z13-)
+highway-construction-tertiary_link # pathtext z15- (also has line z13-)
+highway-construction-track # pathtext z15- (also has line z13-)
+highway-construction-trunk # pathtext z15- (also has line z13-)
+highway-construction-trunk_link # pathtext z15- (also has line z13-)
+highway-construction-unclassified # pathtext z15- (also has line z13-)
highway-living_street # pathtext z14- (also has line z12-, line::border z14-)
highway-living_street-bridge # pathtext z14- (also has line z12-, line::border z14-)
highway-living_street-tunnel # pathtext z14- (also has line z12-, line::border z14-, line(casing) z16-)
diff --git a/data/styles/outdoors/include/priorities_3_FG.prio.txt b/data/styles/outdoors/include/priorities_3_FG.prio.txt
index c9dd78f72..0c0e76124 100644
--- a/data/styles/outdoors/include/priorities_3_FG.prio.txt
+++ b/data/styles/outdoors/include/priorities_3_FG.prio.txt
@@ -295,6 +295,22 @@ highway-track-tunnel # line z11- (also has line::
=== 190
highway-construction # line z11- (also has pathtext z15-)
+highway-construction-living_street # line z11- (also has pathtext z15-)
+highway-construction-motorway # line z11- (also has pathtext z15-)
+highway-construction-motorway_link # line z11- (also has pathtext z15-)
+highway-construction-primary # line z11- (also has pathtext z15-)
+highway-construction-primary_link # line z11- (also has pathtext z15-)
+highway-construction-residential # line z11- (also has pathtext z15-)
+highway-construction-road # line z11- (also has pathtext z15-)
+highway-construction-secondary # line z11- (also has pathtext z15-)
+highway-construction-secondary_link # line z11- (also has pathtext z15-)
+highway-construction-service # line z11- (also has pathtext z15-)
+highway-construction-tertiary # line z11- (also has pathtext z15-)
+highway-construction-tertiary_link # line z11- (also has pathtext z15-)
+highway-construction-track # line z11- (also has pathtext z15-)
+highway-construction-trunk # line z11- (also has pathtext z15-)
+highway-construction-trunk_link # line z11- (also has pathtext z15-)
+highway-construction-unclassified # line z11- (also has pathtext z15-)
leisure-track # line z15- (also has caption z16-)
railway-abandoned # line z13-
railway-construction # line z13-
diff --git a/data/styles/outdoors/include/priorities_4_overlays.prio.txt b/data/styles/outdoors/include/priorities_4_overlays.prio.txt
index 1785479cc..2320321bc 100644
--- a/data/styles/outdoors/include/priorities_4_overlays.prio.txt
+++ b/data/styles/outdoors/include/priorities_4_overlays.prio.txt
@@ -838,6 +838,22 @@ attraction-maze # icon z16- (also has captio
attraction-roller_coaster # icon z16- (also has caption(optional) z16-)
attraction-water_slide # icon z17- (also has caption(optional) z17-)
highway-construction # pathtext z15- (also has line z11-)
+highway-construction-living_street # pathtext z15- (also has line z11-)
+highway-construction-motorway # pathtext z15- (also has line z11-)
+highway-construction-motorway_link # pathtext z15- (also has line z11-)
+highway-construction-primary # pathtext z15- (also has line z11-)
+highway-construction-primary_link # pathtext z15- (also has line z11-)
+highway-construction-residential # pathtext z15- (also has line z11-)
+highway-construction-road # pathtext z15- (also has line z11-)
+highway-construction-secondary # pathtext z15- (also has line z11-)
+highway-construction-secondary_link # pathtext z15- (also has line z11-)
+highway-construction-service # pathtext z15- (also has line z11-)
+highway-construction-tertiary # pathtext z15- (also has line z11-)
+highway-construction-tertiary_link # pathtext z15- (also has line z11-)
+highway-construction-track # pathtext z15- (also has line z11-)
+highway-construction-trunk # pathtext z15- (also has line z11-)
+highway-construction-trunk_link # pathtext z15- (also has line z11-)
+highway-construction-unclassified # pathtext z15- (also has line z11-)
highway-living_street # pathtext z14- (also has line z12-, line::border z12-)
highway-living_street-bridge # pathtext z14- (also has line z12-, line::border z12-)
highway-living_street-tunnel # pathtext z14- (also has line z12-, line::border z12-, line(casing) z16-)
diff --git a/data/styles/vehicle/include/priorities_3_FG.prio.txt b/data/styles/vehicle/include/priorities_3_FG.prio.txt
index 2a2904281..9068150fe 100644
--- a/data/styles/vehicle/include/priorities_3_FG.prio.txt
+++ b/data/styles/vehicle/include/priorities_3_FG.prio.txt
@@ -271,6 +271,22 @@ highway-track-no-access # line z16-
=== 130
highway-construction # line z13-
+highway-construction-living_street # line z13-
+highway-construction-motorway # line z13-
+highway-construction-motorway_link # line z13-
+highway-construction-primary # line z13-
+highway-construction-primary_link # line z13-
+highway-construction-residential # line z13-
+highway-construction-road # line z13-
+highway-construction-secondary # line z13-
+highway-construction-secondary_link # line z13-
+highway-construction-service # line z13-
+highway-construction-tertiary # line z13-
+highway-construction-tertiary_link # line z13-
+highway-construction-track # line z13-
+highway-construction-trunk # line z13-
+highway-construction-trunk_link # line z13-
+highway-construction-unclassified # line z13-
railway-abandoned # line z16-
railway-construction # line z16-
railway-disused # line z16-
diff --git a/data/test_data/traff/DE-A10-Werder-GrossKreutz.xml b/data/test_data/traff/DE-A10-Werder-GrossKreutz.xml
new file mode 100644
index 000000000..6ea4707b8
--- /dev/null
+++ b/data/test_data/traff/DE-A10-Werder-GrossKreutz.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+ +52.334801 +12.814650
+ +52.393700 +12.835000
+
+
+
+
+
+
+
diff --git a/data/test_data/traff/DE-A115-PotsdamDrewitz-Nuthetal.xml b/data/test_data/traff/DE-A115-PotsdamDrewitz-Nuthetal.xml
new file mode 100644
index 000000000..3d1c93472
--- /dev/null
+++ b/data/test_data/traff/DE-A115-PotsdamDrewitz-Nuthetal.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+ +52.352650 +13.140700
+ +52.300201 +13.083500
+
+
+
+
+
+
+
diff --git a/data/test_data/traff/DE-B2R-LerchenauerStrasse-Petueltunnel.xml b/data/test_data/traff/DE-B2R-LerchenauerStrasse-Petueltunnel.xml
new file mode 100644
index 000000000..f8ac9beb7
--- /dev/null
+++ b/data/test_data/traff/DE-B2R-LerchenauerStrasse-Petueltunnel.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ +48.176102 +11.558100
+ +48.178001 +11.572800
+
+
+
+
+
+
+
diff --git a/data/test_data/traff/DE-B2R-SendlingSued-Passauerstrasse.xml b/data/test_data/traff/DE-B2R-SendlingSued-Passauerstrasse.xml
new file mode 100644
index 000000000..50df83776
--- /dev/null
+++ b/data/test_data/traff/DE-B2R-SendlingSued-Passauerstrasse.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+ +48.110901 +11.518500
+ +48.110649 +11.534150
+
+
+
+
+
+
+
diff --git a/data/test_data/traff/DE-GM4-UrbanDualCarriagewayEndpoint.xml b/data/test_data/traff/DE-GM4-UrbanDualCarriagewayEndpoint.xml
new file mode 100644
index 000000000..5340bdd5e
--- /dev/null
+++ b/data/test_data/traff/DE-GM4-UrbanDualCarriagewayEndpoint.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ +48.167301 +11.586200
+ +48.164200 +11.586500
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/test_data/traff/DE-RoundaboutEndpoint.xml b/data/test_data/traff/DE-RoundaboutEndpoint.xml
new file mode 100755
index 000000000..616834699
--- /dev/null
+++ b/data/test_data/traff/DE-RoundaboutEndpoint.xml
@@ -0,0 +1,58 @@
+
+
+
+
+ +48.661098 +7.936800
+ +48.683701 +7.916600
+
+
+
+
+
+
+
+
+
+
+ +49.822948 +7.906900
+ +49.803650 +7.901450
+
+
+
+
+
+
+
+
+
+
+ +49.239750 +8.222300
+ +49.253448 +8.268100
+
+
+
+
+
+
+
+
+
+
+ +50.372398 +8.038500
+ +50.370850 +8.004050
+
+
+
+
+
+
+
+
+
+
diff --git a/data/test_data/traff/LT-A5-Kaunas-Eastbound.xml b/data/test_data/traff/LT-A5-Kaunas-Eastbound.xml
new file mode 100644
index 000000000..d700afd4c
--- /dev/null
+++ b/data/test_data/traff/LT-A5-Kaunas-Eastbound.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+ 54.939945 23.879789
+ 54.940094 23.881950
+
+
+
+
+
+
+
+
diff --git a/defines.hpp b/defines.hpp
index 79b48e27a..eabc7ac63 100644
--- a/defines.hpp
+++ b/defines.hpp
@@ -109,8 +109,6 @@ auto constexpr TMP_OFFSETS_EXT = OFFSET_EXT EXTENSION_TMP;
#define CROSS_MWM_OSM_WAYS_DIR "cross_mwm_osm_ways"
#define TEMP_ADDR_EXTENSION ".tempaddr"
-#define TRAFFIC_FILE_EXTENSION ".traffic"
-
#define SKIPPED_ELEMENTS_FILE "skipped_elements.json"
#define MAPCSS_MAPPING_FILE "mapcss-mapping.csv"
diff --git a/generator/CMakeLists.txt b/generator/CMakeLists.txt
index 9818a561d..15e5e643d 100644
--- a/generator/CMakeLists.txt
+++ b/generator/CMakeLists.txt
@@ -205,8 +205,6 @@ set(SRC
tesselator.hpp
towns_dumper.cpp
towns_dumper.hpp
- traffic_generator.cpp
- traffic_generator.hpp
transit_generator.cpp
transit_generator.hpp
transit_generator_experimental.cpp
diff --git a/generator/generator_tool/generator_tool.cpp b/generator/generator_tool/generator_tool.cpp
index c0dcc8cef..75c887f9e 100644
--- a/generator/generator_tool/generator_tool.cpp
+++ b/generator/generator_tool/generator_tool.cpp
@@ -26,7 +26,6 @@
#include "generator/routing_world_roads_generator.hpp"
#include "generator/search_index_builder.hpp"
#include "generator/statistics.hpp"
-#include "generator/traffic_generator.hpp"
#include "generator/transit_generator.hpp"
#include "generator/transit_generator_experimental.hpp"
#include "generator/unpack_mwm.hpp"
@@ -171,7 +170,6 @@ DEFINE_string(unpack_borders, "", "Convert packed_polygons to a directory of pol
DEFINE_bool(unpack_mwm, false, "Unpack each section of mwm into a separate file with name filePath.sectionName.");
DEFINE_bool(check_mwm, false, "Check map file to be correct.");
DEFINE_string(delete_section, "", "Delete specified section (defines.hpp) from container.");
-DEFINE_bool(generate_traffic_keys, false, "Generate keys for the traffic map (road segment -> speed group).");
DEFINE_bool(dump_mwm_tmp, false, "Prints feature builder objects from .mwm.tmp");
@@ -546,12 +544,6 @@ MAIN_WITH_ERROR_HANDLING([](int argc, char ** argv)
BuildPopularPlacesFromDescriptions(dataFile);
}
}
-
- if (FLAGS_generate_traffic_keys)
- {
- if (!traffic::GenerateTrafficKeysFromDataFile(dataFile))
- LOG(LCRITICAL, ("Error generating traffic keys."));
- }
}
string const dataFile = base::JoinPath(path, FLAGS_output + DATA_FILE_EXTENSION);
diff --git a/generator/road_access_generator.cpp b/generator/road_access_generator.cpp
index 9c5707c14..0420e0967 100644
--- a/generator/road_access_generator.cpp
+++ b/generator/road_access_generator.cpp
@@ -220,6 +220,7 @@ RoadAccessTagProcessor::RoadAccessTagProcessor(VehicleType vehicleType) : m_vehi
switch (vehicleType)
{
case VehicleType::Car:
+ case VehicleType::Decoder:
// Order is important here starting from most specific (motorcar) to generic (access).
m_accessMappings.push_back(&kMotorCarTagMapping);
m_accessMappings.push_back(&kMotorVehicleTagMapping);
diff --git a/generator/routing_index_generator.cpp b/generator/routing_index_generator.cpp
index 094ab83b6..dfd411670 100644
--- a/generator/routing_index_generator.cpp
+++ b/generator/routing_index_generator.cpp
@@ -22,6 +22,7 @@
#include "routing_common/bicycle_model.hpp"
#include "routing_common/car_model.hpp"
+#include "routing_common/decoder_model.hpp"
#include "routing_common/pedestrian_model.hpp"
#include "indexer/feature.hpp"
@@ -61,19 +62,17 @@ class VehicleMaskBuilder final
: m_pedestrianModel(PedestrianModelFactory(countryParentNameGetterFn).GetVehicleModelForCountry(country))
, m_bicycleModel(BicycleModelFactory(countryParentNameGetterFn).GetVehicleModelForCountry(country))
, m_carModel(CarModelFactory(countryParentNameGetterFn).GetVehicleModelForCountry(country))
- , m_constructionType(classif().GetTypeByPath({"highway", "construction"}))
+ , m_decoderModel(DecoderModelFactory(countryParentNameGetterFn).GetVehicleModelForCountry(country))
{
CHECK(m_pedestrianModel, ());
CHECK(m_bicycleModel, ());
CHECK(m_carModel, ());
+ CHECK(m_decoderModel, ());
}
VehicleMask CalcRoadMask(FeatureType & f) const
{
feature::TypesHolder const types(f);
- if (types.HasWithSubclass(m_constructionType))
- return 0;
-
return CalcMask([&](VehicleModelInterface const & model) { return model.IsRoad(types); });
}
@@ -94,6 +93,8 @@ class VehicleMaskBuilder final
mask |= kBicycleMask;
if (fn(*m_carModel))
mask |= kCarMask;
+ if (fn(*m_decoderModel))
+ mask |= kDecoderMask;
return mask;
}
@@ -101,8 +102,7 @@ class VehicleMaskBuilder final
std::shared_ptr const m_pedestrianModel;
std::shared_ptr const m_bicycleModel;
std::shared_ptr const m_carModel;
-
- uint32_t const m_constructionType;
+ std::shared_ptr const m_decoderModel;
};
class Processor final
diff --git a/generator/traffic_generator.cpp b/generator/traffic_generator.cpp
deleted file mode 100644
index c2dd80217..000000000
--- a/generator/traffic_generator.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-#include "generator/traffic_generator.hpp"
-
-#include "routing/routing_helpers.hpp"
-
-#include "traffic/traffic_info.hpp"
-
-#include "routing_common/car_model.hpp"
-
-#include "platform/mwm_traits.hpp"
-
-#include "indexer/feature_algo.hpp"
-#include "indexer/feature_processor.hpp"
-#include "indexer/features_offsets_table.hpp"
-
-#include "coding/file_writer.hpp"
-#include "coding/files_container.hpp"
-
-#include
-
-namespace traffic
-{
-bool GenerateTrafficKeysFromDataFile(std::string const & mwmPath)
-{
- try
- {
- std::vector keys;
- TrafficInfo::ExtractTrafficKeys(mwmPath, keys);
-
- std::vector buf;
- TrafficInfo::SerializeTrafficKeys(keys, buf);
-
- FilesContainerW writeContainer(mwmPath, FileWriter::OP_WRITE_EXISTING);
- auto writer = writeContainer.GetWriter(TRAFFIC_KEYS_FILE_TAG);
- writer->Write(buf.data(), buf.size());
- }
- catch (RootException const & e)
- {
- LOG(LERROR, ("Failed to build traffic keys:", e.Msg()));
- return false;
- }
-
- return true;
-}
-} // namespace traffic
diff --git a/generator/traffic_generator.hpp b/generator/traffic_generator.hpp
deleted file mode 100644
index f09cc24cc..000000000
--- a/generator/traffic_generator.hpp
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma once
-
-#include
-
-namespace traffic
-{
-bool GenerateTrafficKeysFromDataFile(std::string const & mwmPath);
-} // namespace traffic
diff --git a/iphone/Maps/Core/Settings/MWMSettings.h b/iphone/Maps/Core/Settings/MWMSettings.h
index da011bfad..263155dfa 100644
--- a/iphone/Maps/Core/Settings/MWMSettings.h
+++ b/iphone/Maps/Core/Settings/MWMSettings.h
@@ -1,6 +1,12 @@
NS_SWIFT_NAME(SettingsBridge)
@interface MWMSettings : NSObject
++ (BOOL)liveTrafficEnabled;
++ (void)setLiveTrafficEnabled:(BOOL)liveTrafficEnabled;
+
++ (NSURL *)liveTrafficUrl;
++ (void)setLiveTrafficUrl:(NSURL *)liveTrafficUrl;
+
+ (BOOL)buildings3dViewEnabled;
+ (void)setBuildings3dViewEnabled:(BOOL)buildings3dViewEnabled;
diff --git a/iphone/Maps/Core/Settings/MWMSettings.mm b/iphone/Maps/Core/Settings/MWMSettings.mm
index 40f92d59d..ca2e42267 100644
--- a/iphone/Maps/Core/Settings/MWMSettings.mm
+++ b/iphone/Maps/Core/Settings/MWMSettings.mm
@@ -25,6 +25,40 @@
@implementation MWMSettings
++ (BOOL)liveTrafficEnabled;
+{
+ return GetFramework().LoadTrafficHttpEnabled();
+}
+
++ (void)setLiveTrafficEnabled:(BOOL)liveTrafficEnabled;
+{
+ auto &f = GetFramework();
+ f.SaveTrafficHttpEnabled(liveTrafficEnabled);
+ f.SetTrafficHttpEnabled(liveTrafficEnabled);
+}
+
++ (NSURL *)liveTrafficUrl;
+{
+ NSString * link = @(GetFramework().LoadTrafficHttpUrl().c_str());
+ if ([link length] == 0) {
+ return nil;
+ } else {
+ return [NSURL URLWithString:link];
+ }
+}
+
++ (void)setLiveTrafficUrl:(NSURL *)liveTrafficUrl;
+{
+ auto &f = GetFramework();
+ if (liveTrafficUrl == nil) {
+ f.SaveTrafficHttpUrl(@"".UTF8String);
+ f.SetTrafficHttpUrl(@"".UTF8String);
+ } else {
+ f.SaveTrafficHttpUrl(liveTrafficUrl.absoluteString.UTF8String);
+ f.SetTrafficHttpUrl(liveTrafficUrl.absoluteString.UTF8String);
+ }
+}
+
+ (BOOL)buildings3dViewEnabled;
{
bool _ = true, on = true;
diff --git a/iphone/Maps/LocalizedStrings/af.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/af.lproj/LocalizableTypes.strings
index cd7a56a43..f47faaab2 100644
--- a/iphone/Maps/LocalizedStrings/af.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/af.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tonnel";
"type.highway.bus_stop" = "Bushalte";
"type.highway.construction" = "Pad in aanbou";
+"type.highway.construction.motorway" = "Pad in aanbou";
+"type.highway.construction.motorway_link" = "Pad in aanbou";
+"type.highway.construction.trunk" = "Pad in aanbou";
+"type.highway.construction.trunk_link" = "Pad in aanbou";
+"type.highway.construction.primary" = "Pad in aanbou";
+"type.highway.construction.primary_link" = "Pad in aanbou";
+"type.highway.construction.secondary" = "Pad in aanbou";
+"type.highway.construction.secondary_link" = "Pad in aanbou";
+"type.highway.construction.tertiary" = "Pad in aanbou";
+"type.highway.construction.tertiary_link" = "Pad in aanbou";
+"type.highway.construction.residential" = "Pad in aanbou";
+"type.highway.construction.unclassified" = "Pad in aanbou";
+"type.highway.construction.service" = "Pad in aanbou";
+"type.highway.construction.living_street" = "Pad in aanbou";
+"type.highway.construction.road" = "Pad in aanbou";
+"type.highway.construction.track" = "Pad in aanbou";
"type.highway.cycleway" = "Fietspad";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Brug";
diff --git a/iphone/Maps/LocalizedStrings/ar.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/ar.lproj/LocalizableTypes.strings
index 33224871a..331e949ca 100644
--- a/iphone/Maps/LocalizedStrings/ar.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/ar.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "نفق";
"type.highway.bus_stop" = "موقف حافلات";
"type.highway.construction" = "طريق تحت الإنشاء";
+"type.highway.construction.motorway" = "طريق تحت الإنشاء";
+"type.highway.construction.motorway_link" = "طريق تحت الإنشاء";
+"type.highway.construction.trunk" = "طريق تحت الإنشاء";
+"type.highway.construction.trunk_link" = "طريق تحت الإنشاء";
+"type.highway.construction.primary" = "طريق تحت الإنشاء";
+"type.highway.construction.primary_link" = "طريق تحت الإنشاء";
+"type.highway.construction.secondary" = "طريق تحت الإنشاء";
+"type.highway.construction.secondary_link" = "طريق تحت الإنشاء";
+"type.highway.construction.tertiary" = "طريق تحت الإنشاء";
+"type.highway.construction.tertiary_link" = "طريق تحت الإنشاء";
+"type.highway.construction.residential" = "طريق تحت الإنشاء";
+"type.highway.construction.unclassified" = "طريق تحت الإنشاء";
+"type.highway.construction.service" = "طريق تحت الإنشاء";
+"type.highway.construction.living_street" = "طريق تحت الإنشاء";
+"type.highway.construction.road" = "طريق تحت الإنشاء";
+"type.highway.construction.track" = "طريق تحت الإنشاء";
"type.highway.cycleway" = "مسار للدراجات";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "جسر";
diff --git a/iphone/Maps/LocalizedStrings/az.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/az.lproj/LocalizableTypes.strings
index 7045faf53..1cc3d133c 100644
--- a/iphone/Maps/LocalizedStrings/az.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/az.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tunel";
"type.highway.bus_stop" = "Avtobus dayanacağı";
"type.highway.construction" = "Tikili Yol";
+"type.highway.construction.motorway" = "Tikili Yol";
+"type.highway.construction.motorway_link" = "Tikili Yol";
+"type.highway.construction.trunk" = "Tikili Yol";
+"type.highway.construction.trunk_link" = "Tikili Yol";
+"type.highway.construction.primary" = "Tikili Yol";
+"type.highway.construction.primary_link" = "Tikili Yol";
+"type.highway.construction.secondary" = "Tikili Yol";
+"type.highway.construction.secondary_link" = "Tikili Yol";
+"type.highway.construction.tertiary" = "Tikili Yol";
+"type.highway.construction.tertiary_link" = "Tikili Yol";
+"type.highway.construction.residential" = "Tikili Yol";
+"type.highway.construction.unclassified" = "Tikili Yol";
+"type.highway.construction.service" = "Tikili Yol";
+"type.highway.construction.living_street" = "Tikili Yol";
+"type.highway.construction.road" = "Tikili Yol";
+"type.highway.construction.track" = "Tikili Yol";
"type.highway.cycleway" = "Velosiped yolu";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Körpü";
diff --git a/iphone/Maps/LocalizedStrings/bg.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/bg.lproj/LocalizableTypes.strings
index 43a70662e..3b887aa1b 100644
--- a/iphone/Maps/LocalizedStrings/bg.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/bg.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Тунел";
"type.highway.bus_stop" = "Спирка";
"type.highway.construction" = "Път в процес на изграждане";
+"type.highway.construction.motorway" = "Път в процес на изграждане";
+"type.highway.construction.motorway_link" = "Път в процес на изграждане";
+"type.highway.construction.trunk" = "Път в процес на изграждане";
+"type.highway.construction.trunk_link" = "Път в процес на изграждане";
+"type.highway.construction.primary" = "Път в процес на изграждане";
+"type.highway.construction.primary_link" = "Път в процес на изграждане";
+"type.highway.construction.secondary" = "Път в процес на изграждане";
+"type.highway.construction.secondary_link" = "Път в процес на изграждане";
+"type.highway.construction.tertiary" = "Път в процес на изграждане";
+"type.highway.construction.tertiary_link" = "Път в процес на изграждане";
+"type.highway.construction.residential" = "Път в процес на изграждане";
+"type.highway.construction.unclassified" = "Път в процес на изграждане";
+"type.highway.construction.service" = "Път в процес на изграждане";
+"type.highway.construction.living_street" = "Път в процес на изграждане";
+"type.highway.construction.road" = "Път в процес на изграждане";
+"type.highway.construction.track" = "Път в процес на изграждане";
"type.highway.cycleway" = "Пътека за колела";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Мост";
diff --git a/iphone/Maps/LocalizedStrings/ca.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/ca.lproj/LocalizableTypes.strings
index a552c8947..389e1eb25 100644
--- a/iphone/Maps/LocalizedStrings/ca.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/ca.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Túnel";
"type.highway.bus_stop" = "Parada d’autobús";
"type.highway.construction" = "Via en construcció";
+"type.highway.construction.motorway" = "Via en construcció";
+"type.highway.construction.motorway_link" = "Via en construcció";
+"type.highway.construction.trunk" = "Via en construcció";
+"type.highway.construction.trunk_link" = "Via en construcció";
+"type.highway.construction.primary" = "Via en construcció";
+"type.highway.construction.primary_link" = "Via en construcció";
+"type.highway.construction.secondary" = "Via en construcció";
+"type.highway.construction.secondary_link" = "Via en construcció";
+"type.highway.construction.tertiary" = "Via en construcció";
+"type.highway.construction.tertiary_link" = "Via en construcció";
+"type.highway.construction.residential" = "Via en construcció";
+"type.highway.construction.unclassified" = "Via en construcció";
+"type.highway.construction.service" = "Via en construcció";
+"type.highway.construction.living_street" = "Via en construcció";
+"type.highway.construction.road" = "Via en construcció";
+"type.highway.construction.track" = "Via en construcció";
"type.highway.cycleway" = "Carril bici";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Pont";
diff --git a/iphone/Maps/LocalizedStrings/cs.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/cs.lproj/LocalizableTypes.strings
index c6fb365e1..487e5cbe2 100644
--- a/iphone/Maps/LocalizedStrings/cs.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/cs.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tunel";
"type.highway.bus_stop" = "Autobusová zastávka";
"type.highway.construction" = "Silnice v rekonstrukci";
+"type.highway.construction.motorway" = "Silnice v rekonstrukci";
+"type.highway.construction.motorway_link" = "Silnice v rekonstrukci";
+"type.highway.construction.trunk" = "Silnice v rekonstrukci";
+"type.highway.construction.trunk_link" = "Silnice v rekonstrukci";
+"type.highway.construction.primary" = "Silnice v rekonstrukci";
+"type.highway.construction.primary_link" = "Silnice v rekonstrukci";
+"type.highway.construction.secondary" = "Silnice v rekonstrukci";
+"type.highway.construction.secondary_link" = "Silnice v rekonstrukci";
+"type.highway.construction.tertiary" = "Silnice v rekonstrukci";
+"type.highway.construction.tertiary_link" = "Silnice v rekonstrukci";
+"type.highway.construction.residential" = "Silnice v rekonstrukci";
+"type.highway.construction.unclassified" = "Silnice v rekonstrukci";
+"type.highway.construction.service" = "Silnice v rekonstrukci";
+"type.highway.construction.living_street" = "Silnice v rekonstrukci";
+"type.highway.construction.road" = "Silnice v rekonstrukci";
+"type.highway.construction.track" = "Silnice v rekonstrukci";
"type.highway.cycleway" = "Cyklostezka";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Most";
diff --git a/iphone/Maps/LocalizedStrings/da.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/da.lproj/LocalizableTypes.strings
index 5ef17b5da..0397a1007 100644
--- a/iphone/Maps/LocalizedStrings/da.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/da.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tunnel";
"type.highway.bus_stop" = "Busstoppested";
"type.highway.construction" = "Vej under anlæggelse";
+"type.highway.construction.motorway" = "Vej under anlæggelse";
+"type.highway.construction.motorway_link" = "Vej under anlæggelse";
+"type.highway.construction.trunk" = "Vej under anlæggelse";
+"type.highway.construction.trunk_link" = "Vej under anlæggelse";
+"type.highway.construction.primary" = "Vej under anlæggelse";
+"type.highway.construction.primary_link" = "Vej under anlæggelse";
+"type.highway.construction.secondary" = "Vej under anlæggelse";
+"type.highway.construction.secondary_link" = "Vej under anlæggelse";
+"type.highway.construction.tertiary" = "Vej under anlæggelse";
+"type.highway.construction.tertiary_link" = "Vej under anlæggelse";
+"type.highway.construction.residential" = "Vej under anlæggelse";
+"type.highway.construction.unclassified" = "Vej under anlæggelse";
+"type.highway.construction.service" = "Vej under anlæggelse";
+"type.highway.construction.living_street" = "Vej under anlæggelse";
+"type.highway.construction.road" = "Vej under anlæggelse";
+"type.highway.construction.track" = "Vej under anlæggelse";
"type.highway.cycleway" = "Cykelsti";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Bro";
diff --git a/iphone/Maps/LocalizedStrings/de.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/de.lproj/LocalizableTypes.strings
index 0be87478c..d50f857bb 100644
--- a/iphone/Maps/LocalizedStrings/de.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/de.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tunnel";
"type.highway.bus_stop" = "Bushaltestelle";
"type.highway.construction" = "Straße im Bau";
+"type.highway.construction.motorway" = "Straße im Bau";
+"type.highway.construction.motorway_link" = "Straße im Bau";
+"type.highway.construction.trunk" = "Straße im Bau";
+"type.highway.construction.trunk_link" = "Straße im Bau";
+"type.highway.construction.primary" = "Straße im Bau";
+"type.highway.construction.primary_link" = "Straße im Bau";
+"type.highway.construction.secondary" = "Straße im Bau";
+"type.highway.construction.secondary_link" = "Straße im Bau";
+"type.highway.construction.tertiary" = "Straße im Bau";
+"type.highway.construction.tertiary_link" = "Straße im Bau";
+"type.highway.construction.residential" = "Straße im Bau";
+"type.highway.construction.unclassified" = "Straße im Bau";
+"type.highway.construction.service" = "Straße im Bau";
+"type.highway.construction.living_street" = "Straße im Bau";
+"type.highway.construction.road" = "Straße im Bau";
+"type.highway.construction.track" = "Straße im Bau";
"type.highway.cycleway" = "Radweg";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Brücke";
diff --git a/iphone/Maps/LocalizedStrings/el.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/el.lproj/LocalizableTypes.strings
index cbd6fb769..30c13237c 100644
--- a/iphone/Maps/LocalizedStrings/el.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/el.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Σήραγγα";
"type.highway.bus_stop" = "Στάση λεωφορείου";
"type.highway.construction" = "Οδός υπό κατασκευή";
+"type.highway.construction.motorway" = "Οδός υπό κατασκευή";
+"type.highway.construction.motorway_link" = "Οδός υπό κατασκευή";
+"type.highway.construction.trunk" = "Οδός υπό κατασκευή";
+"type.highway.construction.trunk_link" = "Οδός υπό κατασκευή";
+"type.highway.construction.primary" = "Οδός υπό κατασκευή";
+"type.highway.construction.primary_link" = "Οδός υπό κατασκευή";
+"type.highway.construction.secondary" = "Οδός υπό κατασκευή";
+"type.highway.construction.secondary_link" = "Οδός υπό κατασκευή";
+"type.highway.construction.tertiary" = "Οδός υπό κατασκευή";
+"type.highway.construction.tertiary_link" = "Οδός υπό κατασκευή";
+"type.highway.construction.residential" = "Οδός υπό κατασκευή";
+"type.highway.construction.unclassified" = "Οδός υπό κατασκευή";
+"type.highway.construction.service" = "Οδός υπό κατασκευή";
+"type.highway.construction.living_street" = "Οδός υπό κατασκευή";
+"type.highway.construction.road" = "Οδός υπό κατασκευή";
+"type.highway.construction.track" = "Οδός υπό κατασκευή";
"type.highway.cycleway" = "Ποδηλατόδρομος";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Γέφυρα";
diff --git a/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings
index cb1ae7f09..c9265653f 100644
--- a/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings
+++ b/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings
@@ -538,6 +538,14 @@
"editor_place_doesnt_exist" = "Place does not exist";
"editor_place_doesnt_exist_description" = "Describe what the place looks like now to send an error note to the OpenStreetMap community";
"text_more_button" = "…more";
+
+/* Live traffic data */
+"traffic_http" = "Live Traffic";
+"traffic_http_enabled" = "Enable live traffic data";
+"traffic_http_enabled_description" = "When enabled, the app will periodically retrieve traffic information from the configured URL.";
+"traffic_http_url" = "Traffic service URL";
+"traffic_http_url_not_set" = "Not set";
+
/* Phone number error message */
"error_enter_correct_phone" = "Enter a valid phone number";
"error_enter_correct_web" = "Enter a valid web address";
@@ -626,6 +634,7 @@
"privacy_policy" = "Privacy policy";
"terms_of_use" = "Terms of use";
"button_layer_subway" = "Metro";
+"button_layer_traffic" = "Traffic";
"layers_title" = "Map Styles and Layers";
"subway_data_unavailable" = "Metro map is unavailable";
"title_error_downloading_bookmarks" = "An error occurred";
diff --git a/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings
index cc6260d1d..f08ebb8b0 100644
--- a/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings
+++ b/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings
@@ -538,6 +538,14 @@
"editor_place_doesnt_exist" = "Place does not exist";
"editor_place_doesnt_exist_description" = "Describe what the place looks like now to send an error note to the OpenStreetMap community";
"text_more_button" = "…more";
+
+/* Live traffic data */
+"traffic_http" = "Live Traffic";
+"traffic_http_enabled" = "Enable live traffic data";
+"traffic_http_enabled_description" = "When enabled, the app will periodically retrieve traffic information from the configured URL.";
+"traffic_http_url" = "Traffic service URL";
+"traffic_http_url_not_set" = "Not set";
+
/* Phone number error message */
"error_enter_correct_phone" = "Enter a valid phone number";
"error_enter_correct_web" = "Enter a valid web address";
@@ -626,6 +634,7 @@
"privacy_policy" = "Privacy policy";
"terms_of_use" = "Terms of use";
"button_layer_subway" = "Subway";
+"button_layer_traffic" = "Traffic";
"layers_title" = "Map Styles and Layers";
"subway_data_unavailable" = "Subway map is unavailable";
"title_error_downloading_bookmarks" = "An error occurred";
diff --git a/iphone/Maps/LocalizedStrings/es-MX.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/es-MX.lproj/LocalizableTypes.strings
index cd4a82b12..34cb13dac 100644
--- a/iphone/Maps/LocalizedStrings/es-MX.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/es-MX.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Túnel";
"type.highway.bus_stop" = "Parada de autobús";
"type.highway.construction" = "Vía en construcción";
+"type.highway.construction.motorway" = "Vía en construcción";
+"type.highway.construction.motorway_link" = "Vía en construcción";
+"type.highway.construction.trunk" = "Vía en construcción";
+"type.highway.construction.trunk_link" = "Vía en construcción";
+"type.highway.construction.primary" = "Vía en construcción";
+"type.highway.construction.primary_link" = "Vía en construcción";
+"type.highway.construction.secondary" = "Vía en construcción";
+"type.highway.construction.secondary_link" = "Vía en construcción";
+"type.highway.construction.tertiary" = "Vía en construcción";
+"type.highway.construction.tertiary_link" = "Vía en construcción";
+"type.highway.construction.residential" = "Vía en construcción";
+"type.highway.construction.unclassified" = "Vía en construcción";
+"type.highway.construction.service" = "Vía en construcción";
+"type.highway.construction.living_street" = "Vía en construcción";
+"type.highway.construction.road" = "Vía en construcción";
+"type.highway.construction.track" = "Vía en construcción";
"type.highway.cycleway" = "Ciclovía";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Puente";
diff --git a/iphone/Maps/LocalizedStrings/es.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/es.lproj/LocalizableTypes.strings
index d1b2f4e87..ef978a6a7 100644
--- a/iphone/Maps/LocalizedStrings/es.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/es.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Túnel";
"type.highway.bus_stop" = "Parada de bus";
"type.highway.construction" = "Vía en construcción";
+"type.highway.construction.motorway" = "Vía en construcción";
+"type.highway.construction.motorway_link" = "Vía en construcción";
+"type.highway.construction.trunk" = "Vía en construcción";
+"type.highway.construction.trunk_link" = "Vía en construcción";
+"type.highway.construction.primary" = "Vía en construcción";
+"type.highway.construction.primary_link" = "Vía en construcción";
+"type.highway.construction.secondary" = "Vía en construcción";
+"type.highway.construction.secondary_link" = "Vía en construcción";
+"type.highway.construction.tertiary" = "Vía en construcción";
+"type.highway.construction.tertiary_link" = "Vía en construcción";
+"type.highway.construction.residential" = "Vía en construcción";
+"type.highway.construction.unclassified" = "Vía en construcción";
+"type.highway.construction.service" = "Vía en construcción";
+"type.highway.construction.living_street" = "Vía en construcción";
+"type.highway.construction.road" = "Vía en construcción";
+"type.highway.construction.track" = "Vía en construcción";
"type.highway.cycleway" = "Ciclovía";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Puente";
diff --git a/iphone/Maps/LocalizedStrings/et.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/et.lproj/LocalizableTypes.strings
index 5351c7076..3ebbfc57a 100644
--- a/iphone/Maps/LocalizedStrings/et.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/et.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tunnel";
"type.highway.bus_stop" = "Bussipeatus";
"type.highway.construction" = "Ehitusjärgus tee";
+"type.highway.construction.motorway" = "Ehitusjärgus tee";
+"type.highway.construction.motorway_link" = "Ehitusjärgus tee";
+"type.highway.construction.trunk" = "Ehitusjärgus tee";
+"type.highway.construction.trunk_link" = "Ehitusjärgus tee";
+"type.highway.construction.primary" = "Ehitusjärgus tee";
+"type.highway.construction.primary_link" = "Ehitusjärgus tee";
+"type.highway.construction.secondary" = "Ehitusjärgus tee";
+"type.highway.construction.secondary_link" = "Ehitusjärgus tee";
+"type.highway.construction.tertiary" = "Ehitusjärgus tee";
+"type.highway.construction.tertiary_link" = "Ehitusjärgus tee";
+"type.highway.construction.residential" = "Ehitusjärgus tee";
+"type.highway.construction.unclassified" = "Ehitusjärgus tee";
+"type.highway.construction.service" = "Ehitusjärgus tee";
+"type.highway.construction.living_street" = "Ehitusjärgus tee";
+"type.highway.construction.road" = "Ehitusjärgus tee";
+"type.highway.construction.track" = "Ehitusjärgus tee";
"type.highway.cycleway" = "Jalgrattatee";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Jalgrattasild";
diff --git a/iphone/Maps/LocalizedStrings/eu.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/eu.lproj/LocalizableTypes.strings
index 48cceea08..ae9d56c0d 100644
--- a/iphone/Maps/LocalizedStrings/eu.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/eu.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tunela";
"type.highway.bus_stop" = "Autobus geltokia";
"type.highway.construction" = "Eraikitzen ari diren errepidea";
+"type.highway.construction.motorway" = "Eraikitzen ari diren errepidea";
+"type.highway.construction.motorway_link" = "Eraikitzen ari diren errepidea";
+"type.highway.construction.trunk" = "Eraikitzen ari diren errepidea";
+"type.highway.construction.trunk_link" = "Eraikitzen ari diren errepidea";
+"type.highway.construction.primary" = "Eraikitzen ari diren errepidea";
+"type.highway.construction.primary_link" = "Eraikitzen ari diren errepidea";
+"type.highway.construction.secondary" = "Eraikitzen ari diren errepidea";
+"type.highway.construction.secondary_link" = "Eraikitzen ari diren errepidea";
+"type.highway.construction.tertiary" = "Eraikitzen ari diren errepidea";
+"type.highway.construction.tertiary_link" = "Eraikitzen ari diren errepidea";
+"type.highway.construction.residential" = "Eraikitzen ari diren errepidea";
+"type.highway.construction.unclassified" = "Eraikitzen ari diren errepidea";
+"type.highway.construction.service" = "Eraikitzen ari diren errepidea";
+"type.highway.construction.living_street" = "Eraikitzen ari diren errepidea";
+"type.highway.construction.road" = "Eraikitzen ari diren errepidea";
+"type.highway.construction.track" = "Eraikitzen ari diren errepidea";
"type.highway.cycleway" = "Bidegorri edo bizikleta-bidea";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Zubia";
diff --git a/iphone/Maps/LocalizedStrings/fa.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/fa.lproj/LocalizableTypes.strings
index b4874226f..b22b14412 100644
--- a/iphone/Maps/LocalizedStrings/fa.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/fa.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "تونل";
"type.highway.bus_stop" = "حمل و نقل";
"type.highway.construction" = "جاده در دست ساخت است";
+"type.highway.construction.motorway" = "جاده در دست ساخت است";
+"type.highway.construction.motorway_link" = "جاده در دست ساخت است";
+"type.highway.construction.trunk" = "جاده در دست ساخت است";
+"type.highway.construction.trunk_link" = "جاده در دست ساخت است";
+"type.highway.construction.primary" = "جاده در دست ساخت است";
+"type.highway.construction.primary_link" = "جاده در دست ساخت است";
+"type.highway.construction.secondary" = "جاده در دست ساخت است";
+"type.highway.construction.secondary_link" = "جاده در دست ساخت است";
+"type.highway.construction.tertiary" = "جاده در دست ساخت است";
+"type.highway.construction.tertiary_link" = "جاده در دست ساخت است";
+"type.highway.construction.residential" = "جاده در دست ساخت است";
+"type.highway.construction.unclassified" = "جاده در دست ساخت است";
+"type.highway.construction.service" = "جاده در دست ساخت است";
+"type.highway.construction.living_street" = "جاده در دست ساخت است";
+"type.highway.construction.road" = "جاده در دست ساخت است";
+"type.highway.construction.track" = "جاده در دست ساخت است";
"type.highway.cycleway" = "Cycle Path";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "پل";
diff --git a/iphone/Maps/LocalizedStrings/fi.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/fi.lproj/LocalizableTypes.strings
index b2a48f0c8..ec141413b 100644
--- a/iphone/Maps/LocalizedStrings/fi.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/fi.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tunneli";
"type.highway.bus_stop" = "Bussipysäkki";
"type.highway.construction" = "Rakenteilla oleva tie";
+"type.highway.construction.motorway" = "Rakenteilla oleva tie";
+"type.highway.construction.motorway_link" = "Rakenteilla oleva tie";
+"type.highway.construction.trunk" = "Rakenteilla oleva tie";
+"type.highway.construction.trunk_link" = "Rakenteilla oleva tie";
+"type.highway.construction.primary" = "Rakenteilla oleva tie";
+"type.highway.construction.primary_link" = "Rakenteilla oleva tie";
+"type.highway.construction.secondary" = "Rakenteilla oleva tie";
+"type.highway.construction.secondary_link" = "Rakenteilla oleva tie";
+"type.highway.construction.tertiary" = "Rakenteilla oleva tie";
+"type.highway.construction.tertiary_link" = "Rakenteilla oleva tie";
+"type.highway.construction.residential" = "Rakenteilla oleva tie";
+"type.highway.construction.unclassified" = "Rakenteilla oleva tie";
+"type.highway.construction.service" = "Rakenteilla oleva tie";
+"type.highway.construction.living_street" = "Rakenteilla oleva tie";
+"type.highway.construction.road" = "Rakenteilla oleva tie";
+"type.highway.construction.track" = "Rakenteilla oleva tie";
"type.highway.cycleway" = "Pyörätie";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Silta";
diff --git a/iphone/Maps/LocalizedStrings/fr.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/fr.lproj/LocalizableTypes.strings
index a59cef009..5e4f0431b 100644
--- a/iphone/Maps/LocalizedStrings/fr.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/fr.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tunnel";
"type.highway.bus_stop" = "Arrêt de bus";
"type.highway.construction" = "Route en construction";
+"type.highway.construction.motorway" = "Route en construction";
+"type.highway.construction.motorway_link" = "Route en construction";
+"type.highway.construction.trunk" = "Route en construction";
+"type.highway.construction.trunk_link" = "Route en construction";
+"type.highway.construction.primary" = "Route en construction";
+"type.highway.construction.primary_link" = "Route en construction";
+"type.highway.construction.secondary" = "Route en construction";
+"type.highway.construction.secondary_link" = "Route en construction";
+"type.highway.construction.tertiary" = "Route en construction";
+"type.highway.construction.tertiary_link" = "Route en construction";
+"type.highway.construction.residential" = "Route en construction";
+"type.highway.construction.unclassified" = "Route en construction";
+"type.highway.construction.service" = "Route en construction";
+"type.highway.construction.living_street" = "Route en construction";
+"type.highway.construction.road" = "Route en construction";
+"type.highway.construction.track" = "Route en construction";
"type.highway.cycleway" = "Piste cyclable";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Pont";
diff --git a/iphone/Maps/LocalizedStrings/gl.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/gl.lproj/LocalizableTypes.strings
index 01a65cd56..1570bad09 100644
--- a/iphone/Maps/LocalizedStrings/gl.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/gl.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Túnel";
"type.highway.bus_stop" = "Parada de autobús";
"type.highway.construction" = "Vía en construción";
+"type.highway.construction.motorway" = "Vía en construción";
+"type.highway.construction.motorway_link" = "Vía en construción";
+"type.highway.construction.trunk" = "Vía en construción";
+"type.highway.construction.trunk_link" = "Vía en construción";
+"type.highway.construction.primary" = "Vía en construción";
+"type.highway.construction.primary_link" = "Vía en construción";
+"type.highway.construction.secondary" = "Vía en construción";
+"type.highway.construction.secondary_link" = "Vía en construción";
+"type.highway.construction.tertiary" = "Vía en construción";
+"type.highway.construction.tertiary_link" = "Vía en construción";
+"type.highway.construction.residential" = "Vía en construción";
+"type.highway.construction.unclassified" = "Vía en construción";
+"type.highway.construction.service" = "Vía en construción";
+"type.highway.construction.living_street" = "Vía en construción";
+"type.highway.construction.road" = "Vía en construción";
+"type.highway.construction.track" = "Vía en construción";
"type.highway.cycleway" = "Ciclovía";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Puente";
diff --git a/iphone/Maps/LocalizedStrings/gsw.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/gsw.lproj/LocalizableTypes.strings
index 0d7a0159f..0748e8c45 100644
--- a/iphone/Maps/LocalizedStrings/gsw.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/gsw.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tunnel";
"type.highway.bus_stop" = "Bushaltistell";
"type.highway.construction" = "Strass im Bau";
+"type.highway.construction.motorway" = "Strass im Bau";
+"type.highway.construction.motorway_link" = "Strass im Bau";
+"type.highway.construction.trunk" = "Strass im Bau";
+"type.highway.construction.trunk_link" = "Strass im Bau";
+"type.highway.construction.primary" = "Strass im Bau";
+"type.highway.construction.primary_link" = "Strass im Bau";
+"type.highway.construction.secondary" = "Strass im Bau";
+"type.highway.construction.secondary_link" = "Strass im Bau";
+"type.highway.construction.tertiary" = "Strass im Bau";
+"type.highway.construction.tertiary_link" = "Strass im Bau";
+"type.highway.construction.residential" = "Strass im Bau";
+"type.highway.construction.unclassified" = "Strass im Bau";
+"type.highway.construction.service" = "Strass im Bau";
+"type.highway.construction.living_street" = "Strass im Bau";
+"type.highway.construction.road" = "Strass im Bau";
+"type.highway.construction.track" = "Strass im Bau";
"type.highway.cycleway" = "Veloweg";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Brugg";
diff --git a/iphone/Maps/LocalizedStrings/he.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/he.lproj/LocalizableTypes.strings
index eaae92700..144fc0041 100644
--- a/iphone/Maps/LocalizedStrings/he.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/he.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "מִנהָרָה";
"type.highway.bus_stop" = "תחנת אוטובוס";
"type.highway.construction" = "כביש בבניה";
+"type.highway.construction.motorway" = "כביש בבניה";
+"type.highway.construction.motorway_link" = "כביש בבניה";
+"type.highway.construction.trunk" = "כביש בבניה";
+"type.highway.construction.trunk_link" = "כביש בבניה";
+"type.highway.construction.primary" = "כביש בבניה";
+"type.highway.construction.primary_link" = "כביש בבניה";
+"type.highway.construction.secondary" = "כביש בבניה";
+"type.highway.construction.secondary_link" = "כביש בבניה";
+"type.highway.construction.tertiary" = "כביש בבניה";
+"type.highway.construction.tertiary_link" = "כביש בבניה";
+"type.highway.construction.residential" = "כביש בבניה";
+"type.highway.construction.unclassified" = "כביש בבניה";
+"type.highway.construction.service" = "כביש בבניה";
+"type.highway.construction.living_street" = "כביש בבניה";
+"type.highway.construction.road" = "כביש בבניה";
+"type.highway.construction.track" = "כביש בבניה";
"type.highway.cycleway" = "שביל אופניים";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "גשר לאופניים";
diff --git a/iphone/Maps/LocalizedStrings/hi.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/hi.lproj/LocalizableTypes.strings
index d00b5f6bf..979a024f0 100644
--- a/iphone/Maps/LocalizedStrings/hi.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/hi.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tunnel";
"type.highway.bus_stop" = "बस स्टॉप";
"type.highway.construction" = "निर्माणाधीन सड़क";
+"type.highway.construction.motorway" = "निर्माणाधीन सड़क";
+"type.highway.construction.motorway_link" = "निर्माणाधीन सड़क";
+"type.highway.construction.trunk" = "निर्माणाधीन सड़क";
+"type.highway.construction.trunk_link" = "निर्माणाधीन सड़क";
+"type.highway.construction.primary" = "निर्माणाधीन सड़क";
+"type.highway.construction.primary_link" = "निर्माणाधीन सड़क";
+"type.highway.construction.secondary" = "निर्माणाधीन सड़क";
+"type.highway.construction.secondary_link" = "निर्माणाधीन सड़क";
+"type.highway.construction.tertiary" = "निर्माणाधीन सड़क";
+"type.highway.construction.tertiary_link" = "निर्माणाधीन सड़क";
+"type.highway.construction.residential" = "निर्माणाधीन सड़क";
+"type.highway.construction.unclassified" = "निर्माणाधीन सड़क";
+"type.highway.construction.service" = "निर्माणाधीन सड़क";
+"type.highway.construction.living_street" = "निर्माणाधीन सड़क";
+"type.highway.construction.road" = "निर्माणाधीन सड़क";
+"type.highway.construction.track" = "निर्माणाधीन सड़क";
"type.highway.cycleway" = "साइकिल मार्ग";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Bridge";
diff --git a/iphone/Maps/LocalizedStrings/hu.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/hu.lproj/LocalizableTypes.strings
index 7312d8abe..292bb067c 100644
--- a/iphone/Maps/LocalizedStrings/hu.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/hu.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Alagút";
"type.highway.bus_stop" = "Buszmegálló";
"type.highway.construction" = "Útépítés";
+"type.highway.construction.motorway" = "Útépítés";
+"type.highway.construction.motorway_link" = "Útépítés";
+"type.highway.construction.trunk" = "Útépítés";
+"type.highway.construction.trunk_link" = "Útépítés";
+"type.highway.construction.primary" = "Útépítés";
+"type.highway.construction.primary_link" = "Útépítés";
+"type.highway.construction.secondary" = "Útépítés";
+"type.highway.construction.secondary_link" = "Útépítés";
+"type.highway.construction.tertiary" = "Útépítés";
+"type.highway.construction.tertiary_link" = "Útépítés";
+"type.highway.construction.residential" = "Útépítés";
+"type.highway.construction.unclassified" = "Útépítés";
+"type.highway.construction.service" = "Útépítés";
+"type.highway.construction.living_street" = "Útépítés";
+"type.highway.construction.road" = "Útépítés";
+"type.highway.construction.track" = "Útépítés";
"type.highway.cycleway" = "Kerékpárút";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Híd";
diff --git a/iphone/Maps/LocalizedStrings/id.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/id.lproj/LocalizableTypes.strings
index 65e0abc17..f31e4c265 100644
--- a/iphone/Maps/LocalizedStrings/id.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/id.lproj/LocalizableTypes.strings
@@ -430,6 +430,22 @@
"type.highway.busway.tunnel" = "Terowongan";
"type.highway.bus_stop" = "Halte bus";
"type.highway.construction" = "Jalan sedang dibangun";
+"type.highway.construction.motorway" = "Jalan sedang dibangun";
+"type.highway.construction.motorway_link" = "Jalan sedang dibangun";
+"type.highway.construction.trunk" = "Jalan sedang dibangun";
+"type.highway.construction.trunk_link" = "Jalan sedang dibangun";
+"type.highway.construction.primary" = "Jalan sedang dibangun";
+"type.highway.construction.primary_link" = "Jalan sedang dibangun";
+"type.highway.construction.secondary" = "Jalan sedang dibangun";
+"type.highway.construction.secondary_link" = "Jalan sedang dibangun";
+"type.highway.construction.tertiary" = "Jalan sedang dibangun";
+"type.highway.construction.tertiary_link" = "Jalan sedang dibangun";
+"type.highway.construction.residential" = "Jalan sedang dibangun";
+"type.highway.construction.unclassified" = "Jalan sedang dibangun";
+"type.highway.construction.service" = "Jalan sedang dibangun";
+"type.highway.construction.living_street" = "Jalan sedang dibangun";
+"type.highway.construction.road" = "Jalan sedang dibangun";
+"type.highway.construction.track" = "Jalan sedang dibangun";
"type.highway.cycleway" = "Jalur Sepeda";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Menjembatani";
diff --git a/iphone/Maps/LocalizedStrings/is.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/is.lproj/LocalizableTypes.strings
index 6df51d2f9..e3d4aded1 100644
--- a/iphone/Maps/LocalizedStrings/is.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/is.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Göng";
"type.highway.bus_stop" = "Strætisvagnabiðstöð";
"type.highway.construction" = "Vegur í byggingu";
+"type.highway.construction.motorway" = "Vegur í byggingu";
+"type.highway.construction.motorway_link" = "Vegur í byggingu";
+"type.highway.construction.trunk" = "Vegur í byggingu";
+"type.highway.construction.trunk_link" = "Vegur í byggingu";
+"type.highway.construction.primary" = "Vegur í byggingu";
+"type.highway.construction.primary_link" = "Vegur í byggingu";
+"type.highway.construction.secondary" = "Vegur í byggingu";
+"type.highway.construction.secondary_link" = "Vegur í byggingu";
+"type.highway.construction.tertiary" = "Vegur í byggingu";
+"type.highway.construction.tertiary_link" = "Vegur í byggingu";
+"type.highway.construction.residential" = "Vegur í byggingu";
+"type.highway.construction.unclassified" = "Vegur í byggingu";
+"type.highway.construction.service" = "Vegur í byggingu";
+"type.highway.construction.living_street" = "Vegur í byggingu";
+"type.highway.construction.road" = "Vegur í byggingu";
+"type.highway.construction.track" = "Vegur í byggingu";
"type.highway.cycleway" = "Hjólastígur";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Brú";
diff --git a/iphone/Maps/LocalizedStrings/it.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/it.lproj/LocalizableTypes.strings
index a53066363..7ff467b0e 100644
--- a/iphone/Maps/LocalizedStrings/it.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/it.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Galleria";
"type.highway.bus_stop" = "Fermata dell'autobus";
"type.highway.construction" = "Strada in costruzione";
+"type.highway.construction.motorway" = "Strada in costruzione";
+"type.highway.construction.motorway_link" = "Strada in costruzione";
+"type.highway.construction.trunk" = "Strada in costruzione";
+"type.highway.construction.trunk_link" = "Strada in costruzione";
+"type.highway.construction.primary" = "Strada in costruzione";
+"type.highway.construction.primary_link" = "Strada in costruzione";
+"type.highway.construction.secondary" = "Strada in costruzione";
+"type.highway.construction.secondary_link" = "Strada in costruzione";
+"type.highway.construction.tertiary" = "Strada in costruzione";
+"type.highway.construction.tertiary_link" = "Strada in costruzione";
+"type.highway.construction.residential" = "Strada in costruzione";
+"type.highway.construction.unclassified" = "Strada in costruzione";
+"type.highway.construction.service" = "Strada in costruzione";
+"type.highway.construction.living_street" = "Strada in costruzione";
+"type.highway.construction.road" = "Strada in costruzione";
+"type.highway.construction.track" = "Strada in costruzione";
"type.highway.cycleway" = "Pista ciclabile";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Ponte";
diff --git a/iphone/Maps/LocalizedStrings/ja.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/ja.lproj/LocalizableTypes.strings
index d738e2909..b5245d9ef 100644
--- a/iphone/Maps/LocalizedStrings/ja.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/ja.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "トンネル";
"type.highway.bus_stop" = "バス停";
"type.highway.construction" = "工事中の道路";
+"type.highway.construction.motorway" = "工事中の道路";
+"type.highway.construction.motorway_link" = "工事中の道路";
+"type.highway.construction.trunk" = "工事中の道路";
+"type.highway.construction.trunk_link" = "工事中の道路";
+"type.highway.construction.primary" = "工事中の道路";
+"type.highway.construction.primary_link" = "工事中の道路";
+"type.highway.construction.secondary" = "工事中の道路";
+"type.highway.construction.secondary_link" = "工事中の道路";
+"type.highway.construction.tertiary" = "工事中の道路";
+"type.highway.construction.tertiary_link" = "工事中の道路";
+"type.highway.construction.residential" = "工事中の道路";
+"type.highway.construction.unclassified" = "工事中の道路";
+"type.highway.construction.service" = "工事中の道路";
+"type.highway.construction.living_street" = "工事中の道路";
+"type.highway.construction.road" = "工事中の道路";
+"type.highway.construction.track" = "工事中の道路";
"type.highway.cycleway" = "自転車道";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "自転車道(橋)";
diff --git a/iphone/Maps/LocalizedStrings/ko.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/ko.lproj/LocalizableTypes.strings
index 0e670e6fa..b8d6b720b 100644
--- a/iphone/Maps/LocalizedStrings/ko.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/ko.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "터널";
"type.highway.bus_stop" = "버스 정류장";
"type.highway.construction" = "공사 중 도로";
+"type.highway.construction.motorway" = "공사 중 도로";
+"type.highway.construction.motorway_link" = "공사 중 도로";
+"type.highway.construction.trunk" = "공사 중 도로";
+"type.highway.construction.trunk_link" = "공사 중 도로";
+"type.highway.construction.primary" = "공사 중 도로";
+"type.highway.construction.primary_link" = "공사 중 도로";
+"type.highway.construction.secondary" = "공사 중 도로";
+"type.highway.construction.secondary_link" = "공사 중 도로";
+"type.highway.construction.tertiary" = "공사 중 도로";
+"type.highway.construction.tertiary_link" = "공사 중 도로";
+"type.highway.construction.residential" = "공사 중 도로";
+"type.highway.construction.unclassified" = "공사 중 도로";
+"type.highway.construction.service" = "공사 중 도로";
+"type.highway.construction.living_street" = "공사 중 도로";
+"type.highway.construction.road" = "공사 중 도로";
+"type.highway.construction.track" = "공사 중 도로";
"type.highway.cycleway" = "Cycle Path";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "다리";
diff --git a/iphone/Maps/LocalizedStrings/lt.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/lt.lproj/LocalizableTypes.strings
index 5f826c63c..afc6749b1 100644
--- a/iphone/Maps/LocalizedStrings/lt.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/lt.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tunelis";
"type.highway.bus_stop" = "Stotelė";
"type.highway.construction" = "Tiesiamas kelias";
+"type.highway.construction.motorway" = "Tiesiamas kelias";
+"type.highway.construction.motorway_link" = "Tiesiamas kelias";
+"type.highway.construction.trunk" = "Tiesiamas kelias";
+"type.highway.construction.trunk_link" = "Tiesiamas kelias";
+"type.highway.construction.primary" = "Tiesiamas kelias";
+"type.highway.construction.primary_link" = "Tiesiamas kelias";
+"type.highway.construction.secondary" = "Tiesiamas kelias";
+"type.highway.construction.secondary_link" = "Tiesiamas kelias";
+"type.highway.construction.tertiary" = "Tiesiamas kelias";
+"type.highway.construction.tertiary_link" = "Tiesiamas kelias";
+"type.highway.construction.residential" = "Tiesiamas kelias";
+"type.highway.construction.unclassified" = "Tiesiamas kelias";
+"type.highway.construction.service" = "Tiesiamas kelias";
+"type.highway.construction.living_street" = "Tiesiamas kelias";
+"type.highway.construction.road" = "Tiesiamas kelias";
+"type.highway.construction.track" = "Tiesiamas kelias";
"type.highway.cycleway" = "Dviračių takas";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Tiltas";
diff --git a/iphone/Maps/LocalizedStrings/mr.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/mr.lproj/LocalizableTypes.strings
index 14918935b..d73ca0f31 100644
--- a/iphone/Maps/LocalizedStrings/mr.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/mr.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "बोगदा";
"type.highway.bus_stop" = "बस थांबा";
"type.highway.construction" = "बांधकामाधीन रस्ता";
+"type.highway.construction.motorway" = "बांधकामाधीन रस्ता";
+"type.highway.construction.motorway_link" = "बांधकामाधीन रस्ता";
+"type.highway.construction.trunk" = "बांधकामाधीन रस्ता";
+"type.highway.construction.trunk_link" = "बांधकामाधीन रस्ता";
+"type.highway.construction.primary" = "बांधकामाधीन रस्ता";
+"type.highway.construction.primary_link" = "बांधकामाधीन रस्ता";
+"type.highway.construction.secondary" = "बांधकामाधीन रस्ता";
+"type.highway.construction.secondary_link" = "बांधकामाधीन रस्ता";
+"type.highway.construction.tertiary" = "बांधकामाधीन रस्ता";
+"type.highway.construction.tertiary_link" = "बांधकामाधीन रस्ता";
+"type.highway.construction.residential" = "बांधकामाधीन रस्ता";
+"type.highway.construction.unclassified" = "बांधकामाधीन रस्ता";
+"type.highway.construction.service" = "बांधकामाधीन रस्ता";
+"type.highway.construction.living_street" = "बांधकामाधीन रस्ता";
+"type.highway.construction.road" = "बांधकामाधीन रस्ता";
+"type.highway.construction.track" = "बांधकामाधीन रस्ता";
"type.highway.cycleway" = "सायकल वाट";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "पूल";
diff --git a/iphone/Maps/LocalizedStrings/nb.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/nb.lproj/LocalizableTypes.strings
index e97471363..811985abb 100644
--- a/iphone/Maps/LocalizedStrings/nb.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/nb.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tunnel";
"type.highway.bus_stop" = "Busstopp";
"type.highway.construction" = "Veikonstruksjon";
+"type.highway.construction.motorway" = "Veikonstruksjon";
+"type.highway.construction.motorway_link" = "Veikonstruksjon";
+"type.highway.construction.trunk" = "Veikonstruksjon";
+"type.highway.construction.trunk_link" = "Veikonstruksjon";
+"type.highway.construction.primary" = "Veikonstruksjon";
+"type.highway.construction.primary_link" = "Veikonstruksjon";
+"type.highway.construction.secondary" = "Veikonstruksjon";
+"type.highway.construction.secondary_link" = "Veikonstruksjon";
+"type.highway.construction.tertiary" = "Veikonstruksjon";
+"type.highway.construction.tertiary_link" = "Veikonstruksjon";
+"type.highway.construction.residential" = "Veikonstruksjon";
+"type.highway.construction.unclassified" = "Veikonstruksjon";
+"type.highway.construction.service" = "Veikonstruksjon";
+"type.highway.construction.living_street" = "Veikonstruksjon";
+"type.highway.construction.road" = "Veikonstruksjon";
+"type.highway.construction.track" = "Veikonstruksjon";
"type.highway.cycleway" = "Sykkelvei";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Bru";
diff --git a/iphone/Maps/LocalizedStrings/nl.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/nl.lproj/LocalizableTypes.strings
index 1119cabb8..7a0990d1c 100644
--- a/iphone/Maps/LocalizedStrings/nl.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/nl.lproj/LocalizableTypes.strings
@@ -52,7 +52,7 @@
"type.leisure.amusement_arcade" = "Speelhal";
"type.amenity.charging_station" = "Laadstation";
"type.amenity.charging_station.motorcar" = "Auto's";
-"type.amenity.charging_station.motorcycle" = "Motors";
+"type.amenity.charging_station.motorcycle" = "Motorfietsen";
"type.amenity.charging_station.bicycle" = "Fietsen";
"type.amenity.charging_station.small" = "Beperkte capaciteit";
"type.amenity.childcare" = "Crèche";
@@ -167,7 +167,7 @@
"type.recycling.green_waste" = "GFTE afval";
"type.recycling.cartons" = "Drankverpakkingen";
"type.amenity.restaurant" = "Restaurant";
-"type.amenity.sanitary_dump_station" = "Caravantoilet cassette-leegpunt";
+"type.amenity.sanitary_dump_station" = "Camperverzorgingsplaats";
"type.amenity.school" = "School";
/* Weather shelter (including sun shelters and natural rock shelters). */
"type.amenity.shelter" = "Schuilplaats";
@@ -191,7 +191,7 @@
"type.amenity.vending_machine.cigarettes" = "Sigarettenautomaat";
"type.amenity.vending_machine.coffee" = "Koffieautomaat";
"type.amenity.vending_machine.condoms" = "Condoomautomaat";
-"type.amenity.vending_machine.drinks" = "Drankautomaat";
+"type.amenity.vending_machine.drinks" = "Drankenautomaat";
"type.amenity.vending_machine.food" = "Etenswarenautomaat";
"type.amenity.vending_machine.newspapers" = "Krantenautomaat";
"type.amenity.vending_machine.parking_tickets" = "Parkeerautomaat";
@@ -205,7 +205,7 @@
"type.amenity.animal_shelter" = "Dierenasiel";
"type.amenity.waste_basket" = "Vuilnisbak";
"type.amenity.waste_disposal" = "Afvalcontainer";
-"type.amenity.waste_transfer_station" = "Overslagstation voor afval";
+"type.amenity.waste_transfer_station" = "Afvaloverslagstation";
"type.amenity.water_point" = "Waterpunt";
"type.amenity.water_point.drinking_water_no" = "Waterpunt";
"type.barrier" = "Barrière";
@@ -265,7 +265,7 @@
"type.craft.brewery" = "Brouwerij";
"type.craft.caterer" = "Cateraar";
"type.craft.carpenter" = "Timmerman";
-"type.craft.confectionery" = "Banketbakkerij";
+"type.craft.confectionery" = "Snoepmakerij";
"type.craft.electrician" = "Elektricien";
"type.craft.electronics_repair" = "Elektronica-reparatie";
"type.craft.gardener" = "Tuinarchitect";
@@ -307,7 +307,7 @@
"type.cuisine.coffee_shop" = "Koffie";
"type.cuisine.crepe" = "Pannenkoek";
"type.cuisine.croatian" = "Kroatisch";
-"type.cuisine.curry" = "Curry's";
+"type.cuisine.curry" = "Curry";
"type.cuisine.deli" = "Delicatessen";
"type.cuisine.diner" = "Diner";
"type.cuisine.donut" = "Donuts";
@@ -321,7 +321,7 @@
"type.cuisine.georgian" = "Georgisch";
"type.cuisine.german" = "Duits";
"type.cuisine.greek" = "Grieks";
-"type.cuisine.grill" = "Gegrilde gerechten";
+"type.cuisine.grill" = "Grill";
"type.cuisine.heuriger" = "Heuriger";
"type.cuisine.hotdog" = "Hotdogs";
"type.cuisine.hungarian" = "Hongaars";
@@ -423,8 +423,8 @@
"type.entrance.house" = "Huis entree";
"type.entrance.garage" = "Garage entree";
"type.entrance.service" = "Dienstingang";
-"type.entrance.entry" = "Ingang (enig)";
-"type.entrance.exit" = "Uitgang (enig)";
+"type.entrance.entry" = "Ingang (alleen ingang)";
+"type.entrance.exit" = "Uitgang (alleen uitgang)";
"type.entrance.emergency" = "Nooduitgang";
"type.fee.yes" = "€";
"type.fee.no" = "Gratis";
@@ -445,13 +445,29 @@
"type.highway.bridleway.permissive" = "Ruiterpad";
/* These translations are used for all type.highway.*.tunnel. */
"type.highway.bridleway.tunnel" = "Tunnel";
-"type.highway.busway" = "Speciale busweg";
+"type.highway.busway" = "Busbaan";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.busway.bridge" = "Brug";
/* These translations are used for all type.highway.*.tunnel. */
"type.highway.busway.tunnel" = "Tunnel";
"type.highway.bus_stop" = "Bushalte";
-"type.highway.construction" = "Baan in aanbouw";
+"type.highway.construction" = "Weg in aanbouw";
+"type.highway.construction.motorway" = "Weg in aanbouw";
+"type.highway.construction.motorway_link" = "Weg in aanbouw";
+"type.highway.construction.trunk" = "Weg in aanbouw";
+"type.highway.construction.trunk_link" = "Weg in aanbouw";
+"type.highway.construction.primary" = "Weg in aanbouw";
+"type.highway.construction.primary_link" = "Weg in aanbouw";
+"type.highway.construction.secondary" = "Weg in aanbouw";
+"type.highway.construction.secondary_link" = "Weg in aanbouw";
+"type.highway.construction.tertiary" = "Weg in aanbouw";
+"type.highway.construction.tertiary_link" = "Weg in aanbouw";
+"type.highway.construction.residential" = "Weg in aanbouw";
+"type.highway.construction.unclassified" = "Weg in aanbouw";
+"type.highway.construction.service" = "Weg in aanbouw";
+"type.highway.construction.living_street" = "Weg in aanbouw";
+"type.highway.construction.road" = "Weg in aanbouw";
+"type.highway.construction.track" = "Weg in aanbouw";
"type.highway.cycleway" = "Fietspad";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Fietsbrug";
@@ -468,7 +484,7 @@
/* These translations are used for all type.highway.*.tunnel. */
"type.highway.footway.tunnel" = "Voetgangerstunnel";
"type.highway.ford" = "Voorde";
-"type.highway.living_street" = "Straat";
+"type.highway.living_street" = "Woonerf";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.living_street.bridge" = "Brug";
/* These translations are used for all type.highway.*.tunnel. */
@@ -479,7 +495,7 @@
/* These translations are used for all type.highway.*.tunnel. */
"type.highway.motorway.tunnel" = "Snelwegtunnel";
"type.highway.motorway_junction" = "Afrit";
-"type.highway.motorway_link" = "Snelwegoprit";
+"type.highway.motorway_link" = "Snelwegoprit/afrit";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.motorway_link.bridge" = "Brug";
/* These translations are used for all type.highway.*.tunnel. */
@@ -488,7 +504,7 @@
/* Hiking trail tagged as sac_scale=demanding_mountain_hiking (3 of 6) or trail_visibility=bad. */
"type.highway.path.difficult" = "Moeilijk of slecht zichtbaar pad";
/* Hiking trail tagged as sac_scale=alpine_hiking (4+ of 6) or trail_visibility=horrible or more extreme. */
-"type.highway.path.expert" = "Zeer moeilijk of niet te onderscheiden spoor";
+"type.highway.path.expert" = "Zeer moeilijk of niet te onderscheiden pad";
"type.highway.path.bicycle" = "Fiets- & voetpad";
"type.highway.footway.bicycle" = "Fiets- & voetpad";
/* These translations are used for all type.highway.*.bridge. */
@@ -564,27 +580,27 @@
"type.highway.tertiary_link.bridge" = "Brug";
/* These translations are used for all type.highway.*.tunnel. */
"type.highway.tertiary_link.tunnel" = "Tunnel";
-"type.highway.track" = "Straat";
-"type.highway.track.area" = "Straat";
+"type.highway.track" = "Bos-/veldweg";
+"type.highway.track.area" = "Bos-/veldweg";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.track.bridge" = "Brug";
-"type.highway.track.grade1" = "Straat";
-"type.highway.track.no.access" = "Straat";
+"type.highway.track.grade1" = "Bos-/veldweg";
+"type.highway.track.no.access" = "Bos-/veldweg";
/* These translations are used for all type.highway.*.tunnel. */
"type.highway.track.tunnel" = "Tunnel";
"type.highway.traffic_signals" = "Verkeerslichten";
-"type.highway.trunk" = "Straat";
+"type.highway.trunk" = "Autoweg";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.trunk.bridge" = "Brug";
/* These translations are used for all type.highway.*.tunnel. */
"type.highway.trunk.tunnel" = "Tunnel";
-"type.highway.trunk_link" = "Straat";
+"type.highway.trunk_link" = "Autowegoprit/afrit";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.trunk_link.bridge" = "Brug";
/* These translations are used for all type.highway.*.tunnel. */
"type.highway.trunk_link.tunnel" = "Tunnel";
-"type.highway.unclassified" = "Straat";
-"type.highway.unclassified.area" = "Straat";
+"type.highway.unclassified" = "Weg";
+"type.highway.unclassified.area" = "Weg";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.unclassified.bridge" = "Brug";
/* These translations are used for all type.highway.*.tunnel. */
@@ -598,12 +614,12 @@
"type.area_highway.primary" = "Hoofdweg";
"type.area_highway.residential" = "Straat";
"type.area_highway.secondary" = "Secundaire weg";
-"type.area_highway.service" = "Straat";
+"type.area_highway.service" = "Serviceweg";
"type.area_highway.tertiary" = "Tertiaire weg";
-"type.area_highway.steps" = "Trappen";
-"type.area_highway.track" = "Straat";
-"type.area_highway.trunk" = "Straat";
-"type.area_highway.unclassified" = "Straat";
+"type.area_highway.steps" = "Trap";
+"type.area_highway.track" = "Bos-/veldweg";
+"type.area_highway.trunk" = "Autoweg";
+"type.area_highway.unclassified" = "Weg";
"type.historic" = "Historisch object";
"type.historic.aircraft" = "Historische vliegtuigen";
"type.historic.anchor" = "Historisch anker";
@@ -618,7 +634,7 @@
"type.historic.castle.fortress" = "Vesting";
"type.historic.castle.hillfort" = "Heuvelfort";
"type.historic.castle.kremlin" = "Kremlin";
-"type.historic.castle.manor" = "Manoir";
+"type.historic.castle.manor" = "Landhuis";
"type.historic.castle.palace" = "Paleis";
"type.historic.castle.shiro" = "Japans kasteel";
"type.historic.castle.stately" = "Landhuis";
@@ -638,7 +654,7 @@
"type.historic.mine" = "Historische mijn";
"type.historic.monument" = "Monument";
"type.historic.pillory" = "Schandpaal";
-"type.historic.ruins" = "Ruïne";
+"type.historic.ruins" = "Historische ruïne";
"type.historic.ship" = "Historisch schip";
"type.historic.tank" = "Historische tank";
"type.historic.tomb" = "Graftombe";
@@ -647,7 +663,7 @@
/* Usually a Christian historical cross placed along a road. */
"type.historic.wayside_cross" = "Wegkruis";
"type.historic.wayside_shrine" = "Kruisbeeld";
-"type.historic.wreck" = "Schipbreuk";
+"type.historic.wreck" = "Schipwrak";
"type.internet_access" = "Internet";
"type.internet_access.wlan" = "Draadloos internet";
"type.junction" = "Kruispunt";
@@ -664,41 +680,41 @@
"type.landuse.religious" = "Religieus Land";
"type.landuse.commercial" = "Commercieel gebied";
"type.landuse.construction" = "Bouwplaats";
-"type.landuse.education" = "Educatieve voorzieningen";
+"type.landuse.education" = "Onderwijsgebied";
"type.landuse.farmland" = "Landbouwgrond";
"type.landuse.farmyard" = "Boerenerf";
"type.landuse.field" = "Veld";
-"type.landuse.flowerbed" = "Bloemenbed";
+"type.landuse.flowerbed" = "Bloembed";
"type.landuse.forest" = "Bos";
"type.landuse.forest.coniferous" = "Naaldbos";
"type.landuse.forest.deciduous" = "Loofbos";
"type.landuse.forest.mixed" = "Gemengd bos";
"type.landuse.garages" = "Garages";
"type.landuse.grass" = "Gazon";
-"type.landuse.greenfield" = "Greenfield";
-"type.landuse.greenhouse_horticulture" = "Kassen";
+"type.landuse.greenfield" = "Bouwgrond";
+"type.landuse.greenhouse_horticulture" = "Kas";
"type.landuse.industrial" = "Industrieterrein";
"type.landuse.landfill" = "Stortplaats";
"type.landuse.meadow" = "Weiland";
"type.landuse.military" = "Militair terrein";
"type.landuse.orchard" = "Boomgaard";
"type.landuse.quarry" = "Steengroeve";
-"type.landuse.railway" = "Spoorwegfaciliteiten";
+"type.landuse.railway" = "Spoorwegterrein";
"type.landuse.recreation_ground" = "Recreatiezone";
"type.landuse.reservoir" = "Reservoir";
"type.landuse.residential" = "Woonwijk";
-"type.landuse.retail" = "Detailhandel";
-"type.landuse.salt_pond" = "Zoutpoel";
+"type.landuse.retail" = "Detailhandelsgebied";
+"type.landuse.salt_pond" = "Zoutpan";
"type.landuse.village_green" = "Dorpspark";
"type.landuse.vineyard" = "Wijngaard";
"type.landuse.plant_nursery" = "Plantenkwekerij";
-"type.leisure" = "Ontspanning";
-"type.leisure.bandstand" = "Muziekpodium";
+"type.leisure" = "Recreatie";
+"type.leisure.bandstand" = "Muziekkoepel";
"type.leisure.common" = "Openbare grond";
"type.leisure.dog_park" = "Hondenuitlaatplek";
"type.leisure.escape_game" = "Escaperoom";
"type.leisure.fitness_centre" = "Fitnesscentrum";
-"type.leisure.fitness_station" = "Fitness-Station";
+"type.leisure.fitness_station" = "Fitness-station";
"type.leisure.dance" = "Danszaal";
"type.leisure.garden" = "Tuin";
"type.leisure.garden.residential" = "Tuin";
@@ -706,15 +722,15 @@
"type.leisure.miniature_golf" = "Minigolf";
"type.leisure.hackerspace" = "Hackerspace";
"type.leisure.ice_rink" = "Schaatsbaan";
-"type.leisure.indoor_play" = "Speelhal";
+"type.leisure.indoor_play" = "Indoor speeltuin";
"type.leisure.marina" = "Jachthaven";
"type.leisure.nature_reserve" = "Natuurreservaat";
"type.leisure.outdoor_seating" = "Zitplaatsen buiten";
"type.leisure.firepit" = "Vuurplaats";
"type.leisure.park" = "Park";
-"type.leisure.park.no.access" = "Park";
+"type.leisure.park.no.access" = "Privépark";
"type.leisure.park.permissive" = "Park";
-"type.leisure.park.private" = "Park";
+"type.leisure.park.private" = "Privépark";
"type.leisure.picnic_table" = "Picknicktafel";
"type.leisure.pitch" = "Sportveld";
"type.leisure.playground" = "Speeltuin";
@@ -757,18 +773,18 @@
"type.leisure.fitness_centre.sport.yoga" = "Yogastudio";
"type.leisure.stadium" = "Stadion";
"type.leisure.swimming_pool" = "Zwembad";
-"type.leisure.swimming_pool.private" = "Privé zwembad";
+"type.leisure.swimming_pool.private" = "Privézwembad";
"type.leisure.track" = "Parcours";
"type.leisure.track.area" = "Parcours";
"type.leisure.water_park" = "Waterpark";
"type.leisure.beach_resort" = "Strandresort";
-"type.man_made" = "Kunstmatige constructies";
+"type.man_made" = "Kunstmatige constructie";
"type.man_made.breakwater" = "Golfbreker";
-"type.man_made.cairn" = "Steengroeve";
+"type.man_made.cairn" = "Steenmannetje";
"type.man_made.chimney" = "Fabrieksschoorsteen";
-"type.man_made.cutline" = "Bospad";
+"type.man_made.cutline" = "Cutline";
"type.man_made.survey_point" = "Meetpunt";
-"type.man_made.flagpole" = "Vlaggenpaal";
+"type.man_made.flagpole" = "Vlaggenmast";
"type.man_made.lighthouse" = "Vuurtoren";
"type.man_made.mast" = "Mast";
"type.man_made.pier" = "Pier";
@@ -797,7 +813,7 @@
"type.man_made.water_well.drinking_water_no" = "Waterput";
"type.man_made.windmill" = "Windmolen";
"type.man_made.works" = "Fabriek";
-"type.military" = "Militair";
+"type.military" = "Militair object";
"type.military.bunker" = "Bunker";
"type.mountain_pass" = "Bergpas";
"type.natural" = "Natuur";
@@ -812,9 +828,9 @@
"type.natural.beach.sand" = "Zandstrand";
"type.natural.beach.gravel" = "Kiezelstrand";
"type.natural.cape" = "Kaap";
-"type.natural.cave_entrance" = "Grot";
+"type.natural.cave_entrance" = "Grotingang";
"type.natural.cliff" = "Klif";
-"type.natural.earth_bank" = "Klif";
+"type.natural.earth_bank" = "Aardhelling";
"type.man_made.embankment" = "Baanlichaam";
"type.natural.coastline" = "Kustlijn";
"type.natural.desert" = "Woestijn";
@@ -825,8 +841,8 @@
"type.natural.heath" = "Heide";
"type.natural.hot_spring" = "Hete bron";
"type.natural.water.lake" = "Meer";
-"type.natural.water.lock" = "Slotkamer";
-"type.natural.water.pond" = "Plas";
+"type.natural.water.lock" = "Schutkolk";
+"type.natural.water.pond" = "Vijver";
"type.natural.water.reservoir" = "Reservoir";
"type.natural.water.basin" = "Waterbassin";
"type.natural.water.river" = "Rivier";
@@ -896,83 +912,83 @@
"type.place.sea" = "Zee";
"type.place.square" = "Plein";
"type.place.state" = "Deelstaat";
-"type.place.state.USA" = "Deelstaat";
+"type.place.state.USA" = "Staat";
/* Named part of a city or town, bigger than place=quarter (Wiki: https://wiki.openstreetmap.org/wiki/Tag:place%3Dsuburb) */
"type.place.suburb" = "Buitenwijk";
"type.place.town" = "Stad";
"type.place.village" = "Dorp";
-"type.power" = "Electriciteit";
+"type.power" = "Elektriciteit";
"type.power.generator" = "Generator";
"type.power.generator.solar" = "Zonne-generator";
-"type.power.generator.wind" = "Wind generator";
-"type.power.generator.gas" = "Gasturbine elektriciteitscentrale";
+"type.power.generator.wind" = "Windgenerator";
+"type.power.generator.gas" = "Gascentrale";
"type.power.generator.hydro" = "Waterkrachtcentrale";
"type.power.line" = "Hoogspanningsleiding";
"type.power.line.underground" = "Ondergrondse hoogspanningsleiding";
"type.power.minor_line" = "Laag-/middelspanningsleiding";
"type.power.plant" = "Energiecentrale";
-"type.power.plant.coal" = "Kolen elektriciteitscentrale";
-"type.power.plant.gas" = "Gasturbine elektriciteitscentrale";
+"type.power.plant.coal" = "Kolencentrale";
+"type.power.plant.gas" = "Gascentrale";
"type.power.plant.hydro" = "Waterkrachtcentrale";
"type.power.plant.solar" = "Zonne-energiecentrale";
"type.power.plant.wind" = "Windkrachtcentrale";
"type.power.substation" = "Transformatorstation";
/* A tower or pylon carrying high voltage electricity cables. */
-"type.power.tower" = "Elektriciteitsmast";
+"type.power.tower" = "Hoogspanningsmast";
/* A single pole supporting minor power lines. */
-"type.power.pole" = "Stroommast";
+"type.power.pole" = "Elektriciteitsmast";
/* A single pole supporting various public utilities, such as lighting or telephony. */
"type.man_made.utility_pole" = "Paal";
"type.public_transport" = "Openbaar vervoer";
-"type.public_transport.platform" = "Platform";
+"type.public_transport.platform" = "Perron";
"type.railway" = "Spoorweg";
"type.railway.abandoned" = "Voormalig treinspoor";
-"type.railway.construction" = "Treinspoor in aanbouw";
+"type.railway.construction" = "Spoor in aanbouw";
"type.railway.crossing" = "Spoorwegovergang";
"type.railway.disused" = "Ongebruikt spoor";
"type.railway.disused.bridge" = "Ongebruikte spoorbrug";
"type.railway.disused.tunnel" = "Ongebruikte spoortunnel";
-"type.railway.funicular" = "Tunnel";
-"type.railway.funicular.bridge" = "Kabelspoorbuur";
+"type.railway.funicular" = "Kabelspoor";
+"type.railway.funicular.bridge" = "Kabelspoorbrug";
"type.railway.funicular.tunnel" = "Kabelspoortunnel";
"type.railway.halt" = "Station";
"type.railway.level_crossing" = "Spoorwegovergang";
"type.railway.light_rail" = "Lightrail";
-"type.railway.light_rail.bridge" = "Lightrail-brug";
-"type.railway.light_rail.tunnel" = "Lightrail-tunnel";
+"type.railway.light_rail.bridge" = "Lightrailbrug";
+"type.railway.light_rail.tunnel" = "Lightrailtunnel";
"type.railway.monorail" = "Monorail";
-"type.railway.monorail.bridge" = "Monorail-brug";
-"type.railway.monorail.tunnel" = "Monorail-tunnel";
-"type.railway.miniature" = "Miniatuur spoorweg";
-"type.railway.miniature.bridge" = "Miniatuur spoorbrug";
-"type.railway.miniature.tunnel" = "Miniatuur spoortunnel";
-"type.railway.narrow_gauge" = "Smalspoorbaan";
+"type.railway.monorail.bridge" = "Monorailbrug";
+"type.railway.monorail.tunnel" = "Monorailtunnel";
+"type.railway.miniature" = "Miniatuurspoorweg";
+"type.railway.miniature.bridge" = "Miniatuurspoorbrug";
+"type.railway.miniature.tunnel" = "Miniatuurspoortunnel";
+"type.railway.narrow_gauge" = "Smalspoor";
"type.railway.narrow_gauge.bridge" = "Smalspoorbrug";
"type.railway.narrow_gauge.tunnel" = "Smalspoortunnel";
"type.railway.platform" = "Perron";
-"type.railway.preserved" = "Museumspoorbaan";
-"type.railway.preserved.bridge" = "Museumspoorbaanbrug";
-"type.railway.preserved.tunnel" = "Museumspoorbaantunnel";
-"type.railway.rail" = "Spoorlijn";
-"type.railway.rail.highspeed" = "Hogesnelheidstrein";
+"type.railway.preserved" = "Museumspoorweg";
+"type.railway.preserved.bridge" = "Museumspoorbrug";
+"type.railway.preserved.tunnel" = "Museumspoortunnel";
+"type.railway.rail" = "Spoor";
+"type.railway.rail.highspeed" = "Hogesnelheidsspoor";
"type.railway.rail.tourism" = "Toeristische spoorlijn";
"type.railway.rail.main" = "Spoor";
-"type.railway.turntable" = "Spoorweg-keerpunt";
+"type.railway.turntable" = "Spoordraaischijf";
/* Includes ordinary railway=rail w/o more specific usage= and service= tags. */
-"type.railway.rail.branch" = "Secundaire spoorwegen";
+"type.railway.rail.branch" = "Secundair spoor";
/* Non-passenger utility tracks: industrial, military, test. */
-"type.railway.rail.utility" = "Spoorwegen";
+"type.railway.rail.utility" = "Dienstspoor";
"type.railway.rail.spur" = "Zijspoor";
/* Short service tracks: siding, yard, crossover. */
"type.railway.rail.service" = "Hulpspoor";
-"type.railway.rail.bridge" = "Treinspoorbrug";
-"type.railway.rail.highspeed.bridge" = "Treinspoorbrug";
-"type.railway.rail.tourism.bridge" = "Treinspoorbrug";
-"type.railway.rail.main.bridge" = "Treinspoorbrug";
-"type.railway.rail.branch.bridge" = "Treinspoorbrug";
-"type.railway.rail.utility.bridge" = "Treinspoorbrug";
-"type.railway.rail.spur.bridge" = "Treinspoorbrug";
-"type.railway.rail.service.bridge" = "Treinspoorbrug";
+"type.railway.rail.bridge" = "Spoorbrug";
+"type.railway.rail.highspeed.bridge" = "Spoorbrug";
+"type.railway.rail.tourism.bridge" = "Spoorbrug";
+"type.railway.rail.main.bridge" = "Spoorbrug";
+"type.railway.rail.branch.bridge" = "Spoorbrug";
+"type.railway.rail.utility.bridge" = "Spoorbrug";
+"type.railway.rail.spur.bridge" = "Spoorbrug";
+"type.railway.rail.service.bridge" = "Spoorbrug";
"type.railway.rail.tunnel" = "Spoortunnel";
"type.railway.rail.highspeed.tunnel" = "Spoortunnel";
"type.railway.rail.tourism.tunnel" = "Spoortunnel";
@@ -982,12 +998,12 @@
"type.railway.rail.spur.tunnel" = "Spoortunnel";
"type.railway.rail.service.tunnel" = "Spoortunnel";
"type.railway.station" = "Station";
-"type.railway.station.funicular" = "Tunnel";
-"type.railway.station.light_rail" = "Station";
+"type.railway.station.funicular" = "Kabelspoorstation";
+"type.railway.station.light_rail" = "Lightrailstation";
"type.railway.station.light_rail.berlin" = "S-bahn station";
"type.railway.station.light_rail.london" = "Station";
"type.railway.station.light_rail.porto" = "Station";
-"type.railway.station.monorail" = "Station";
+"type.railway.station.monorail" = "Monorailstation";
"type.railway.station.subway" = "Metrostation";
"type.railway.station.subway.adana" = "Metrostation";
"type.railway.station.subway.algiers" = "Metrostation";
@@ -1103,124 +1119,124 @@
"type.railway.station.subway.wuhan" = "Metrostation";
"type.railway.station.subway.yerevan" = "Metrostation";
"type.railway.station.subway.yokohama" = "Metrostation";
-"type.railway.subway" = "Metro";
+"type.railway.subway" = "Metrolijn";
"type.railway.subway.bridge" = "Metrobrug";
"type.railway.subway.tunnel" = "Metrotunnel";
-"type.railway.subway_entrance" = "Metro ingang";
-"type.railway.subway_entrance.adana" = "Metro ingang";
-"type.railway.subway_entrance.algiers" = "Metro ingang";
-"type.railway.subway_entrance.almaty" = "Metro ingang";
-"type.railway.subway_entrance.amsterdam" = "Metro ingang";
-"type.railway.subway_entrance.ankara" = "Metro ingang";
-"type.railway.subway_entrance.athens" = "Metro ingang";
-"type.railway.subway_entrance.baku" = "Metro ingang";
-"type.railway.subway_entrance.bangkok" = "Metro ingang";
-"type.railway.subway_entrance.barcelona" = "Metro ingang";
-"type.railway.subway_entrance.beijing" = "Metro ingang";
-"type.railway.subway_entrance.bengalore" = "Metro ingang";
-"type.railway.subway_entrance.berlin" = "Metro ingang";
-"type.railway.subway_entrance.bilbao" = "Metro ingang";
-"type.railway.subway_entrance.brasilia" = "Metro ingang";
-"type.railway.subway_entrance.brescia" = "Metro ingang";
-"type.railway.subway_entrance.brussels" = "Metro ingang";
-"type.railway.subway_entrance.bucharest" = "Metro ingang";
-"type.railway.subway_entrance.budapest" = "Metro ingang";
-"type.railway.subway_entrance.buenos_aires" = "Metro ingang";
-"type.railway.subway_entrance.bursa" = "Metro ingang";
-"type.railway.subway_entrance.cairo" = "Metro ingang";
-"type.railway.subway_entrance.caracas" = "Metro ingang";
-"type.railway.subway_entrance.catania" = "Metro ingang";
-"type.railway.subway_entrance.changchun" = "Metro ingang";
-"type.railway.subway_entrance.chengdu" = "Metro ingang";
-"type.railway.subway_entrance.chicago" = "Metro ingang";
-"type.railway.subway_entrance.chongqing" = "Metro ingang";
-"type.railway.subway_entrance.dalian" = "Metro ingang";
-"type.railway.subway_entrance.delhi" = "Metro ingang";
-"type.railway.subway_entrance.dnepro" = "Metro ingang";
-"type.railway.subway_entrance.dubai" = "Metro ingang";
-"type.railway.subway_entrance.ekb" = "Metro ingang";
-"type.railway.subway_entrance.fukuoka" = "Metro ingang";
-"type.railway.subway_entrance.glasgow" = "Metro ingang";
-"type.railway.subway_entrance.guangzhou" = "Metro ingang";
-"type.railway.subway_entrance.hamburg" = "Metro ingang";
-"type.railway.subway_entrance.helsinki" = "Metro ingang";
-"type.railway.subway_entrance.hiroshima" = "Metro ingang";
-"type.railway.subway_entrance.hongkong" = "Metro ingang";
-"type.railway.subway_entrance.isfahan" = "Metro ingang";
-"type.railway.subway_entrance.istanbul" = "Metro ingang";
-"type.railway.subway_entrance.izmir" = "Metro ingang";
-"type.railway.subway_entrance.kazan" = "Metro ingang";
-"type.railway.subway_entrance.kharkiv" = "Metro ingang";
-"type.railway.subway_entrance.kiev" = "Metro ingang";
-"type.railway.subway_entrance.kobe" = "Metro ingang";
-"type.railway.subway_entrance.kolkata" = "Metro ingang";
-"type.railway.subway_entrance.kunming" = "Metro ingang";
-"type.railway.subway_entrance.kyoto" = "Metro ingang";
-"type.railway.subway_entrance.la" = "Metro ingang";
-"type.railway.subway_entrance.lausanne" = "Metro ingang";
-"type.railway.subway_entrance.lille" = "Metro ingang";
-"type.railway.subway_entrance.lima" = "Metro ingang";
-"type.railway.subway_entrance.lisboa" = "Metro ingang";
-"type.railway.subway_entrance.london" = "Metro ingang";
-"type.railway.subway_entrance.lyon" = "Metro ingang";
-"type.railway.subway_entrance.madrid" = "Metro ingang";
-"type.railway.subway_entrance.malaga" = "Metro ingang";
-"type.railway.subway_entrance.manila" = "Metro ingang";
-"type.railway.subway_entrance.maracaibo" = "Metro ingang";
-"type.railway.subway_entrance.mashhad" = "Metro ingang";
-"type.railway.subway_entrance.mecca" = "Metro ingang";
-"type.railway.subway_entrance.medellin" = "Metro ingang";
-"type.railway.subway_entrance.mexico" = "Metro ingang";
-"type.railway.subway_entrance.milan" = "Metro ingang";
-"type.railway.subway_entrance.minsk" = "Metro ingang";
-"type.railway.subway_entrance.montreal" = "Metro ingang";
-"type.railway.subway_entrance.moscow" = "Metro ingang";
-"type.railway.subway_entrance.munchen" = "Metro ingang";
-"type.railway.subway_entrance.nagoya" = "Metro ingang";
-"type.railway.subway_entrance.newyork" = "Metro ingang";
-"type.railway.subway_entrance.nnov" = "Metro ingang";
-"type.railway.subway_entrance.novosibirsk" = "Metro ingang";
-"type.railway.subway_entrance.osaka" = "Metro ingang";
-"type.railway.subway_entrance.oslo" = "Metro ingang";
-"type.railway.subway_entrance.palma" = "Metro ingang";
-"type.railway.subway_entrance.panama" = "Metro ingang";
-"type.railway.subway_entrance.paris" = "Metro ingang";
-"type.railway.subway_entrance.philadelphia" = "Metro ingang";
-"type.railway.subway_entrance.pyongyang" = "Metro ingang";
-"type.railway.subway_entrance.rennes" = "Metro ingang";
-"type.railway.subway_entrance.rio" = "Metro ingang";
-"type.railway.subway_entrance.roma" = "Metro ingang";
-"type.railway.subway_entrance.rotterdam" = "Metro ingang";
-"type.railway.subway_entrance.samara" = "Metro ingang";
-"type.railway.subway_entrance.santiago" = "Metro ingang";
-"type.railway.subway_entrance.santo_domingo" = "Metro ingang";
-"type.railway.subway_entrance.saopaulo" = "Metro ingang";
-"type.railway.subway_entrance.sapporo" = "Metro ingang";
-"type.railway.subway_entrance.sendai" = "Metro ingang";
-"type.railway.subway_entrance.sf" = "Metro ingang";
-"type.railway.subway_entrance.shanghai" = "Metro ingang";
-"type.railway.subway_entrance.shenzhen" = "Metro ingang";
-"type.railway.subway_entrance.shiraz" = "Metro ingang";
-"type.railway.subway_entrance.singapore" = "Metro ingang";
-"type.railway.subway_entrance.sofia" = "Metro ingang";
-"type.railway.subway_entrance.spb" = "Metro ingang";
-"type.railway.subway_entrance.stockholm" = "Metro ingang";
-"type.railway.subway_entrance.tabriz" = "Metro ingang";
-"type.railway.subway_entrance.taipei" = "Metro ingang";
-"type.railway.subway_entrance.taoyuan" = "Metro ingang";
-"type.railway.subway_entrance.tashkent" = "Metro ingang";
-"type.railway.subway_entrance.tbilisi" = "Metro ingang";
-"type.railway.subway_entrance.tehran" = "Metro ingang";
-"type.railway.subway_entrance.tianjin" = "Metro ingang";
-"type.railway.subway_entrance.tokyo" = "Metro ingang";
-"type.railway.subway_entrance.valencia" = "Metro ingang";
-"type.railway.subway_entrance.vienna" = "Metro ingang";
-"type.railway.subway_entrance.warszawa" = "Metro ingang";
-"type.railway.subway_entrance.washington" = "Metro ingang";
-"type.railway.subway_entrance.wuhan" = "Metro ingang";
-"type.railway.subway_entrance.yerevan" = "Metro ingang";
-"type.railway.subway_entrance.yokohama" = "Metro ingang";
-"type.railway.tram" = "Tram";
+"type.railway.subway_entrance" = "Metro-ingang";
+"type.railway.subway_entrance.adana" = "Metro-ingang";
+"type.railway.subway_entrance.algiers" = "Metro-ingang";
+"type.railway.subway_entrance.almaty" = "Metro-ingang";
+"type.railway.subway_entrance.amsterdam" = "Metro-ingang";
+"type.railway.subway_entrance.ankara" = "Metro-ingang";
+"type.railway.subway_entrance.athens" = "Metro-ingang";
+"type.railway.subway_entrance.baku" = "Metro-ingang";
+"type.railway.subway_entrance.bangkok" = "Metro-ingang";
+"type.railway.subway_entrance.barcelona" = "Metro-ingang";
+"type.railway.subway_entrance.beijing" = "Metro-ingang";
+"type.railway.subway_entrance.bengalore" = "Metro-ingang";
+"type.railway.subway_entrance.berlin" = "Metro-ingang";
+"type.railway.subway_entrance.bilbao" = "Metro-ingang";
+"type.railway.subway_entrance.brasilia" = "Metro-ingang";
+"type.railway.subway_entrance.brescia" = "Metro-ingang";
+"type.railway.subway_entrance.brussels" = "Metro-ingang";
+"type.railway.subway_entrance.bucharest" = "Metro-ingang";
+"type.railway.subway_entrance.budapest" = "Metro-ingang";
+"type.railway.subway_entrance.buenos_aires" = "Metro-ingang";
+"type.railway.subway_entrance.bursa" = "Metro-ingang";
+"type.railway.subway_entrance.cairo" = "Metro-ingang";
+"type.railway.subway_entrance.caracas" = "Metro-ingang";
+"type.railway.subway_entrance.catania" = "Metro-ingang";
+"type.railway.subway_entrance.changchun" = "Metro-ingang";
+"type.railway.subway_entrance.chengdu" = "Metro-ingang";
+"type.railway.subway_entrance.chicago" = "Metro-ingang";
+"type.railway.subway_entrance.chongqing" = "Metro-ingang";
+"type.railway.subway_entrance.dalian" = "Metro-ingang";
+"type.railway.subway_entrance.delhi" = "Metro-ingang";
+"type.railway.subway_entrance.dnepro" = "Metro-ingang";
+"type.railway.subway_entrance.dubai" = "Metro-ingang";
+"type.railway.subway_entrance.ekb" = "Metro-ingang";
+"type.railway.subway_entrance.fukuoka" = "Metro-ingang";
+"type.railway.subway_entrance.glasgow" = "Metro-ingang";
+"type.railway.subway_entrance.guangzhou" = "Metro-ingang";
+"type.railway.subway_entrance.hamburg" = "Metro-ingang";
+"type.railway.subway_entrance.helsinki" = "Metro-ingang";
+"type.railway.subway_entrance.hiroshima" = "Metro-ingang";
+"type.railway.subway_entrance.hongkong" = "Metro-ingang";
+"type.railway.subway_entrance.isfahan" = "Metro-ingang";
+"type.railway.subway_entrance.istanbul" = "Metro-ingang";
+"type.railway.subway_entrance.izmir" = "Metro-ingang";
+"type.railway.subway_entrance.kazan" = "Metro-ingang";
+"type.railway.subway_entrance.kharkiv" = "Metro-ingang";
+"type.railway.subway_entrance.kiev" = "Metro-ingang";
+"type.railway.subway_entrance.kobe" = "Metro-ingang";
+"type.railway.subway_entrance.kolkata" = "Metro-ingang";
+"type.railway.subway_entrance.kunming" = "Metro-ingang";
+"type.railway.subway_entrance.kyoto" = "Metro-ingang";
+"type.railway.subway_entrance.la" = "Metro-ingang";
+"type.railway.subway_entrance.lausanne" = "Metro-ingang";
+"type.railway.subway_entrance.lille" = "Metro-ingang";
+"type.railway.subway_entrance.lima" = "Metro-ingang";
+"type.railway.subway_entrance.lisboa" = "Metro-ingang";
+"type.railway.subway_entrance.london" = "Metro-ingang";
+"type.railway.subway_entrance.lyon" = "Metro-ingang";
+"type.railway.subway_entrance.madrid" = "Metro-ingang";
+"type.railway.subway_entrance.malaga" = "Metro-ingang";
+"type.railway.subway_entrance.manila" = "Metro-ingang";
+"type.railway.subway_entrance.maracaibo" = "Metro-ingang";
+"type.railway.subway_entrance.mashhad" = "Metro-ingang";
+"type.railway.subway_entrance.mecca" = "Metro-ingang";
+"type.railway.subway_entrance.medellin" = "Metro-ingang";
+"type.railway.subway_entrance.mexico" = "Metro-ingang";
+"type.railway.subway_entrance.milan" = "Metro-ingang";
+"type.railway.subway_entrance.minsk" = "Metro-ingang";
+"type.railway.subway_entrance.montreal" = "Metro-ingang";
+"type.railway.subway_entrance.moscow" = "Metro-ingang";
+"type.railway.subway_entrance.munchen" = "Metro-ingang";
+"type.railway.subway_entrance.nagoya" = "Metro-ingang";
+"type.railway.subway_entrance.newyork" = "Metro-ingang";
+"type.railway.subway_entrance.nnov" = "Metro-ingang";
+"type.railway.subway_entrance.novosibirsk" = "Metro-ingang";
+"type.railway.subway_entrance.osaka" = "Metro-ingang";
+"type.railway.subway_entrance.oslo" = "Metro-ingang";
+"type.railway.subway_entrance.palma" = "Metro-ingang";
+"type.railway.subway_entrance.panama" = "Metro-ingang";
+"type.railway.subway_entrance.paris" = "Metro-ingang";
+"type.railway.subway_entrance.philadelphia" = "Metro-ingang";
+"type.railway.subway_entrance.pyongyang" = "Metro-ingang";
+"type.railway.subway_entrance.rennes" = "Metro-ingang";
+"type.railway.subway_entrance.rio" = "Metro-ingang";
+"type.railway.subway_entrance.roma" = "Metro-ingang";
+"type.railway.subway_entrance.rotterdam" = "Metro-ingang";
+"type.railway.subway_entrance.samara" = "Metro-ingang";
+"type.railway.subway_entrance.santiago" = "Metro-ingang";
+"type.railway.subway_entrance.santo_domingo" = "Metro-ingang";
+"type.railway.subway_entrance.saopaulo" = "Metro-ingang";
+"type.railway.subway_entrance.sapporo" = "Metro-ingang";
+"type.railway.subway_entrance.sendai" = "Metro-ingang";
+"type.railway.subway_entrance.sf" = "Metro-ingang";
+"type.railway.subway_entrance.shanghai" = "Metro-ingang";
+"type.railway.subway_entrance.shenzhen" = "Metro-ingang";
+"type.railway.subway_entrance.shiraz" = "Metro-ingang";
+"type.railway.subway_entrance.singapore" = "Metro-ingang";
+"type.railway.subway_entrance.sofia" = "Metro-ingang";
+"type.railway.subway_entrance.spb" = "Metro-ingang";
+"type.railway.subway_entrance.stockholm" = "Metro-ingang";
+"type.railway.subway_entrance.tabriz" = "Metro-ingang";
+"type.railway.subway_entrance.taipei" = "Metro-ingang";
+"type.railway.subway_entrance.taoyuan" = "Metro-ingang";
+"type.railway.subway_entrance.tashkent" = "Metro-ingang";
+"type.railway.subway_entrance.tbilisi" = "Metro-ingang";
+"type.railway.subway_entrance.tehran" = "Metro-ingang";
+"type.railway.subway_entrance.tianjin" = "Metro-ingang";
+"type.railway.subway_entrance.tokyo" = "Metro-ingang";
+"type.railway.subway_entrance.valencia" = "Metro-ingang";
+"type.railway.subway_entrance.vienna" = "Metro-ingang";
+"type.railway.subway_entrance.warszawa" = "Metro-ingang";
+"type.railway.subway_entrance.washington" = "Metro-ingang";
+"type.railway.subway_entrance.wuhan" = "Metro-ingang";
+"type.railway.subway_entrance.yerevan" = "Metro-ingang";
+"type.railway.subway_entrance.yokohama" = "Metro-ingang";
+"type.railway.tram" = "Tramspoor";
"type.railway.tram.bridge" = "Trambrug";
"type.railway.tram.tunnel" = "Tramtunnel";
"type.railway.tram_stop" = "Tramhalte";
@@ -1232,27 +1248,27 @@
"type.shop.bathroom_furnishing" = "Badkamerinrichting";
"type.shop.beauty" = "Schoonheidssalon";
"type.shop.beauty.nails" = "Nagelsalon";
-"type.shop.beverages" = "Drank";
+"type.shop.beverages" = "Drankenwinkel";
"type.shop.bicycle" = "Fietsenwinkel";
-"type.shop.bookmaker" = "Boekbinder";
+"type.shop.bookmaker" = "Wedkantoor";
"type.shop.books" = "Boekwinkel";
"type.shop.butcher" = "Slager";
"type.shop.cannabis" = "Coffeeshop";
-"type.shop.car" = "Autohandelaar";
-"type.shop.car_parts" = "Auto-onderdelen";
-"type.shop.car_repair" = "Auto reparatie";
+"type.shop.car" = "Autodealer";
+"type.shop.car_parts" = "Auto-onderdelenwinkel";
+"type.shop.car_repair" = "Autogarage";
"type.shop.car_repair.tyres" = "Bandenreparatie";
-"type.shop.caravan" = "Caravan en camper verkoper";
+"type.shop.caravan" = "Caravan- en camperdealer";
"type.shop.carpet" = "Tapijtenwinkel";
"type.shop.chemist" = "Drogisterij";
"type.shop.chocolate" = "Chocolaterie";
"type.shop.clothes" = "Kledingwinkel";
-"type.shop.coffee" = "Koffieverkoop";
+"type.shop.coffee" = "Koffiewinkel";
"type.shop.computer" = "Computerwinkel";
"type.shop.confectionery" = "Snoepwinkel";
-"type.shop.convenience" = "Buurtwinkel";
+"type.shop.convenience" = "Gemakswinkel";
"type.shop.copyshop" = "Kopieerwinkel";
-"type.shop.cosmetics" = "Schoonheidsmiddelen";
+"type.shop.cosmetics" = "Cosmeticawinkel";
"type.shop.curtain" = "Gordijnenwinkel";
"type.shop.deli" = "Delicatessenwinkel";
"type.shop.department_store" = "Warenhuis";
@@ -1262,21 +1278,21 @@
"type.shop.erotic" = "Erotiekwinkel";
"type.shop.fabric" = "Stoffenwinkel";
"type.shop.farm" = "Boerderijwinkel";
-"type.shop.fashion_accessories" = "Modeaccessoires";
+"type.shop.fashion_accessories" = "Modeaccessoirewinkel";
"type.shop.florist" = "Bloemist";
-"type.shop.funeral_directors" = "Begrafenisondernemer";
+"type.shop.funeral_directors" = "Uitvaartondernemer";
"type.shop.furniture" = "Meubelwinkel";
"type.shop.garden_centre" = "Tuincentrum";
"type.shop.gas" = "Gaswinkel";
"type.shop.gift" = "Cadeauwinkel";
"type.shop.greengrocer" = "Groentenwinkel";
-"type.shop.grocery" = "Boodschappen";
+"type.shop.grocery" = "Kruidenierswinkel";
"type.shop.hairdresser" = "Kapper";
-"type.shop.hardware" = "IJzerwaren";
+"type.shop.hardware" = "IJzerwarenwinkel";
"type.shop.health_food" = "Natuurvoedingswinkel";
-"type.shop.hearing_aids" = "Winkel met hoortoestellen";
+"type.shop.hearing_aids" = "Hoortoestellenwinkel";
"type.shop.herbalist" = "Kruidenwinkel";
-"type.shop.hifi" = "HiFi-audio";
+"type.shop.hifi" = "Hifiwinkel";
"type.shop.houseware" = "Huishoudwinkel";
"type.shop.jewelry" = "Juwelier";
"type.shop.kiosk" = "Kiosk";
@@ -1285,18 +1301,18 @@
"type.shop.mall" = "Winkelcentrum";
"type.shop.beauty.day_spa" = "Schoonheidssalon";
"type.shop.massage" = "Massagesalon";
-"type.shop.mobile_phone" = "Mobiele telefoonwinkel";
+"type.shop.mobile_phone" = "Mobiele-telefoonwinkel";
"type.shop.money_lender" = "Geldschieter";
"type.shop.motorcycle" = "Motorzaak";
"type.shop.motorcycle_repair" = "Motorfietsreparatie";
"type.shop.music" = "Muziekwinkel";
"type.shop.musical_instrument" = "Muziekinstrumentenwinkel";
-"type.shop.newsagent" = "Kiosk";
+"type.shop.newsagent" = "Krantenkiosk";
"type.shop.optician" = "Opticien";
-"type.shop.outdoor" = "Outdooruitrusting";
+"type.shop.outdoor" = "Buitensportwinkel";
"type.shop.outpost" = "Afhaalpunt";
"type.shop.pasta" = "Pastawinkel";
-"type.shop.pastry" = "Banketbakker";
+"type.shop.pastry" = "Banketbakkerij";
"type.shop.pawnbroker" = "Pandjeshuis";
"type.shop.pet" = "Dierenwinkel";
"type.shop.pet_grooming" = "Huisdierverzorging";
@@ -1306,10 +1322,10 @@
"type.shop.seafood" = "Visboer";
"type.shop.second_hand" = "Tweedehandswinkel";
"type.shop.shoes" = "Schoenenwinkel";
-"type.shop.sports" = "Sportartikelen";
+"type.shop.sports" = "Sportwinkel";
"type.shop.stationery" = "Kantoorboekhandel";
"type.shop.supermarket" = "Supermarkt";
-"type.shop.tattoo" = "Tattoosalon";
+"type.shop.tattoo" = "Tatoeagestudio";
"type.shop.tea" = "Theewinkel";
"type.shop.telecommunication" = "Telecommunicatiewinkel";
"type.shop.ticket" = "Kaartjesverkoop";
@@ -1326,30 +1342,30 @@
"type.shop.water" = "Waterwinkel";
/* maybe change to Art Gallery for en-US when supported */
"type.shop.art" = "Kunstwinkel";
-"type.shop.baby_goods" = "Babyspullenwinkel";
+"type.shop.baby_goods" = "Babywinkel";
"type.shop.bag" = "Tassenwinkel";
"type.shop.bed" = "Beddenwinkel";
"type.shop.boutique" = "Boetiek";
"type.shop.charity" = "Kringloopwinkel";
"type.shop.cheese" = "Kaaswinkel";
-"type.shop.craft" = "Kunst en ambacht";
-"type.shop.dairy" = "Zuivelproducten";
+"type.shop.craft" = "Kunst- en hobbywinkel";
+"type.shop.dairy" = "Zuivelwinkel";
"type.shop.electrical" = "Elektronische benodigdhedenwinkel";
"type.shop.fishing" = "Viswinkel";
"type.shop.interior_decoration" = "Interieurdecoratiewinkel";
-"type.shop.lottery" = "Loten";
-"type.shop.medical_supply" = "Medische benodigdheden";
+"type.shop.lottery" = "Loterijwinkel";
+"type.shop.medical_supply" = "Medische benodigdhedenwinkel";
"type.shop.nutrition_supplements" = "Voedingssupplementenwinkel";
"type.shop.paint" = "Verfwinkel";
"type.shop.perfumery" = "Parfumerie";
-"type.shop.sewing" = "Naaibenodigdheden";
+"type.shop.sewing" = "Naai- en fourniturenwinkel";
"type.shop.storage_rental" = "Opslagverhuur";
-"type.shop.tobacco" = "Tabakszaak";
-"type.shop.trade" = "Handelsbenodigdheden";
-"type.shop.watches" = "Horlogezaak";
+"type.shop.tobacco" = "Tabakswinkel";
+"type.shop.trade" = "Technische groothandel";
+"type.shop.watches" = "Horlogewinkel";
"type.shop.wholesale" = "Groothandel";
"type.shop.lighting" = "Verlichtingswinkel";
-"type.disusedbusiness" = "Leegstand bedrijfspand";
+"type.disusedbusiness" = "Leegstaand bedrijfspand";
"type.sport" = "Sport";
"type.sport.multi" = "Diverse sporten";
"type.sport.9pin" = "Bowlen";
@@ -1362,23 +1378,23 @@
"type.sport.baseball" = "Honkbal";
"type.sport.basketball" = "Basketbal";
"type.sport.beachvolleyball" = "Beachvolleybal";
-"type.sport.bowls" = "Bowling";
+"type.sport.bowls" = "Bowls";
"type.sport.chess" = "Schaken";
"type.sport.climbing" = "Klimmen";
"type.sport.cricket" = "Cricket";
"type.sport.curling" = "Curling";
"type.sport.diving" = "Klifduiken";
-"type.sport.equestrian" = "Paardesport";
+"type.sport.equestrian" = "Paardensport";
"type.sport.field_hockey" = "Veldhockey";
-"type.sport.futsal" = "Futsal";
+"type.sport.futsal" = "Zaalvoetbal";
"type.sport.golf" = "Golf";
"type.sport.gymnastics" = "Gymnastiek";
"type.sport.handball" = "Handbal";
"type.sport.ice_hockey" = "IJshockey";
"type.sport.padel" = "Padel";
-"type.sport.pelota" = "Baskische peloton";
+"type.sport.pelota" = "Pelota";
"type.sport.scuba_diving" = "Duiken";
-"type.sport.shooting" = "schieten";
+"type.sport.shooting" = "Schietsport";
"type.sport.skateboard" = "Skateboarden";
"type.sport.skiing" = "Skiën";
"type.sport.soccer" = "Voetbal";
@@ -1391,30 +1407,30 @@
"type.tourism.aquarium" = "Aquarium";
/* Typically serviced, staff is present and food is available (compared to wilderness_hut). */
"type.tourism.alpine_hut" = "Berghut";
-"type.tourism.apartment" = "Appartement";
+"type.tourism.apartment" = "Vakantieappartement";
"type.tourism.artwork" = "Kunstwerk";
-"type.tourism.artwork.architecture" = "Kunstwerk";
+"type.tourism.artwork.architecture" = "Architectonisch kunstwerk";
"type.tourism.artwork.painting" = "Schilderij";
-"type.tourism.artwork.sculpture" = "Kunstwerk";
+"type.tourism.artwork.sculpture" = "Sculptuur";
"type.tourism.artwork.statue" = "Standbeeld";
-"type.tourism.attraction" = "Toeristische attractie";
+"type.tourism.attraction" = "Bezienswaardigheid";
"type.attraction" = "Attractie";
-"type.attraction.amusement_ride" = "Prettocht";
+"type.attraction.amusement_ride" = "Attractie";
"type.attraction.animal" = "Dierenverblijf";
"type.attraction.bumper_car" = "Botsauto's";
"type.attraction.big_wheel" = "Reuzenrad";
-"type.attraction.carousel" = "Reuzenrad";
+"type.attraction.carousel" = "Draaimolen";
"type.attraction.historic" = "Historische attractie";
"type.attraction.maze" = "Doolhof";
"type.attraction.roller_coaster" = "Achtbaan";
"type.attraction.water_slide" = "Waterglijbaan";
"type.tourism.attraction.specified" = "Attractie";
-"type.tourism.camp_site" = "Kampeerterrein";
-"type.tourism.caravan_site" = "Caravan site";
+"type.tourism.camp_site" = "Camping";
+"type.tourism.caravan_site" = "Caravan- en camperplaats";
/* A rentable countryside vacation house. */
"type.tourism.chalet" = "Vakantiehuis";
-"type.tourism.gallery" = "Galerij";
-"type.tourism.guest_house" = "Gasthuis";
+"type.tourism.gallery" = "Galerie";
+"type.tourism.guest_house" = "Pension";
"type.tourism.hostel" = "Jeugdherberg";
"type.tourism.hotel" = "Hotel";
"type.tourism.information" = "Toeristische informatie";
@@ -1422,22 +1438,22 @@
"type.tourism.information.guidepost" = "Wegwijzer";
"type.tourism.information.map" = "Toeristische kaart";
"type.tourism.information.tactile_map" = "Tactiele kaart";
-"type.tourism.information.office" = "Toeristische informatie";
+"type.tourism.information.office" = "Toeristeninformatiepunt";
"type.tourism.information.visitor_centre" = "Bezoekerscentrum";
-"type.amenity.ranger_station" = "Boswachterkantoor";
+"type.amenity.ranger_station" = "Boswachterskantoor";
"type.tourism.motel" = "Motel";
"type.tourism.museum" = "Museum";
-"type.tourism.picnic_site" = "Picnicplaats";
+"type.tourism.picnic_site" = "Picknickplaats";
"type.leisure.resort" = "Complex";
"type.tourism.theme_park" = "Attractiepark";
"type.tourism.viewpoint" = "Uitkijkpunt";
/* Typically more basic, not staffed and free (compared to alpine_hut). */
-"type.tourism.wilderness_hut" = "Wildernishut";
+"type.tourism.wilderness_hut" = "Zelfverzorgingshut";
"type.tourism.zoo" = "Dierentuin";
"type.tourism.zoo.petting" = "Kinderboerderij";
-"type.traffic_calming" = "Snelheidsmatigende maatregel";
+"type.traffic_calming" = "Snelheidsremmende maatregel";
"type.traffic_calming.bump" = "Verkeersdrempel";
-"type.traffic_calming.hump" = "Verkeersheuvel";
+"type.traffic_calming.hump" = "Verkeersdrempel";
"type.waterway" = "Waterweg";
"type.waterway.canal" = "Kanaal";
"type.waterway.canal.tunnel" = "Kanaal";
@@ -1461,37 +1477,37 @@
"type.waterway.waterfall" = "Waterval";
"type.waterway.weir" = "Stuw";
"type.wheelchair" = "Rolstoel";
-"type.wheelchair.limited" = "Gedeeltelijk uitgerust voor gehandicapten";
-"type.wheelchair.no" = "Niet uitgerust voor gehandicapten";
-"type.wheelchair.yes" = "Uitgerust voor gehandicapten";
+"type.wheelchair.limited" = "Gedeeltelijk rolstoeltoegankelijk";
+"type.wheelchair.no" = "Niet rolstoeltoegankelijk";
+"type.wheelchair.yes" = "Volledig rolstoeltoegankelijk";
"type.aerialway.j.bar" = "Sleeplift (J)";
-"type.aerialway.magic_carpet" = "Loopband";
-"type.aerialway.platter" = "Sleeplift";
+"type.aerialway.magic_carpet" = "Lopende band (skilift)";
+"type.aerialway.platter" = "Pannenkoeklift";
"type.aerialway.rope_tow" = "Touwlift";
"type.aerialway.t.bar" = "Sleeplift (T-beugel)";
-"type.piste_type.downhill" = "Ski-afdaling";
-"type.piste_type.downhill.area" = "Ski-afdaling";
-"type.piste_type.downhill.advanced" = "Moeilijke afdaling";
-"type.piste_type.downhill.advanced.area" = "Moeilijke afdaling";
-"type.piste_type.downhill.easy" = "Makkelijke afdaling";
-"type.piste_type.downhill.easy.area" = "Makkelijke afdaling";
-"type.piste_type.downhill.expert" = "Afdaling voor experts";
-"type.piste_type.downhill.expert.area" = "Afdaling voor experts";
-"type.piste_type.downhill.freeride" = "Ski-afdaling";
-"type.piste_type.downhill.intermediate" = "Gemiddelde afdaling";
-"type.piste_type.downhill.intermediate.area" = "Gemiddelde afdaling";
-"type.piste_type.downhill.novice" = "Beginnersafdaling";
-"type.piste_type.downhill.novice.area" = "Beginnersafdaling";
+"type.piste_type.downhill" = "Skipiste";
+"type.piste_type.downhill.area" = "Skipiste";
+"type.piste_type.downhill.advanced" = "Moeilijke skipiste";
+"type.piste_type.downhill.advanced.area" = "Moeilijke skipiste";
+"type.piste_type.downhill.easy" = "Makkelijke skipiste";
+"type.piste_type.downhill.easy.area" = "Makkelijke skipiste";
+"type.piste_type.downhill.expert" = "Zeer moeilijke skipiste";
+"type.piste_type.downhill.expert.area" = "Zeer moeilijke skipiste";
+"type.piste_type.downhill.freeride" = "Freeride ski-afdaling";
+"type.piste_type.downhill.intermediate" = "Gemiddelde skipiste";
+"type.piste_type.downhill.intermediate.area" = "Gemiddelde skipiste";
+"type.piste_type.downhill.novice" = "Beginnersskipiste";
+"type.piste_type.downhill.novice.area" = "Beginnersskipiste";
"type.piste_type.nordic" = "Langlaufroute";
"type.piste_type.sled" = "Sleebaan";
"type.piste_type.sled.area" = "Sleebaan";
"type.piste_type.snow_park" = "Sneeuwpark";
"type.piste_type.hike" = "Sneeuwwandelpad";
"type.piste_type.connection" = "Pisteverbinding";
-"type.piste_type.skitour" = "Skitourspoor";
+"type.piste_type.skitour" = "Toerskiroute";
"type.amenity.events_venue" = "Evenementenlocatie";
"type.shop.auction" = "Veiling";
-"type.shop.collector" = "Verzamelobjecten";
+"type.shop.collector" = "Verzamelaarswinkel";
"type.self_service.yes" = "Zelfbediening beschikbaar";
"type.self_service.only" = "Alleen zelfbediening";
"type.self_service.partially" = "Gedeeltelijke zelfbediening";
@@ -1500,8 +1516,8 @@
"type.amenity.social_facility" = "Sociale voorziening";
"type.social_facility.soup_kitchen" = "Gaarkeuken";
"type.social_facility.food_bank" = "Voedselbank";
-"type.amenity.food_sharing" = "Voedsel delen";
-"type.amenity.give_box" = "Giftenkist";
+"type.amenity.food_sharing" = "Voedseldeelpunt";
+"type.amenity.give_box" = "Weggeefkast";
/* https://wiki.openstreetmap.org/wiki/Tag:emergency=emergency_ward_entrance */
"type.emergency.emergency_ward_entrance" = "Ingang noodafdeling";
/* https://wiki.openstreetmap.org/wiki/Tag:amenity=dojo */
@@ -1511,7 +1527,7 @@
"type.xmas.tree" = "Kerstboom";
"type.leisure.sports_centre.sport.four_square" = "Sportcentrum";
"type.sport.four_square" = "Four Square";
-"type.sport.boules" = "jeu de boules";
+"type.sport.boules" = "Jeu de boules";
"type.leisure.sports_centre.sport.boules" = "Sportcentrum";
"type.sport.pickleball" = "Pickleball";
"type.leisure.sports_centre.sport.pickleball" = "Sportcentrum";
diff --git a/iphone/Maps/LocalizedStrings/pl.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/pl.lproj/LocalizableTypes.strings
index d1ebcf6e2..47f40d336 100644
--- a/iphone/Maps/LocalizedStrings/pl.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/pl.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tunel";
"type.highway.bus_stop" = "Przystanek autobusowy";
"type.highway.construction" = "Droga w trakcie budowy";
+"type.highway.construction.motorway" = "Droga w trakcie budowy";
+"type.highway.construction.motorway_link" = "Droga w trakcie budowy";
+"type.highway.construction.trunk" = "Droga w trakcie budowy";
+"type.highway.construction.trunk_link" = "Droga w trakcie budowy";
+"type.highway.construction.primary" = "Droga w trakcie budowy";
+"type.highway.construction.primary_link" = "Droga w trakcie budowy";
+"type.highway.construction.secondary" = "Droga w trakcie budowy";
+"type.highway.construction.secondary_link" = "Droga w trakcie budowy";
+"type.highway.construction.tertiary" = "Droga w trakcie budowy";
+"type.highway.construction.tertiary_link" = "Droga w trakcie budowy";
+"type.highway.construction.residential" = "Droga w trakcie budowy";
+"type.highway.construction.unclassified" = "Droga w trakcie budowy";
+"type.highway.construction.service" = "Droga w trakcie budowy";
+"type.highway.construction.living_street" = "Droga w trakcie budowy";
+"type.highway.construction.road" = "Droga w trakcie budowy";
+"type.highway.construction.track" = "Droga w trakcie budowy";
"type.highway.cycleway" = "Droga rowerowa";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Most drogowy dla rowerów";
diff --git a/iphone/Maps/LocalizedStrings/pt-BR.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/pt-BR.lproj/LocalizableTypes.strings
index c72164138..f014c7469 100644
--- a/iphone/Maps/LocalizedStrings/pt-BR.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/pt-BR.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Túnel";
"type.highway.bus_stop" = "Ponto de ônibus";
"type.highway.construction" = "Via em construção";
+"type.highway.construction.motorway" = "Via em construção";
+"type.highway.construction.motorway_link" = "Via em construção";
+"type.highway.construction.trunk" = "Via em construção";
+"type.highway.construction.trunk_link" = "Via em construção";
+"type.highway.construction.primary" = "Via em construção";
+"type.highway.construction.primary_link" = "Via em construção";
+"type.highway.construction.secondary" = "Via em construção";
+"type.highway.construction.secondary_link" = "Via em construção";
+"type.highway.construction.tertiary" = "Via em construção";
+"type.highway.construction.tertiary_link" = "Via em construção";
+"type.highway.construction.residential" = "Via em construção";
+"type.highway.construction.unclassified" = "Via em construção";
+"type.highway.construction.service" = "Via em construção";
+"type.highway.construction.living_street" = "Via em construção";
+"type.highway.construction.road" = "Via em construção";
+"type.highway.construction.track" = "Via em construção";
"type.highway.cycleway" = "Ciclovia";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Ponte para ciclistas";
diff --git a/iphone/Maps/LocalizedStrings/pt.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/pt.lproj/LocalizableTypes.strings
index 66f873814..779b15f49 100644
--- a/iphone/Maps/LocalizedStrings/pt.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/pt.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Túnel";
"type.highway.bus_stop" = "Paragem de autocarros";
"type.highway.construction" = "Estrada em construção";
+"type.highway.construction.motorway" = "Estrada em construção";
+"type.highway.construction.motorway_link" = "Estrada em construção";
+"type.highway.construction.trunk" = "Estrada em construção";
+"type.highway.construction.trunk_link" = "Estrada em construção";
+"type.highway.construction.primary" = "Estrada em construção";
+"type.highway.construction.primary_link" = "Estrada em construção";
+"type.highway.construction.secondary" = "Estrada em construção";
+"type.highway.construction.secondary_link" = "Estrada em construção";
+"type.highway.construction.tertiary" = "Estrada em construção";
+"type.highway.construction.tertiary_link" = "Estrada em construção";
+"type.highway.construction.residential" = "Estrada em construção";
+"type.highway.construction.unclassified" = "Estrada em construção";
+"type.highway.construction.service" = "Estrada em construção";
+"type.highway.construction.living_street" = "Estrada em construção";
+"type.highway.construction.road" = "Estrada em construção";
+"type.highway.construction.track" = "Estrada em construção";
"type.highway.cycleway" = "Ciclovia";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Ponte";
diff --git a/iphone/Maps/LocalizedStrings/ro.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/ro.lproj/LocalizableTypes.strings
index c6c7fd80c..761979379 100644
--- a/iphone/Maps/LocalizedStrings/ro.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/ro.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tunel";
"type.highway.bus_stop" = "Stație de autobuz";
"type.highway.construction" = "Drum în construcție";
+"type.highway.construction.motorway" = "Drum în construcție";
+"type.highway.construction.motorway_link" = "Drum în construcție";
+"type.highway.construction.trunk" = "Drum în construcție";
+"type.highway.construction.trunk_link" = "Drum în construcție";
+"type.highway.construction.primary" = "Drum în construcție";
+"type.highway.construction.primary_link" = "Drum în construcție";
+"type.highway.construction.secondary" = "Drum în construcție";
+"type.highway.construction.secondary_link" = "Drum în construcție";
+"type.highway.construction.tertiary" = "Drum în construcție";
+"type.highway.construction.tertiary_link" = "Drum în construcție";
+"type.highway.construction.residential" = "Drum în construcție";
+"type.highway.construction.unclassified" = "Drum în construcție";
+"type.highway.construction.service" = "Drum în construcție";
+"type.highway.construction.living_street" = "Drum în construcție";
+"type.highway.construction.road" = "Drum în construcție";
+"type.highway.construction.track" = "Drum în construcție";
"type.highway.cycleway" = "Pistă de Biciclete";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Pod";
diff --git a/iphone/Maps/LocalizedStrings/ru.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/ru.lproj/LocalizableTypes.strings
index 3ad63b87d..e1eeb2d67 100644
--- a/iphone/Maps/LocalizedStrings/ru.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/ru.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Тоннель";
"type.highway.bus_stop" = "Остановка";
"type.highway.construction" = "Строящаяся дорога";
+"type.highway.construction.motorway" = "Строящаяся дорога";
+"type.highway.construction.motorway_link" = "Строящаяся дорога";
+"type.highway.construction.trunk" = "Строящаяся дорога";
+"type.highway.construction.trunk_link" = "Строящаяся дорога";
+"type.highway.construction.primary" = "Строящаяся дорога";
+"type.highway.construction.primary_link" = "Строящаяся дорога";
+"type.highway.construction.secondary" = "Строящаяся дорога";
+"type.highway.construction.secondary_link" = "Строящаяся дорога";
+"type.highway.construction.tertiary" = "Строящаяся дорога";
+"type.highway.construction.tertiary_link" = "Строящаяся дорога";
+"type.highway.construction.residential" = "Строящаяся дорога";
+"type.highway.construction.unclassified" = "Строящаяся дорога";
+"type.highway.construction.service" = "Строящаяся дорога";
+"type.highway.construction.living_street" = "Строящаяся дорога";
+"type.highway.construction.road" = "Строящаяся дорога";
+"type.highway.construction.track" = "Строящаяся дорога";
"type.highway.cycleway" = "Велодорожка";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Мост";
diff --git a/iphone/Maps/LocalizedStrings/sk.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/sk.lproj/LocalizableTypes.strings
index 3a58f841c..c9527912b 100644
--- a/iphone/Maps/LocalizedStrings/sk.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/sk.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tunel";
"type.highway.bus_stop" = "Autobusová zastávka";
"type.highway.construction" = "Cesta vo výstavbe";
+"type.highway.construction.motorway" = "Cesta vo výstavbe";
+"type.highway.construction.motorway_link" = "Cesta vo výstavbe";
+"type.highway.construction.trunk" = "Cesta vo výstavbe";
+"type.highway.construction.trunk_link" = "Cesta vo výstavbe";
+"type.highway.construction.primary" = "Cesta vo výstavbe";
+"type.highway.construction.primary_link" = "Cesta vo výstavbe";
+"type.highway.construction.secondary" = "Cesta vo výstavbe";
+"type.highway.construction.secondary_link" = "Cesta vo výstavbe";
+"type.highway.construction.tertiary" = "Cesta vo výstavbe";
+"type.highway.construction.tertiary_link" = "Cesta vo výstavbe";
+"type.highway.construction.residential" = "Cesta vo výstavbe";
+"type.highway.construction.unclassified" = "Cesta vo výstavbe";
+"type.highway.construction.service" = "Cesta vo výstavbe";
+"type.highway.construction.living_street" = "Cesta vo výstavbe";
+"type.highway.construction.road" = "Cesta vo výstavbe";
+"type.highway.construction.track" = "Cesta vo výstavbe";
"type.highway.cycleway" = "Cyklocesta";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Most";
diff --git a/iphone/Maps/LocalizedStrings/sl.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/sl.lproj/LocalizableTypes.strings
index b8b71c21c..d103a4b4d 100644
--- a/iphone/Maps/LocalizedStrings/sl.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/sl.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Predor";
"type.highway.bus_stop" = "Avtobusno postajališče";
"type.highway.construction" = "Cesta v gradnji";
+"type.highway.construction.motorway" = "Cesta v gradnji";
+"type.highway.construction.motorway_link" = "Cesta v gradnji";
+"type.highway.construction.trunk" = "Cesta v gradnji";
+"type.highway.construction.trunk_link" = "Cesta v gradnji";
+"type.highway.construction.primary" = "Cesta v gradnji";
+"type.highway.construction.primary_link" = "Cesta v gradnji";
+"type.highway.construction.secondary" = "Cesta v gradnji";
+"type.highway.construction.secondary_link" = "Cesta v gradnji";
+"type.highway.construction.tertiary" = "Cesta v gradnji";
+"type.highway.construction.tertiary_link" = "Cesta v gradnji";
+"type.highway.construction.residential" = "Cesta v gradnji";
+"type.highway.construction.unclassified" = "Cesta v gradnji";
+"type.highway.construction.service" = "Cesta v gradnji";
+"type.highway.construction.living_street" = "Cesta v gradnji";
+"type.highway.construction.road" = "Cesta v gradnji";
+"type.highway.construction.track" = "Cesta v gradnji";
"type.highway.cycleway" = "Kolesarska steza";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Most";
diff --git a/iphone/Maps/LocalizedStrings/sr.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/sr.lproj/LocalizableTypes.strings
index 04faa5d22..0e4b62f0b 100644
--- a/iphone/Maps/LocalizedStrings/sr.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/sr.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Тунел";
"type.highway.bus_stop" = "Аутобуско стајалиште";
"type.highway.construction" = "Пут у изградњи";
+"type.highway.construction.motorway" = "Пут у изградњи";
+"type.highway.construction.motorway_link" = "Пут у изградњи";
+"type.highway.construction.trunk" = "Пут у изградњи";
+"type.highway.construction.trunk_link" = "Пут у изградњи";
+"type.highway.construction.primary" = "Пут у изградњи";
+"type.highway.construction.primary_link" = "Пут у изградњи";
+"type.highway.construction.secondary" = "Пут у изградњи";
+"type.highway.construction.secondary_link" = "Пут у изградњи";
+"type.highway.construction.tertiary" = "Пут у изградњи";
+"type.highway.construction.tertiary_link" = "Пут у изградњи";
+"type.highway.construction.residential" = "Пут у изградњи";
+"type.highway.construction.unclassified" = "Пут у изградњи";
+"type.highway.construction.service" = "Пут у изградњи";
+"type.highway.construction.living_street" = "Пут у изградњи";
+"type.highway.construction.road" = "Пут у изградњи";
+"type.highway.construction.track" = "Пут у изградњи";
"type.highway.cycleway" = "Бициклистичка стаза";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Мост";
diff --git a/iphone/Maps/LocalizedStrings/sv.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/sv.lproj/LocalizableTypes.strings
index fb7710792..9940f4cad 100644
--- a/iphone/Maps/LocalizedStrings/sv.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/sv.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tunnel";
"type.highway.bus_stop" = "Busshållplats";
"type.highway.construction" = "Väg under uppförande";
+"type.highway.construction.motorway" = "Väg under uppförande";
+"type.highway.construction.motorway_link" = "Väg under uppförande";
+"type.highway.construction.trunk" = "Väg under uppförande";
+"type.highway.construction.trunk_link" = "Väg under uppförande";
+"type.highway.construction.primary" = "Väg under uppförande";
+"type.highway.construction.primary_link" = "Väg under uppförande";
+"type.highway.construction.secondary" = "Väg under uppförande";
+"type.highway.construction.secondary_link" = "Väg under uppförande";
+"type.highway.construction.tertiary" = "Väg under uppförande";
+"type.highway.construction.tertiary_link" = "Väg under uppförande";
+"type.highway.construction.residential" = "Väg under uppförande";
+"type.highway.construction.unclassified" = "Väg under uppförande";
+"type.highway.construction.service" = "Väg under uppförande";
+"type.highway.construction.living_street" = "Väg under uppförande";
+"type.highway.construction.road" = "Väg under uppförande";
+"type.highway.construction.track" = "Väg under uppförande";
"type.highway.cycleway" = "Cykelbana";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Cykelbro";
diff --git a/iphone/Maps/LocalizedStrings/sw.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/sw.lproj/LocalizableTypes.strings
index 2e55cf16e..370cab5b4 100644
--- a/iphone/Maps/LocalizedStrings/sw.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/sw.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Mtaro";
"type.highway.bus_stop" = "Bus Stop";
"type.highway.construction" = "Barabara inatengenezwa";
+"type.highway.construction.motorway" = "Barabara inatengenezwa";
+"type.highway.construction.motorway_link" = "Barabara inatengenezwa";
+"type.highway.construction.trunk" = "Barabara inatengenezwa";
+"type.highway.construction.trunk_link" = "Barabara inatengenezwa";
+"type.highway.construction.primary" = "Barabara inatengenezwa";
+"type.highway.construction.primary_link" = "Barabara inatengenezwa";
+"type.highway.construction.secondary" = "Barabara inatengenezwa";
+"type.highway.construction.secondary_link" = "Barabara inatengenezwa";
+"type.highway.construction.tertiary" = "Barabara inatengenezwa";
+"type.highway.construction.tertiary_link" = "Barabara inatengenezwa";
+"type.highway.construction.residential" = "Barabara inatengenezwa";
+"type.highway.construction.unclassified" = "Barabara inatengenezwa";
+"type.highway.construction.service" = "Barabara inatengenezwa";
+"type.highway.construction.living_street" = "Barabara inatengenezwa";
+"type.highway.construction.road" = "Barabara inatengenezwa";
+"type.highway.construction.track" = "Barabara inatengenezwa";
"type.highway.cycleway" = "Cycle Path";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Daraja";
diff --git a/iphone/Maps/LocalizedStrings/ta.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/ta.lproj/LocalizableTypes.strings
index afabc5b62..b79562586 100644
--- a/iphone/Maps/LocalizedStrings/ta.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/ta.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "சுரங்கப்பாதை";
"type.highway.bus_stop" = "பேருந்து நிறுத்தம்";
"type.highway.construction" = "சாலை கட்டுமானத்தில் உள்ளது";
+"type.highway.construction.motorway" = "சாலை கட்டுமானத்தில் உள்ளது";
+"type.highway.construction.motorway_link" = "சாலை கட்டுமானத்தில் உள்ளது";
+"type.highway.construction.trunk" = "சாலை கட்டுமானத்தில் உள்ளது";
+"type.highway.construction.trunk_link" = "சாலை கட்டுமானத்தில் உள்ளது";
+"type.highway.construction.primary" = "சாலை கட்டுமானத்தில் உள்ளது";
+"type.highway.construction.primary_link" = "சாலை கட்டுமானத்தில் உள்ளது";
+"type.highway.construction.secondary" = "சாலை கட்டுமானத்தில் உள்ளது";
+"type.highway.construction.secondary_link" = "சாலை கட்டுமானத்தில் உள்ளது";
+"type.highway.construction.tertiary" = "சாலை கட்டுமானத்தில் உள்ளது";
+"type.highway.construction.tertiary_link" = "சாலை கட்டுமானத்தில் உள்ளது";
+"type.highway.construction.residential" = "சாலை கட்டுமானத்தில் உள்ளது";
+"type.highway.construction.unclassified" = "சாலை கட்டுமானத்தில் உள்ளது";
+"type.highway.construction.service" = "சாலை கட்டுமானத்தில் உள்ளது";
+"type.highway.construction.living_street" = "சாலை கட்டுமானத்தில் உள்ளது";
+"type.highway.construction.road" = "சாலை கட்டுமானத்தில் உள்ளது";
+"type.highway.construction.track" = "சாலை கட்டுமானத்தில் உள்ளது";
"type.highway.cycleway" = "சைக்கிள் பாதை";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "பாலம்";
diff --git a/iphone/Maps/LocalizedStrings/th.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/th.lproj/LocalizableTypes.strings
index 539dee496..44a861866 100644
--- a/iphone/Maps/LocalizedStrings/th.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/th.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "อุโมงค์";
"type.highway.bus_stop" = "ป้ายรถเมล์";
"type.highway.construction" = "ทางกำลังอยู่ในการก่อสร้าง";
+"type.highway.construction.motorway" = "ทางกำลังอยู่ในการก่อสร้าง";
+"type.highway.construction.motorway_link" = "ทางกำลังอยู่ในการก่อสร้าง";
+"type.highway.construction.trunk" = "ทางกำลังอยู่ในการก่อสร้าง";
+"type.highway.construction.trunk_link" = "ทางกำลังอยู่ในการก่อสร้าง";
+"type.highway.construction.primary" = "ทางกำลังอยู่ในการก่อสร้าง";
+"type.highway.construction.primary_link" = "ทางกำลังอยู่ในการก่อสร้าง";
+"type.highway.construction.secondary" = "ทางกำลังอยู่ในการก่อสร้าง";
+"type.highway.construction.secondary_link" = "ทางกำลังอยู่ในการก่อสร้าง";
+"type.highway.construction.tertiary" = "ทางกำลังอยู่ในการก่อสร้าง";
+"type.highway.construction.tertiary_link" = "ทางกำลังอยู่ในการก่อสร้าง";
+"type.highway.construction.residential" = "ทางกำลังอยู่ในการก่อสร้าง";
+"type.highway.construction.unclassified" = "ทางกำลังอยู่ในการก่อสร้าง";
+"type.highway.construction.service" = "ทางกำลังอยู่ในการก่อสร้าง";
+"type.highway.construction.living_street" = "ทางกำลังอยู่ในการก่อสร้าง";
+"type.highway.construction.road" = "ทางกำลังอยู่ในการก่อสร้าง";
+"type.highway.construction.track" = "ทางกำลังอยู่ในการก่อสร้าง";
"type.highway.cycleway" = "Cycle Path";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "สะพาน";
diff --git a/iphone/Maps/LocalizedStrings/tr.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/tr.lproj/LocalizableTypes.strings
index 64c184d8c..aa4118850 100644
--- a/iphone/Maps/LocalizedStrings/tr.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/tr.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Tünel";
"type.highway.bus_stop" = "Otobüs Durağı";
"type.highway.construction" = "Yapım Aşamasında Yol";
+"type.highway.construction.motorway" = "Yapım Aşamasında Yol";
+"type.highway.construction.motorway_link" = "Yapım Aşamasında Yol";
+"type.highway.construction.trunk" = "Yapım Aşamasında Yol";
+"type.highway.construction.trunk_link" = "Yapım Aşamasında Yol";
+"type.highway.construction.primary" = "Yapım Aşamasında Yol";
+"type.highway.construction.primary_link" = "Yapım Aşamasında Yol";
+"type.highway.construction.secondary" = "Yapım Aşamasında Yol";
+"type.highway.construction.secondary_link" = "Yapım Aşamasında Yol";
+"type.highway.construction.tertiary" = "Yapım Aşamasında Yol";
+"type.highway.construction.tertiary_link" = "Yapım Aşamasında Yol";
+"type.highway.construction.residential" = "Yapım Aşamasında Yol";
+"type.highway.construction.unclassified" = "Yapım Aşamasında Yol";
+"type.highway.construction.service" = "Yapım Aşamasında Yol";
+"type.highway.construction.living_street" = "Yapım Aşamasında Yol";
+"type.highway.construction.road" = "Yapım Aşamasında Yol";
+"type.highway.construction.track" = "Yapım Aşamasında Yol";
"type.highway.cycleway" = "Bisiklet Yolu";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Köprü";
diff --git a/iphone/Maps/LocalizedStrings/uk.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/uk.lproj/LocalizableTypes.strings
index f77c8e226..cf619f2d2 100644
--- a/iphone/Maps/LocalizedStrings/uk.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/uk.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Тунель";
"type.highway.bus_stop" = "Зупинка";
"type.highway.construction" = "Дорога, що будується";
+"type.highway.construction.motorway" = "Дорога, що будується";
+"type.highway.construction.motorway_link" = "Дорога, що будується";
+"type.highway.construction.trunk" = "Дорога, що будується";
+"type.highway.construction.trunk_link" = "Дорога, що будується";
+"type.highway.construction.primary" = "Дорога, що будується";
+"type.highway.construction.primary_link" = "Дорога, що будується";
+"type.highway.construction.secondary" = "Дорога, що будується";
+"type.highway.construction.secondary_link" = "Дорога, що будується";
+"type.highway.construction.tertiary" = "Дорога, що будується";
+"type.highway.construction.tertiary_link" = "Дорога, що будується";
+"type.highway.construction.residential" = "Дорога, що будується";
+"type.highway.construction.unclassified" = "Дорога, що будується";
+"type.highway.construction.service" = "Дорога, що будується";
+"type.highway.construction.living_street" = "Дорога, що будується";
+"type.highway.construction.road" = "Дорога, що будується";
+"type.highway.construction.track" = "Дорога, що будується";
"type.highway.cycleway" = "Велодоріжка";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Міст";
diff --git a/iphone/Maps/LocalizedStrings/vi.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/vi.lproj/LocalizableTypes.strings
index d0812b31a..e893f6a66 100644
--- a/iphone/Maps/LocalizedStrings/vi.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/vi.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "Đường hầm";
"type.highway.bus_stop" = "Bến xe buýt";
"type.highway.construction" = "Đường đang thi công";
+"type.highway.construction.motorway" = "Đường đang thi công";
+"type.highway.construction.motorway_link" = "Đường đang thi công";
+"type.highway.construction.trunk" = "Đường đang thi công";
+"type.highway.construction.trunk_link" = "Đường đang thi công";
+"type.highway.construction.primary" = "Đường đang thi công";
+"type.highway.construction.primary_link" = "Đường đang thi công";
+"type.highway.construction.secondary" = "Đường đang thi công";
+"type.highway.construction.secondary_link" = "Đường đang thi công";
+"type.highway.construction.tertiary" = "Đường đang thi công";
+"type.highway.construction.tertiary_link" = "Đường đang thi công";
+"type.highway.construction.residential" = "Đường đang thi công";
+"type.highway.construction.unclassified" = "Đường đang thi công";
+"type.highway.construction.service" = "Đường đang thi công";
+"type.highway.construction.living_street" = "Đường đang thi công";
+"type.highway.construction.road" = "Đường đang thi công";
+"type.highway.construction.track" = "Đường đang thi công";
"type.highway.cycleway" = "Cycle Path";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "Cầu";
diff --git a/iphone/Maps/LocalizedStrings/zh-Hans.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/zh-Hans.lproj/LocalizableTypes.strings
index 04034ad3b..6c048a662 100644
--- a/iphone/Maps/LocalizedStrings/zh-Hans.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/zh-Hans.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "隧道";
"type.highway.bus_stop" = "公交站";
"type.highway.construction" = "在建道路";
+"type.highway.construction.motorway" = "在建道路";
+"type.highway.construction.motorway_link" = "在建道路";
+"type.highway.construction.trunk" = "在建道路";
+"type.highway.construction.trunk_link" = "在建道路";
+"type.highway.construction.primary" = "在建道路";
+"type.highway.construction.primary_link" = "在建道路";
+"type.highway.construction.secondary" = "在建道路";
+"type.highway.construction.secondary_link" = "在建道路";
+"type.highway.construction.tertiary" = "在建道路";
+"type.highway.construction.tertiary_link" = "在建道路";
+"type.highway.construction.residential" = "在建道路";
+"type.highway.construction.unclassified" = "在建道路";
+"type.highway.construction.service" = "在建道路";
+"type.highway.construction.living_street" = "在建道路";
+"type.highway.construction.road" = "在建道路";
+"type.highway.construction.track" = "在建道路";
"type.highway.cycleway" = "自行车道";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "桥";
diff --git a/iphone/Maps/LocalizedStrings/zh-Hant.lproj/LocalizableTypes.strings b/iphone/Maps/LocalizedStrings/zh-Hant.lproj/LocalizableTypes.strings
index 17f755638..a43aec4ba 100644
--- a/iphone/Maps/LocalizedStrings/zh-Hant.lproj/LocalizableTypes.strings
+++ b/iphone/Maps/LocalizedStrings/zh-Hant.lproj/LocalizableTypes.strings
@@ -452,6 +452,22 @@
"type.highway.busway.tunnel" = "隧道";
"type.highway.bus_stop" = "公車站";
"type.highway.construction" = "施工中道路";
+"type.highway.construction.motorway" = "施工中道路";
+"type.highway.construction.motorway_link" = "施工中道路";
+"type.highway.construction.trunk" = "施工中道路";
+"type.highway.construction.trunk_link" = "施工中道路";
+"type.highway.construction.primary" = "施工中道路";
+"type.highway.construction.primary_link" = "施工中道路";
+"type.highway.construction.secondary" = "施工中道路";
+"type.highway.construction.secondary_link" = "施工中道路";
+"type.highway.construction.tertiary" = "施工中道路";
+"type.highway.construction.tertiary_link" = "施工中道路";
+"type.highway.construction.residential" = "施工中道路";
+"type.highway.construction.unclassified" = "施工中道路";
+"type.highway.construction.service" = "施工中道路";
+"type.highway.construction.living_street" = "施工中道路";
+"type.highway.construction.road" = "施工中道路";
+"type.highway.construction.track" = "施工中道路";
"type.highway.cycleway" = "單車道";
/* These translations are used for all type.highway.*.bridge. */
"type.highway.cycleway.bridge" = "橋樑";
diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj
index ebd4c6072..b0c2bd429 100644
--- a/iphone/Maps/Maps.xcodeproj/project.pbxproj
+++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj
@@ -22,6 +22,7 @@
272F1F3D2E0EE0C800FA52EF /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 272F1F3C2E0EE0C400FA52EF /* ProfileView.swift */; };
272F1F462E0EEF9400FA52EF /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 272F1F452E0EEF8B00FA52EF /* SafariView.swift */; };
2744A9912F23D86D00E7D02C /* AlternativeMapLanguageHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2744A98E2F23D86B00E7D02C /* AlternativeMapLanguageHandling.swift */; };
+ 2747205A2E439FBA00C516DF /* libtraffxml.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 274720592E439FBA00C516DF /* libtraffxml.a */; };
2752B6CA2E31197500887CC4 /* MapLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2752B6C92E31197000887CC4 /* MapLanguage.swift */; };
2752B6CE2E3121D900887CC4 /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2752B6CD2E3121D800887CC4 /* Language.swift */; };
2765D1D02E13F9C20005CA2B /* BridgeControllers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2765D1CD2E13F9BC0005CA2B /* BridgeControllers.swift */; };
@@ -776,6 +777,7 @@
272F1F3C2E0EE0C400FA52EF /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = ""; };
272F1F452E0EEF8B00FA52EF /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = ""; };
2744A98E2F23D86B00E7D02C /* AlternativeMapLanguageHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlternativeMapLanguageHandling.swift; sourceTree = ""; };
+ 274720592E439FBA00C516DF /* libtraffxml.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libtraffxml.a; sourceTree = BUILT_PRODUCTS_DIR; };
2752B6C92E31197000887CC4 /* MapLanguage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLanguage.swift; sourceTree = ""; };
2752B6CD2E3121D800887CC4 /* Language.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Language.swift; sourceTree = ""; };
2765D1CD2E13F9BC0005CA2B /* BridgeControllers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BridgeControllers.swift; sourceTree = ""; };
@@ -1802,6 +1804,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 2747205A2E439FBA00C516DF /* libtraffxml.a in Frameworks */,
FAF9DDA32A86DC54000D7037 /* libharfbuzz.a in Frameworks */,
FA456C3C26BDC6AD00B83C20 /* Chart.framework in Frameworks */,
FA853BF326BC5DE50026D455 /* libshaders.a in Frameworks */,
@@ -2041,6 +2044,7 @@
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
isa = PBXGroup;
children = (
+ 274720592E439FBA00C516DF /* libtraffxml.a */,
FAF9DDA22A86DC54000D7037 /* libharfbuzz.a */,
FA456C3B26BDC6AD00B83C20 /* Chart.framework */,
FA853BF226BC5DE50026D455 /* libshaders.a */,
diff --git a/iphone/Maps/Model/Settings.swift b/iphone/Maps/Model/Settings.swift
index b5207571a..c6b4a52d0 100644
--- a/iphone/Maps/Model/Settings.swift
+++ b/iphone/Maps/Model/Settings.swift
@@ -448,6 +448,28 @@ import Combine
}
+ /// If live traffic data should be used
+ @objc static var hasLiveTraffic: Bool {
+ get {
+ return SettingsBridge.liveTrafficEnabled()
+ }
+ set {
+ SettingsBridge.setLiveTrafficEnabled(newValue)
+ }
+ }
+
+
+ /// The url of the live traffic data server
+ @objc static var liveTrafficServerUrl: URL? {
+ get {
+ return SettingsBridge.liveTrafficUrl()
+ }
+ set {
+ SettingsBridge.setLiveTrafficUrl(newValue)
+ }
+ }
+
+
// MARK: Methods
diff --git a/iphone/Maps/UI/BottomMenu/Menu/Cells/BottomMenuLayersCell.swift b/iphone/Maps/UI/BottomMenu/Menu/Cells/BottomMenuLayersCell.swift
index 704445eda..460b228a6 100644
--- a/iphone/Maps/UI/BottomMenu/Menu/Cells/BottomMenuLayersCell.swift
+++ b/iphone/Maps/UI/BottomMenu/Menu/Cells/BottomMenuLayersCell.swift
@@ -18,6 +18,11 @@ class BottomMenuLayersCell: UITableViewCell {
updateOutdoorButton()
}
}
+ @IBOutlet private var trafficButton: BottomMenuLayerButton! {
+ didSet {
+ updateTrafficButton()
+ }
+ }
var onClose: (()->())?
@@ -32,6 +37,7 @@ class BottomMenuLayersCell: UITableViewCell {
outdoorButton.setupWith(image: UIImage(resource: .btnMenuOutdoors), text: L("button_layer_outdoor"))
isoLinesButton.setupWith(image: UIImage(resource: .btnMenuIsomaps), text: L("button_layer_isolines"))
subwayButton.setupWith(image: UIImage(resource: .btnMenuSubway), text: L("button_layer_subway"))
+ trafficButton.setupWith(image: UIImage(resource: .btnMenuTraffic), text: L("button_layer_traffic"))
}
deinit {
@@ -56,6 +62,11 @@ class BottomMenuLayersCell: UITableViewCell {
let enabled = MapOverlayManager.outdoorEnabled()
outdoorButton.setStyleAndApply(styleFor(enabled))
}
+
+ private func updateTrafficButton() {
+ let enabled = MapOverlayManager.trafficEnabled()
+ trafficButton.setStyleAndApply(styleFor(enabled))
+ }
@IBAction func onCloseButtonPressed(_ sender: Any) {
onClose?()
@@ -75,6 +86,11 @@ class BottomMenuLayersCell: UITableViewCell {
let enable = !MapOverlayManager.outdoorEnabled()
MapOverlayManager.setOutdoorEnabled(enable)
}
+
+ @IBAction func onTrafficButton(_ sender: Any) {
+ let enable = !MapOverlayManager.trafficEnabled()
+ MapOverlayManager.setTrafficEnabled(enable)
+ }
}
extension BottomMenuLayersCell: MapOverlayManagerObserver {
@@ -89,6 +105,10 @@ extension BottomMenuLayersCell: MapOverlayManagerObserver {
func onOutdoorStateUpdated() {
updateOutdoorButton()
}
+
+ func onTrafficStateUpdated() {
+ updateTrafficButton()
+ }
}
private extension BottomMenuLayersCell {
diff --git a/iphone/Maps/UI/BottomMenu/Menu/Cells/BottomMenuLayersCell.xib b/iphone/Maps/UI/BottomMenu/Menu/Cells/BottomMenuLayersCell.xib
index 2261721f3..cc71a35e5 100644
--- a/iphone/Maps/UI/BottomMenu/Menu/Cells/BottomMenuLayersCell.xib
+++ b/iphone/Maps/UI/BottomMenu/Menu/Cells/BottomMenuLayersCell.xib
@@ -1,9 +1,9 @@
-
+
-
+
@@ -81,26 +81,35 @@
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
@@ -144,6 +153,7 @@
+
diff --git a/iphone/Maps/UI/Settings/SettingsNavigationView.swift b/iphone/Maps/UI/Settings/SettingsNavigationView.swift
index 8debbd132..8e7d96291 100644
--- a/iphone/Maps/UI/Settings/SettingsNavigationView.swift
+++ b/iphone/Maps/UI/Settings/SettingsNavigationView.swift
@@ -60,6 +60,14 @@ struct SettingsNavigationView: View {
@State var forceRefreshDate: Date = Date.now
+ /// If live traffic data should be used
+ @State var hasLiveTraffic: Bool = false
+
+
+ /// The url of the live traffic data server
+ @State var liveTrafficServerUrlString: String = ""
+
+
/// The actual view
var body: some View {
List {
@@ -222,6 +230,24 @@ struct SettingsNavigationView: View {
Text("driving_options_title")
}
}
+
+ Section {
+ Toggle(isOn: $hasLiveTraffic) {
+ VStack(alignment: .leading) {
+ Text("traffic_http_enabled")
+
+ Text("traffic_http_enabled_description")
+ .font(.footnote)
+ .foregroundStyle(.secondary)
+ }
+ }
+ .tint(.accent)
+
+ TextField("traffic_http_url", text: $liveTrafficServerUrlString, prompt: Text("traffic_http_url_not_set"))
+ .tint(.accent)
+ } header: {
+ Text("traffic_http")
+ }
}
.accentColor(.accent)
.navigationViewStyle(StackNavigationViewStyle())
@@ -239,6 +265,8 @@ struct SettingsNavigationView: View {
shouldAvoidFerriesWhileRouting = Settings.shouldAvoidFerriesWhileRouting
shouldAvoidMotorwaysWhileRouting = Settings.shouldAvoidMotorwaysWhileRouting
shouldAvoidStepsWhileRouting = Settings.shouldAvoidStepsWhileRouting
+ hasLiveTraffic = Settings.hasLiveTraffic
+ liveTrafficServerUrlString = Settings.liveTrafficServerUrl?.absoluteString ?? ""
}
.onChange(of: scenePhase) { _ in
forceRefreshDate = Date.now
@@ -286,6 +314,15 @@ struct SettingsNavigationView: View {
}
.onChange(of: shouldAvoidStepsWhileRouting) { changedShouldAvoidStepsWhileRouting in
Settings.shouldAvoidStepsWhileRouting = changedShouldAvoidStepsWhileRouting
+ .onChange(of: hasLiveTraffic) { changedHasLiveTraffic in
+ Settings.hasLiveTraffic = changedHasLiveTraffic
+ }
+ .onChange(of: liveTrafficServerUrlString) { changedLiveTrafficServerUrlString in
+ if !changedLiveTrafficServerUrlString.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty, let changedLiveTrafficServerUrl = URL(string: changedLiveTrafficServerUrlString.trimmingCharacters(in: .whitespacesAndNewlines)) {
+ Settings.liveTrafficServerUrl = changedLiveTrafficServerUrl
+ } else {
+ Settings.liveTrafficServerUrl = nil
+ }
}
}
}
diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt
index 00a05fa9d..fd648a805 100644
--- a/libs/CMakeLists.txt
+++ b/libs/CMakeLists.txt
@@ -19,4 +19,5 @@ add_subdirectory(shaders)
add_subdirectory(storage)
add_subdirectory(tracking)
add_subdirectory(traffic)
+add_subdirectory(traffxml)
add_subdirectory(transit)
diff --git a/libs/drape_frontend/drape_api.hpp b/libs/drape_frontend/drape_api.hpp
index 2d7497012..d9cf95bfa 100644
--- a/libs/drape_frontend/drape_api.hpp
+++ b/libs/drape_frontend/drape_api.hpp
@@ -15,6 +15,9 @@
namespace df
{
+/**
+ * @brief Geometry and style for a line rendered with Drape.
+ */
struct DrapeApiLineData
{
DrapeApiLineData() = default;
@@ -22,6 +25,14 @@ struct DrapeApiLineData
DrapeApiLineData(std::vector const & points, dp::Color const & color) : m_points(points), m_color(color)
{}
+ /**
+ * @brief Enables showing the points of the line.
+ *
+ * Calling this method will set the internal showPoints attribute to true (causing the points of
+ * the line to be shown as dots) and the markPoints attribite to the value supplied.
+ *
+ * @param markPoints Whether to mark the points.
+ */
DrapeApiLineData & ShowPoints(bool markPoints)
{
m_showPoints = true;
@@ -29,6 +40,9 @@ struct DrapeApiLineData
return *this;
}
+ /**
+ * @brief Sets the width for the line in pixels.
+ */
DrapeApiLineData & Width(float width)
{
m_width = width;
@@ -59,8 +73,25 @@ class DrapeApi
void SetDrapeEngine(ref_ptr engine);
+ /**
+ * @brief Adds a new line.
+ *
+ * @param id A unique identifier for the line. If a line with the same identifier exists, it will
+ * be replaced by the new line.
+ * @param data The data (geometry and style) for the new line.
+ */
void AddLine(std::string const & id, DrapeApiLineData const & data);
+
+ /**
+ * @brief Removes a line.
+ *
+ * @param id The id of the line to remove.
+ */
void RemoveLine(std::string const & id);
+
+ /**
+ * @brief Removes all lines added with AddLine.
+ */
void Clear();
void Invalidate();
diff --git a/libs/drape_frontend/rule_drawer.cpp b/libs/drape_frontend/rule_drawer.cpp
index 03ab5b6ed..fa0174c00 100644
--- a/libs/drape_frontend/rule_drawer.cpp
+++ b/libs/drape_frontend/rule_drawer.cpp
@@ -38,9 +38,9 @@ namespace
{
// The first zoom level in kAverageSegmentsCount.
int constexpr kFirstZoomInAverageSegments = 10;
-std::array constexpr kAverageSegmentsCount = {
- // 10 11 12 13 14 15 16 17 18 19
- 10000, 5000, 10000, 5000, 2500, 5000, 2000, 1000, 500, 500};
+std::array constexpr kAverageSegmentsCount = {
+ // 10 11 12 13 14 15 16 17 18 19 20
+ 10000, 5000, 10000, 5000, 2500, 5000, 2000, 1000, 500, 500, 500};
double constexpr kMetersPerLevel = 3.0;
diff --git a/libs/geometry/parametrized_segment.hpp b/libs/geometry/parametrized_segment.hpp
index e71af2e28..8d5a79669 100644
--- a/libs/geometry/parametrized_segment.hpp
+++ b/libs/geometry/parametrized_segment.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include "geometry/mercator.hpp"
#include "geometry/point2d.hpp"
#include "base/math.hpp"
@@ -9,17 +10,20 @@
namespace m2
{
-// This class holds a parametrization of the
-// line segment between two points p0 and p1.
-// The parametrization is of the form
-// p(t) = p0 + t * dir.
-// Other conditions:
-// dir is the normalized (p1 - p0) vector.
-// length(dir) = 1.
-// p(0) = p0.
-// p(T) = p1 with T = length(p1 - p0).
-//
-// The points with t in [0, T] are the points of the segment.
+/**
+ * @brief This class holds a parametrization of the line segment between two points `p0` and `p1`.
+ *
+ * The parametrization is of the form
+ * `p(t) = p0 + t * dir`.
+ *
+ * Other conditions:
+ * * `dir` is the normalized `(p1 - p0)` vector.
+ * * `length(dir) = 1`.
+ * * `p(0) = p0`.
+ * * `p(T) = p1` with `T = length(p1 - p0)`.
+ *
+ * The points with `t` in `[0, T]` are the points of the segment.
+ */
template
class ParametrizedSegment
{
@@ -36,7 +40,9 @@ class ParametrizedSegment
m_d = m_d / m_length;
}
- // Returns the squared (euclidean) distance from the segment to |p|.
+ /**
+ * @brief Returns the squared (euclidean) distance from the segment to `p`.
+ */
double SquaredDistanceToPoint(Point const & p) const
{
m2::PointD const diff(p - m_p0);
@@ -52,9 +58,22 @@ class ParametrizedSegment
return math::Pow2(CrossProduct(diff, m_d));
}
- // Returns the point of the segment that is closest to |p|.
- m2::PointD ClosestPointTo(Point const & p) const
+ /**
+ * @brief Returns the point of the segment that is closest to `p`.
+ *
+ * @param p The checkpoint
+ * @param snapToEnds If true, the result is the endpoint of the segment which is closest to `p`
+ */
+ m2::PointD ClosestPointTo(Point const & p, bool snapToEnds = false) const
{
+ if (snapToEnds)
+ {
+ if (mercator::DistanceOnEarth(p, m_p0) < mercator::DistanceOnEarth(p, m_p1))
+ return m_p0;
+ else
+ return m_p1;
+ }
+
m2::PointD const diff(p - m_p0);
double const t = DotProduct(m_d, diff);
diff --git a/libs/indexer/classificator.hpp b/libs/indexer/classificator.hpp
index 152b87e2d..4a0420d6a 100644
--- a/libs/indexer/classificator.hpp
+++ b/libs/indexer/classificator.hpp
@@ -26,12 +26,37 @@ void PushValue(uint32_t & type, uint8_t value);
/// @pre level < GetLevel(type).
uint8_t GetValue(uint32_t type, uint8_t level);
void PopValue(uint32_t & type);
+
+/**
+ * @brief Truncates a type to the number of levels specified.
+ *
+ * For example, truncating `natural-beach-sand` to 2 levels would result in `natural-beach`.
+ *
+ * Truncation is done in place, i.e. `type` is altered.
+ *
+ * @param type The type to truncate
+ * @param level The number of levels to truncate to
+ */
void TruncValue(uint32_t & type, uint8_t level);
+
+/**
+ * @brief Truncates a type to the number of levels specified, and returns the result.
+ *
+ * For example, truncating `natural-beach-sand` to 2 levels would result in `natural-beach`.
+ *
+ * This is a convenience wrapper around `TruncValue()`, which will not alter `type`.
+ *
+ * @param type The type to truncate
+ * @param level The number of levels to truncate to
+ *
+ * @return The truncated type
+ */
inline uint32_t Trunc(uint32_t type, uint8_t level)
{
TruncValue(type, level);
return type;
}
+
uint8_t GetLevel(uint32_t type);
} // namespace ftype
diff --git a/libs/indexer/data_source.hpp b/libs/indexer/data_source.hpp
index fb9f4e680..efcc51ec0 100644
--- a/libs/indexer/data_source.hpp
+++ b/libs/indexer/data_source.hpp
@@ -19,20 +19,31 @@ class DataSource : public MwmSet
/// Registers a new map.
std::pair RegisterMap(platform::LocalCountryFile const & localFile);
- /// Deregisters a map from internal records.
- ///
- /// \param countryFile A countryFile denoting a map to be deregistered.
- /// \return True if the map was successfully deregistered. If map is locked
- /// now, returns false.
+ /**
+ * @brief Deregisters a map from internal records.
+ * @param countryFile A `CountryFile` denoting a map to be deregistered.
+ * @return True if the map was successfully deregistered, false if the map is locked now.
+ */
bool DeregisterMap(platform::CountryFile const & countryFile);
void ForEachFeatureIDInRect(FeatureIdCallback const & f, m2::RectD const & rect, int scale,
covering::CoveringMode mode = covering::ViewportWithLowLevels) const;
void ForEachInRect(FeatureCallback const & f, m2::RectD const & rect, int scale) const;
- // Calls |f| for features closest to |center| until |stopCallback| returns true or distance
- // |sizeM| from has been reached. Then for EditableDataSource calls |f| for each edited feature
- // inside square with center |center| and side |2 * sizeM|. Edited features are not in the same
- // hierarchy and there is no fast way to merge frozen and edited features.
+
+ /**
+ * @brief Iterates over features within a given distance of a center point.
+ *
+ * Calls `f` for features closest to `center` until `stopCallback` returns true or distance
+ * `sizeM` from has been reached. Then for EditableDataSource calls `f` for each edited feature
+ * inside square with center `center` and side `2 * sizeM`. Edited features are not in the same
+ * hierarchy and there is no fast way to merge frozen and edited features.
+ *
+ * @brief f Callback function that is called on each feature.
+ * @brief stopCallback Callback function which decides whether to continue searching or stop.
+ * @brief center The center of the search area.
+ * @brief sizeM The size of the search area, as a distance from the center point.
+ * @brief scale
+ */
void ForClosestToPoint(FeatureCallback const & f, StopSearchCallback const & stopCallback, m2::PointD const & center,
double sizeM, int scale) const;
void ForEachInScale(FeatureCallback const & f, int scale) const;
@@ -66,18 +77,24 @@ class DataSource : public MwmSet
std::unique_ptr m_factory;
};
-// DataSource which operates with features from mwm file and does not support features creation
-// deletion or modification.
+/**
+ * @brief A `DataSource` which operates with features from an MWM file and does not support
+ * creation, deletion or modification of features.
+ */
class FrozenDataSource : public DataSource
{
public:
FrozenDataSource() : DataSource(std::make_unique()) {}
};
-/// Guard for loading features from particular MWM by demand.
-/// @note If you need to work with FeatureType from different threads you need to use
-/// a unique FeaturesLoaderGuard instance for every thread.
-/// For an example of concurrent extracting feature details please see ConcurrentFeatureParsingTest.
+/**
+ * @brief Guard for loading features from particular MWM by demand.
+ *
+ * @note If you need to work with `FeatureType` from different threads, you need to use
+ * a unique `FeaturesLoaderGuard` instance for every thread.
+ * For an example of concurrent extracting feature details please see `ConcurrentFeatureParsingTest`
+ * in `routing/routing_integration_tests`.
+ */
class FeaturesLoaderGuard
{
public:
diff --git a/libs/indexer/feature.hpp b/libs/indexer/feature.hpp
index 2f6de8486..3668cf7f3 100644
--- a/libs/indexer/feature.hpp
+++ b/libs/indexer/feature.hpp
@@ -77,6 +77,18 @@ class FeatureType
// (number of points in inner triangle-strips).
using PointsBufferT = buffer_vector;
+ /**
+ * @brief Retrieves the points of the feature.
+ *
+ * Depending on `scale`, the geometry may be simplified by reducing groups of nearby points to
+ * one point. If `scale` equals `FeatureType::BEST_GEOMETRY`, no such simplification takes place.
+ *
+ * Points are cached between calls and `scale` may not be honored if cached points are returned.
+ * To reliably enforce `scale`, call `ResetGemoetry()` immediately prior to `GetPoints()`.
+ *
+ * @param scale The map scale
+ * @return The points of the feature, simplified according to `scale`.
+ */
PointsBufferT const & GetPoints(int scale);
PointsBufferT const & GetTrianglesAsPoints(int scale);
@@ -86,6 +98,13 @@ class FeatureType
void ParseHeader2();
void ParseRelations();
void ParseAllBeforeGeometry() { ParseRelations(); }
+
+ /**
+ * @brief Resets the geometry.
+ *
+ * This discards any cached points, resulting in points being re-fetched the next time
+ * `GetPoints()` or `GetTrianglesAsPoints()` is called.
+ */
void ResetGeometry();
void ParseGeometry(int scale);
void ParseTriangles(int scale);
diff --git a/libs/indexer/ftypes_matcher.cpp b/libs/indexer/ftypes_matcher.cpp
index 1082d294f..af640ea64 100644
--- a/libs/indexer/ftypes_matcher.cpp
+++ b/libs/indexer/ftypes_matcher.cpp
@@ -66,7 +66,32 @@ class HighwayClasses
m_map[c.GetTypeByPath({"highway", "steps"})] = HighwayClass::Pedestrian;
m_map[c.GetTypeByPath({"highway", "cycleway"})] = HighwayClass::Pedestrian;
m_map[c.GetTypeByPath({"highway", "path"})] = HighwayClass::Pedestrian;
+
+ // mvglasow: Appears to never have been used
m_map[c.GetTypeByPath({"highway", "construction"})] = HighwayClass::Pedestrian;
+
+ // Construction types
+ m_map[c.GetTypeByPath({"highway", "construction", "motorway"})] = HighwayClass::Motorway;
+ m_map[c.GetTypeByPath({"highway", "construction", "motorway_link"})] = HighwayClass::Motorway;
+ m_map[c.GetTypeByPath({"highway", "construction", "trunk"})] = HighwayClass::Trunk;
+ m_map[c.GetTypeByPath({"highway", "construction", "trunk_link"})] = HighwayClass::Trunk;
+
+ m_map[c.GetTypeByPath({"highway", "construction", "primary"})] = HighwayClass::Primary;
+ m_map[c.GetTypeByPath({"highway", "primary_link"})] = HighwayClass::Primary;
+
+ m_map[c.GetTypeByPath({"highway", "construction", "secondary"})] = HighwayClass::Secondary;
+ m_map[c.GetTypeByPath({"highway", "construction", "secondary_link"})] = HighwayClass::Secondary;
+
+ m_map[c.GetTypeByPath({"highway", "construction", "tertiary"})] = HighwayClass::Tertiary;
+ m_map[c.GetTypeByPath({"highway", "construction", "tertiary_link"})] = HighwayClass::Tertiary;
+
+ m_map[c.GetTypeByPath({"highway", "construction", "unclassified"})] = HighwayClass::LivingStreet;
+ m_map[c.GetTypeByPath({"highway", "construction", "residential"})] = HighwayClass::LivingStreet;
+ m_map[c.GetTypeByPath({"highway", "construction", "living_street"})] = HighwayClass::LivingStreet;
+ m_map[c.GetTypeByPath({"highway", "construction", "road"})] = HighwayClass::LivingStreet;
+
+ m_map[c.GetTypeByPath({"highway", "construction", "service"})] = HighwayClass::Service;
+ m_map[c.GetTypeByPath({"highway", "construction", "track"})] = HighwayClass::Service;
}
HighwayClass Get(uint32_t t) const
diff --git a/libs/indexer/ftypes_matcher.hpp b/libs/indexer/ftypes_matcher.hpp
index c44dea423..281a391be 100644
--- a/libs/indexer/ftypes_matcher.hpp
+++ b/libs/indexer/ftypes_matcher.hpp
@@ -708,24 +708,46 @@ double GetRadiusByPopulationForRouting(uint64_t p, LocalityType localityType);
uint64_t GetPopulationByRadius(double r);
//@}
-// Highway class. The order is important.
-// The enum values follow from the biggest roads (Trunk) to the smallest ones (Service).
+/**
+ * @brief Highway class.
+ *
+ * The order is important. The enum values follow from the biggest roads (Trunk) to the smallest ones (Service).
+ */
enum class HighwayClass
{
- Undefined = 0, // There has not been any attempt of calculating HighwayClass.
+ /**
+ * Used when there has not been any attempt of calculating HighwayClass.
+ */
+ Undefined = 0,
Motorway,
Trunk,
Primary,
Secondary,
Tertiary,
+ /**
+ * Unclassified, residential, living street and `highway=road`.
+ */
LivingStreet,
+ /**
+ * Service, track, busway and `man_made=pier`.
+ */
Service,
// OSM highway=service type is widely used even for _significant_ roads.
// Adding a new type to distinguish mapped driveway or parking_aisle.
ServiceMinor,
+ /**
+ * Anything not intended for motorized traffic: pedestrian, footway, bridleway, steps, cycleway,
+ * path and also `highway=construction`.
+ */
Pedestrian,
- Transported, // Vehicles are transported by train or ferry.
- Count // This value is used for internals only.
+ /**
+ * Vehicles are transported by train or ferry.
+ */
+ Transported,
+ /**
+ * This value is used for internals only.
+ */
+ Count
};
std::string DebugPrint(HighwayClass const cls);
diff --git a/libs/indexer/mwm_set.hpp b/libs/indexer/mwm_set.hpp
index 6236fe6dc..7e064cc6e 100644
--- a/libs/indexer/mwm_set.hpp
+++ b/libs/indexer/mwm_set.hpp
@@ -151,8 +151,10 @@ class MwmSet
explicit MwmSet(size_t cacheSize = 64) : m_cacheSize(cacheSize) {}
virtual ~MwmSet() = default;
- // Mwm handle, which is used to refer to mwm and prevent it from
- // deletion when its FileContainer is used.
+ /**
+ * @brief Mwm handle, which is used to refer to mwm and prevent it from deletion when its
+ * FileContainer is used.
+ */
class MwmHandle
{
public:
@@ -237,19 +239,26 @@ class MwmSet
BadFile
};
- // An Observer interface to MwmSet. Note that these functions can
- // be called from *ANY* thread because most signals are sent when
- // some thread releases its MwmHandle, so overrides must be as fast
- // as possible and non-blocking when it's possible.
+ /**
+ * @brief An Observer interface to `MwmSet`.
+ *
+ * Note that these functions can be called from *ANY* thread because most signals are sent when
+ * some thread releases its MwmHandle, so overrides must be as fast as possible and non-blocking
+ * when it's possible.
+ */
class Observer
{
public:
virtual ~Observer() = default;
- // Called when a map is registered for the first time and can be used.
+ /**
+ * @brief Called when a map is registered for the first time and can be used.
+ */
virtual void OnMapRegistered(platform::LocalCountryFile const & /* localFile */) {}
- // Called when a map is deregistered and can no longer be used.
+ /**
+ * @brief Called when a map is deregistered and can no longer be used.
+ */
virtual void OnMapDeregistered(platform::LocalCountryFile const & /* localFile */) {}
};
@@ -298,7 +307,14 @@ class MwmSet
/// @todo In fact, std::shared_ptr is a MwmId. Seems like better to make vector interface.
void GetMwmsInfo(std::vector> & info) const;
- // Clears caches and mwm's registry. All known mwms won't be marked as DEREGISTERED.
+ /**
+ * @brief Clears caches and mwm's registry.
+ *
+ * All known mwms won't be marked as DEREGISTERED.
+ *
+ * @todo what does “all won’t be marked” mean? Not all will be marked/some might not be marked?
+ * Or all will be unmarked?
+ */
void Clear();
void ClearCache();
@@ -339,10 +355,18 @@ class MwmSet
ProcessEventList(events);
}
- // Sets |status| in |info|, adds corresponding event to |event|.
+ /**
+ * @brief Sets `status` in `info`, adds corresponding event to `event`.
+ * @param info
+ * @param status
+ * @param events
+ */
void SetStatus(MwmInfo & info, MwmInfo::Status status, EventList & events);
- // Triggers observers on each event in |events|.
+ /**
+ * @brief Triggers observers on each event in `events`.
+ * @param events
+ */
void ProcessEventList(EventList & events);
/// @precondition This function is always called under mutex m_lock.
diff --git a/libs/map/framework.cpp b/libs/map/framework.cpp
index 605253b57..e9345a433 100644
--- a/libs/map/framework.cpp
+++ b/libs/map/framework.cpp
@@ -7,7 +7,9 @@
#include "ge0/url_generator.hpp"
+#include "routing/index_router.hpp"
#include "routing/route.hpp"
+#include "routing/routing_helpers.hpp"
#include "routing/speed_camera_prohibition.hpp"
#include "routing_common/num_mwm_id.hpp"
@@ -17,13 +19,18 @@
#include "search/locality_finder.hpp"
#include "storage/country_info_getter.hpp"
+#include "storage/routing_helpers.hpp"
#include "storage/storage.hpp"
#include "storage/storage_helpers.hpp"
+#include "traffxml/traff_source.hpp"
+
#include "drape_frontend/color_constants.hpp"
#include "drape_frontend/gps_track_point.hpp"
#include "drape_frontend/visual_params.hpp"
+#include "editor/editable_data_source.hpp"
+
#include "descriptions/loader.hpp"
#include "indexer/categories_holder.hpp"
@@ -39,6 +46,7 @@
#include "indexer/scales.hpp"
#include "indexer/transliteration_loader.hpp"
+#include "platform/local_country_file_utils.hpp"
#include "platform/localization.hpp"
#include "platform/measurement_utils.hpp"
#include "platform/mwm_version.hpp"
@@ -61,11 +69,13 @@
#include "base/logging.hpp"
#include "base/math.hpp"
+#include "base/stl_helpers.hpp"
#include "base/string_utils.hpp"
#include "std/target_os.hpp"
#include "defines.hpp"
+#include "private.h"
#include
@@ -93,6 +103,8 @@ std::string_view constexpr kAllow3dKey = "Allow3d";
std::string_view constexpr kAllow3dBuildingsKey = "Buildings3d";
std::string_view constexpr kAllowAutoZoom = "AutoZoom";
std::string_view constexpr kTrafficEnabledKey = "TrafficEnabled";
+std::string_view constexpr kTrafficHttpEnabledKey = "TrafficHttpEnabled";
+std::string_view constexpr kTrafficHttpUrlKey = "TrafficHttpUrl";
std::string_view constexpr kTransitSchemeEnabledKey = "TransitSchemeEnabled";
std::string_view constexpr kIsolinesEnabledKey = "IsolinesEnabled";
std::string_view constexpr kOutdoorsEnabledKey = "OutdoorsEnabled";
@@ -288,8 +300,11 @@ Framework::Framework(FrameworkParams const & params, bool loadMaps)
[this]() -> StringsBundle const &
{ return m_stringsBundle; }, [this]() -> power_management::PowerManager const & { return m_powerManager; }),
static_cast(*this))
- , m_trafficManager(bind(&Framework::GetMwmsByRect, this, _1, false /* rough */), kMaxTrafficCacheSizeBytes,
- m_routingManager.RoutingSession())
+ , m_trafficManager(m_featuresFetcher.GetDataSource(),
+ [this]() -> storage::CountryInfoGetter const & { return GetCountryInfoGetter(); },
+ [this](string const & id) -> string { return m_storage.GetParentIdFor(id); },
+ bind(&Framework::GetMwmsByRect, this, _1, false /* rough */),
+ kMaxTrafficCacheSizeBytes, m_routingManager.RoutingSession())
, m_lastReportedCountry(kInvalidCountryId)
, m_popularityLoader(m_featuresFetcher.GetDataSource(), POPULARITY_RANKS_FILE_TAG)
, m_descriptionsLoader(std::make_unique(m_featuresFetcher.GetDataSource()))
@@ -363,10 +378,10 @@ Framework::Framework(FrameworkParams const & params, bool loadMaps)
editor.SetDelegate(make_unique(m_featuresFetcher.GetDataSource()));
editor.SetInvalidateFn([this]() { InvalidateRect(GetCurrentViewport()); });
- /// @todo Uncomment when we will integrate a traffic provider.
- // m_trafficManager.SetCurrentDataVersion(m_storage.GetCurrentDataVersion());
- // m_trafficManager.SetSimplifiedColorScheme(LoadTrafficSimplifiedColors());
- // m_trafficManager.SetEnabled(LoadTrafficEnabled());
+ if (params.m_trafficTestMode)
+ m_trafficManager.SetTestMode();
+ m_trafficManager.SetCurrentDataVersion(m_storage.GetCurrentDataVersion());
+ m_trafficManager.SetSimplifiedColorScheme(LoadTrafficSimplifiedColors());
m_isolinesManager.SetEnabled(LoadIsolinesEnabled());
@@ -398,6 +413,19 @@ Framework::~Framework()
m_featuresFetcher.SetOnMapDeregisteredCallback(nullptr);
}
+void Framework::InitializeTraffic()
+{
+ m_trafficManager.SetEnabled(LoadTrafficEnabled());
+ if (!m_trafficManager.IsTestMode() && LoadTrafficHttpEnabled())
+ // TODO handle invalid URLs
+ traffxml::HttpTraffSource::Create(m_trafficManager, LoadTrafficHttpUrl());
+
+ /*
+ * MockTraffSource for debugging purposes.
+ */
+ //traffxml::MockTraffSource::Create(m_trafficManager);
+}
+
void Framework::ShowNode(storage::CountryId const & countryId)
{
StopLocationFollow();
@@ -413,14 +441,14 @@ void Framework::OnCountryFileDownloaded(storage::CountryId const &, storage::Loc
m2::RectD rect = mercator::Bounds::FullRect();
if (localFile && localFile->OnDisk(MapFileType::Map))
- {
- auto const res = RegisterMap(*localFile);
- MwmSet::MwmId const & id = res.first;
- if (id.IsAlive())
- rect = id.GetInfo()->m_bordersRect;
- }
+ m_trafficManager.RunSynchronized([this, localFile, &rect](){
+ auto const res = RegisterMap(*localFile);
+ MwmSet::MwmId const & id = res.first;
+ if (id.IsAlive())
+ rect = id.GetInfo()->m_bordersRect;
+ m_trafficManager.Invalidate(id);
+ });
- m_trafficManager.Invalidate();
m_transitManager.Invalidate();
m_isolinesManager.Invalidate();
@@ -502,6 +530,8 @@ void Framework::LoadMapsSync()
LOG(LDEBUG, ("Editor initialized"));
GetStorage().RestoreDownloadQueue();
+
+ InitializeTraffic();
}
// Small copy-paste with LoadMapsSync, but I don't have a better solution.
@@ -524,6 +554,8 @@ void Framework::LoadMapsAsync(std::function && callback)
GetStorage().RestoreDownloadQueue();
+ InitializeTraffic();
+
callback();
});
}).detach();
@@ -2490,6 +2522,42 @@ void Framework::SaveTrafficEnabled(bool trafficEnabled)
settings::Set(kTrafficEnabledKey, trafficEnabled);
}
+void Framework::SetTrafficHttpEnabled(bool enabled)
+{
+ m_trafficManager.SetHttpTraffSource(enabled, LoadTrafficHttpUrl());
+}
+
+bool Framework::LoadTrafficHttpEnabled()
+{
+ bool enabled;
+ if (!settings::Get(kTrafficHttpEnabledKey, enabled))
+ enabled = false;
+ return enabled;
+}
+
+void Framework::SaveTrafficHttpEnabled(bool trafficHttpEnabled)
+{
+ settings::Set(kTrafficHttpEnabledKey, trafficHttpEnabled);
+}
+
+void Framework::SetTrafficHttpUrl(std::string url)
+{
+ m_trafficManager.SetHttpTraffSource(LoadTrafficHttpEnabled(), url);
+}
+
+std::string Framework::LoadTrafficHttpUrl()
+{
+ std::string url;
+ if (!settings::Get(kTrafficHttpUrlKey, url))
+ url = TRAFFIC_HTTP_URL_DEFAULT;
+ return url;
+}
+
+void Framework::SaveTrafficHttpUrl(std::string trafficHttpUrl)
+{
+ settings::Set(kTrafficHttpUrlKey, trafficHttpUrl);
+}
+
bool Framework::LoadTrafficSimplifiedColors()
{
bool simplified;
diff --git a/libs/map/framework.hpp b/libs/map/framework.hpp
index 6ef95bab4..b2febdb08 100644
--- a/libs/map/framework.hpp
+++ b/libs/map/framework.hpp
@@ -104,11 +104,22 @@ class Loader;
/// build version for screenshots.
// #define FIXED_LOCATION
+/**
+ * @brief Initialization parameters for the framework.
+ *
+ * `FrameworkParams` is intended for parameters which are hardcoded rather than read from a
+ * configuration. It allows test cases to run on a tailored configuration.
+ */
struct FrameworkParams
{
bool m_enableDiffs = true;
size_t m_numSearchAPIThreads = 1;
+ /**
+ * @brief Whether the traffic manager should start in test mode.
+ */
+ bool m_trafficTestMode = false;
+
FrameworkParams() = default;
FrameworkParams(bool enableDiffs) : m_enableDiffs(enableDiffs) {}
};
@@ -234,7 +245,34 @@ class Framework
/// \note It works for group and leaf node.
bool HasUnsavedEdits(storage::CountryId const & countryId);
+ /**
+ * @brief Loads maps synchronously.
+ *
+ * Maps are loaded on the calling thread.
+ *
+ * This function also performs certain initialization operations which depend on map data being
+ * available, such as search, traffic and the download queue.
+ *
+ * @note This function is not suitable for use on platforms which enforce restrictions on
+ * time-consuming or potentially blocking operations on the UI thread (as Android does). On such
+ * platforms, `LoadMapsAsync()` should be used instead.
+ */
void LoadMapsSync();
+
+ /**
+ * @brief Loads maps asynchronously.
+ *
+ * Maps are loaded on a new thread. Some operations are executed as part of a separate task which
+ * is posted to the GUI thread.
+ *
+ * This function also performs certain initialization operations which depend on map data being
+ * available, such as search, traffic and the download queue.
+ *
+ * After finishing initialization, the caller-supplied callback function is called. This function
+ * also runs on the GUI thread and should therefore not perform any time-consuming operations.
+ *
+ * @param callback A callback function to run at the end of initialization.
+ */
void LoadMapsAsync(std::function && callback);
/// Registers all local map files in internal indexes.
@@ -385,6 +423,16 @@ class Framework
private:
std::vector GetSelectedFeatureTriangles() const;
+ /**
+ * @brief Initializes the traffic manager.
+ *
+ * This enables the traffic manager if defined in settings. If the traffic manager is not in test
+ * mode, all cunfigured sources are also added here.
+ *
+ * Maps must be loaded prior to calling this method.
+ */
+ void InitializeTraffic();
+
public:
/// @name GPS location updates routine.
void OnLocationError(location::TLocationError error);
@@ -491,11 +539,18 @@ class Framework
std::unique_ptr m_descriptionsLoader;
public:
- // Moves viewport to the search result and taps on it.
+ /**
+ * @brief Moves viewport to the search result and taps on it.
+ * @param res
+ * @param animation
+ */
void SelectSearchResult(search::Result const & res, bool animation);
- // Cancels all searches, stops location follow and then selects
- // search result.
+ /**
+ * @brief Cancels all searches, stops location follow and then selects search result.
+ * @param res
+ * @param animation
+ */
void ShowSearchResult(search::Result const & res, bool animation = true);
void UpdateViewport(search::Results const & results);
@@ -729,6 +784,14 @@ class Framework
bool LoadTrafficEnabled();
void SaveTrafficEnabled(bool trafficEnabled);
+ void SetTrafficHttpEnabled(bool enabled);
+ bool LoadTrafficHttpEnabled();
+ void SaveTrafficHttpEnabled(bool trafficHttpEnabled);
+
+ void SetTrafficHttpUrl(std::string url);
+ std::string LoadTrafficHttpUrl();
+ void SaveTrafficHttpUrl(std::string trafficHttpUrl);
+
bool LoadTrafficSimplifiedColors();
void SaveTrafficSimplifiedColors(bool simplified);
diff --git a/libs/map/routing_manager.cpp b/libs/map/routing_manager.cpp
index 0f881f5d6..3876a5f19 100644
--- a/libs/map/routing_manager.cpp
+++ b/libs/map/routing_manager.cpp
@@ -498,7 +498,7 @@ void RoutingManager::SetRouterImpl(RouterType type)
VehicleType const vehicleType = GetVehicleType(type);
- m_loadAltitudes = vehicleType != VehicleType::Car;
+ m_loadAltitudes = (vehicleType != VehicleType::Car) && (vehicleType != VehicleType::Decoder);
auto const countryFileGetter = [this](m2::PointD const & p) -> string
{
diff --git a/libs/map/traffic_manager.cpp b/libs/map/traffic_manager.cpp
index 66114e50b..be2a2976e 100644
--- a/libs/map/traffic_manager.cpp
+++ b/libs/map/traffic_manager.cpp
@@ -8,51 +8,80 @@
#include "indexer/ftypes_matcher.hpp"
#include "indexer/scales.hpp"
+#include "geometry/distance_on_sphere.hpp"
#include "geometry/mercator.hpp"
#include "platform/platform.hpp"
+#include "traffxml/traff_model_xml.hpp"
+
using namespace std::chrono;
namespace
{
+/**
+ * Poll interval for traffic data
+ */
auto constexpr kUpdateInterval = minutes(1);
+
+/**
+ * Purge interval for expired traffic messages
+ */
+auto constexpr kPurgeInterval = minutes(1);
+
auto constexpr kOutdatedDataTimeout = minutes(5) + kUpdateInterval;
auto constexpr kNetworkErrorTimeout = minutes(20);
auto constexpr kMaxRetriesCount = 5;
+
+/**
+ * Interval at which the Drape engine gets traffic updates while messages are being processed.
+ */
+auto constexpr kDrapeUpdateInterval = seconds(10);
+
+/**
+ * Interval at which the traffic observer gets traffic updates while messages are being processed.
+ */
+auto constexpr kObserverUpdateInterval = minutes(1);
+
+/**
+ * Interval at which the message cache file is updated while messages are being processed.
+ */
+auto constexpr kStorageUpdateInterval = minutes(1);
+
+/**
+ * File name at which traffic data is persisted.
+ */
+auto constexpr kTrafficXMLFileName = "traffic.xml";
+
+/**
+ * Threshold by which viewport or user position can change without requiring the nessage queue to
+ * be resorted.
+ */
+auto constexpr kPositionThreshold = 1000.0;
} // namespace
-TrafficManager::CacheEntry::CacheEntry()
- : m_isLoaded(false)
- , m_dataSize(0)
- , m_retriesCount(0)
- , m_isWaitingForResponse(false)
- , m_lastAvailability(traffic::TrafficInfo::Availability::Unknown)
-{}
-
-TrafficManager::CacheEntry::CacheEntry(time_point const & requestTime)
- : m_isLoaded(false)
- , m_dataSize(0)
- , m_lastActiveTime(requestTime)
- , m_lastRequestTime(requestTime)
- , m_retriesCount(0)
- , m_isWaitingForResponse(true)
- , m_lastAvailability(traffic::TrafficInfo::Availability::Unknown)
-{}
-
-TrafficManager::TrafficManager(GetMwmsByRectFn const & getMwmsByRectFn, size_t maxCacheSizeBytes,
- traffic::TrafficObserver & observer)
- : m_getMwmsByRectFn(getMwmsByRectFn)
- , m_observer(observer)
+TrafficManager::TrafficManager(DataSource & dataSource, CountryInfoGetterFn countryInfoGetter,
+ const CountryParentNameGetterFn &countryParentNameGetter,
+ GetMwmsByRectFn const & getMwmsByRectFn, size_t maxCacheSizeBytes,
+ routing::RoutingSession & routingSession)
+ : m_dataSource(dataSource)
+ , m_countryInfoGetterFn(countryInfoGetter)
+ , m_countryParentNameGetterFn(countryParentNameGetter)
+ , m_getMwmsByRectFn(getMwmsByRectFn)
+ , m_routingSession(routingSession)
, m_currentDataVersion(0)
, m_state(TrafficState::Disabled)
- , m_maxCacheSizeBytes(maxCacheSizeBytes)
, m_isRunning(true)
, m_isPaused(false)
, m_thread(&TrafficManager::ThreadRoutine, this)
{
CHECK(m_getMwmsByRectFn != nullptr, ());
+ GetPlatform().RunTask(Platform::Thread::Gui, [this]() {
+ m_routingSession.SetChangeSessionStateCallback([this](routing::SessionState previous, routing::SessionState current) {
+ OnChangeRoutingSessionState(previous, current);
+ });
+ });
}
TrafficManager::~TrafficManager()
@@ -77,6 +106,12 @@ void TrafficManager::Teardown()
m_thread.join();
}
+std::map TrafficManager::GetMessageCache()
+{
+ std::lock_guard lock(m_mutex);
+ return m_messageCache;
+}
+
TrafficManager::TrafficState TrafficManager::GetState() const
{
return m_state;
@@ -89,32 +124,62 @@ void TrafficManager::SetStateListener(TrafficStateChangedFn const & onStateChang
void TrafficManager::SetEnabled(bool enabled)
{
+ /*
+ * Whether to notify interested parties that traffic data has been updated.
+ * This is based on the return value of RestoreCache().
+ */
+ bool notifyUpdate = false;
{
std::lock_guard lock(m_mutex);
if (enabled == IsEnabled())
- return;
- Clear();
+ return;
+ if (enabled)
+ {
+ if (!m_traffDecoder)
+ // deferred decoder initialization (requires maps to be loaded)
+ m_traffDecoder = make_unique(m_dataSource, m_countryInfoGetterFn,
+ m_countryParentNameGetterFn, m_messageCache);
+ if (!m_storage && !IsTestMode())
+ {
+ m_storage = std::make_unique(kTrafficXMLFileName);
+ notifyUpdate = RestoreCache();
+ m_lastStorageUpdate = steady_clock::now();
+ }
+ }
ChangeState(enabled ? TrafficState::Enabled : TrafficState::Disabled);
}
m_drapeEngine.SafeCall(&df::DrapeEngine::EnableTraffic, enabled);
if (enabled)
- Invalidate();
+ {
+ if (notifyUpdate)
+ OnTrafficDataUpdate();
+ else
+ RecalculateSubscription(true);
+ m_canSetMode = false;
+ }
else
- m_observer.OnTrafficInfoClear();
+ {
+ Unsubscribe();
+ m_routingSession.OnTrafficInfoClear();
+ }
}
void TrafficManager::Clear()
{
- m_currentCacheSizeBytes = 0;
- m_mwmCache.clear();
- m_lastDrapeMwmsByRect.clear();
- m_lastRoutingMwmsByRect.clear();
- m_activeDrapeMwms.clear();
- m_activeRoutingMwms.clear();
- m_requestedMwms.clear();
- m_trafficETags.clear();
+ {
+ std::lock_guard lock(m_mutex);
+
+ m_messageCache.clear();
+ m_feedQueue.clear();
+ }
+ OnTrafficDataUpdate();
+}
+
+void TrafficManager::SetTrafficUpdateCallbackFn(TrafficUpdateCallbackFn && fn)
+{
+ m_trafficUpdateCallbackFn = std::move(fn);
}
void TrafficManager::SetDrapeEngine(ref_ptr engine)
@@ -129,24 +194,7 @@ void TrafficManager::SetCurrentDataVersion(int64_t dataVersion)
void TrafficManager::OnMwmDeregistered(platform::LocalCountryFile const & countryFile)
{
- if (!IsEnabled())
- return;
-
- {
- std::lock_guard lock(m_mutex);
-
- MwmSet::MwmId mwmId;
- for (auto const & cacheEntry : m_mwmCache)
- {
- if (cacheEntry.first.IsDeregistered(countryFile))
- {
- mwmId = cacheEntry.first;
- break;
- }
- }
-
- ClearCache(mwmId);
- }
+ // TODO we don’t need this any more (called by Framework::OnMapDeregistered())
}
void TrafficManager::OnDestroySurface()
@@ -159,18 +207,154 @@ void TrafficManager::OnRecoverSurface()
Resume();
}
-void TrafficManager::Invalidate()
+void TrafficManager::OnChangeRoutingSessionState(routing::SessionState previous, routing::SessionState current)
{
- if (!IsEnabled())
+ // TODO assert we’re running on the UI thread
+ LOG(LINFO, ("Routing session state changed from", previous, "to", current));
+ LOG(LINFO, ("Running on thread", std::this_thread::get_id()));
+
+ m_routingSessionState = current;
+
+ /*
+ * Filter based on session state (see routing_callbacks.hpp for states and transitions).
+ *
+ * GetAllRegions() seems to get fresh data during build (presumably also rebuild), which will be
+ * available on the next state transition (to RouteNotStarted or OnRoute) and remain unchanged
+ * until the next route (re)build. Therefore, calling GetAllRegions() when new state is one of
+ * RouteNotStarted, OnRoute or RouteNoFollowing, and clearing MWMs when the new state is
+ * NoValidRoute, and ignoring all other transitions, should work for our purposes.
+ * There may be cases where we first calculate the route, then subscribe to the regions and only
+ * then get notified about a traffic problem on the route, requiring us to recalculate.
+ */
+ std::set mwmNames;
+ if (current == routing::SessionState::RouteNotStarted
+ || current == routing::SessionState::OnRoute
+ || current == routing::SessionState::RouteNoFollowing)
+ /*
+ * GetAllRegions() may block when run for the first time. This should happen during routing, so
+ * the call here would always return cached results without blocking, causing no problems on the
+ * UI thread.
+ *
+ * Note that this method is called before the state is updated internally, thus `GetAllRegions()`
+ * and any other functions would still have `previous` as their state.
+ */
+ m_routingSession.GetAllRegions(mwmNames);
+ else if (current == routing::SessionState::NoValidRoute)
+ mwmNames.clear();
+ else
+ // for all other states, keep current set
return;
- m_lastDrapeMwmsByRect.clear();
- m_lastRoutingMwmsByRect.clear();
+ LOG(LINFO, ("Router MWMs:", mwmNames));
+
+ std::set mwms;
+ for (auto const & mwmName : mwmNames)
+ {
+ MwmSet::MwmId mwmId = m_dataSource.GetMwmIdByCountryFile(platform::CountryFile(mwmName));
+ if (mwmId.IsAlive())
+ mwms.insert(mwmId);
+ }
+
+ LOG(LINFO, ("MWM set:", mwms));
+
+ {
+ std::lock_guard lock(m_mutex);
+
+ if (mwms != m_activeRoutingMwms)
+ {
+ m_activeMwmsChanged = true;
+ std::swap(mwms, m_activeRoutingMwms);
+
+ if ((m_activeDrapeMwms.empty() && m_activePositionMwms.empty() && m_activeRoutingMwms.empty())
+ || !IsEnabled() || IsInvalidState() || IsPausedAndNotRouting())
+ return;
+
+ m_condition.notify_one();
+ }
+ }
+}
+
+void TrafficManager::RecalculateSubscription(bool forceRenewal)
+{
+ if (!IsEnabled() || IsPausedAndNotRouting())
+ return;
if (m_currentModelView.second)
UpdateViewport(m_currentModelView.first);
if (m_currentPosition.second)
UpdateMyPosition(m_currentPosition.first);
+
+ {
+ std::lock_guard lock(m_mutex);
+
+ /*
+ * If UpdateViewport() or UpdateMyPosition() had changes, they would also have updated the
+ * routing MWMs and reset m_activeMwmsChanged. If neither of them had changes and
+ * m_activeMwmsChanged is true, it indicates changes in route MWMs which we need to process.
+ * If `forceRenewal` is true, we set `m_activeMwmsChanged` to true in order to force renewal of
+ * all subscriptions.
+ */
+ m_activeMwmsChanged |= forceRenewal;
+ if (m_activeMwmsChanged)
+ {
+ if ((m_activeDrapeMwms.empty() && m_activePositionMwms.empty() && m_activeRoutingMwms.empty())
+ || IsInvalidState())
+ return;
+
+ m_condition.notify_one();
+ }
+ }
+}
+
+void TrafficManager::Invalidate(MwmSet::MwmId const & mwmId)
+{
+ auto const mwmRect = mwmId.GetInfo()->m_bordersRect; // m2::RectD
+ traffxml::TraffFeed invalidated;
+
+ for (auto it = m_messageCache.begin(); it != m_messageCache.end(); )
+ {
+ if (!it->second.m_location)
+ {
+ it++;
+ continue;
+ }
+
+ bool isInvalid = false;
+
+ // invalidate if decoded location uses a previous version of the MWM
+ for (auto const & [decodedMwmId, coloring] : it->second.m_decoded)
+ if (decodedMwmId.GetInfo()->GetCountryName() == mwmId.GetInfo()->GetCountryName())
+ isInvalid = true;
+
+ // invalidate if bounding rect of reference points intersects with bounding rect of MWM
+ if (!isInvalid)
+ {
+ m2::RectD locationRect;
+ for (auto const & point : {it->second.m_location.value().m_from,
+ it->second.m_location.value().m_via,
+ it->second.m_location.value().m_at,
+ it->second.m_location.value().m_to})
+ if (point)
+ locationRect.Add(mercator::FromLatLon(point.value().m_coordinates));
+ isInvalid = locationRect.IsIntersect(mwmRect);
+ }
+
+ if (isInvalid)
+ {
+ traffxml::TraffMessage message(it->second);
+ message.m_decoded.clear();
+ invalidated.push_back(message);
+ it = m_messageCache.erase(it);
+ }
+ else
+ ++it;
+ }
+
+ if (!invalidated.empty())
+ {
+ m_feedQueue.insert(m_feedQueue.begin(), invalidated);
+ m_condition.notify_one();
+ }
}
void TrafficManager::UpdateActiveMwms(m2::RectD const & rect, std::vector & lastMwmsByRect,
@@ -183,11 +367,17 @@ void TrafficManager::UpdateActiveMwms(m2::RectD const & rect, std::vector lock(m_mutex);
+ m_activeMwmsChanged = true;
activeMwms.clear();
for (auto const & mwm : mwms)
if (mwm.IsAlive())
activeMwms.insert(mwm);
- RequestTrafficData();
+
+ if ((m_activeDrapeMwms.empty() && m_activePositionMwms.empty() && m_activeRoutingMwms.empty())
+ || !IsEnabled() || IsInvalidState() || IsPausedAndNotRouting())
+ return;
+
+ m_condition.notify_one();
}
}
@@ -198,21 +388,33 @@ void TrafficManager::UpdateMyPosition(MyPosition const & myPosition)
double constexpr kSquareSideM = 5000.0;
m_currentPosition = {myPosition, true /* initialized */};
- if (!IsEnabled() || IsInvalidState() || m_isPaused)
+ if (!m_currentPositionLazy.second
+ || (mercator::DistanceOnEarth(m_currentPositionLazy.first.m_position, myPosition.m_position) > kPositionThreshold))
+ {
+ m_isFeedQueueSortInvalid = true;
+ m_currentPositionLazy = m_currentPosition;
+ }
+
+ if (!IsEnabled() || IsInvalidState() || IsPausedAndNotRouting())
return;
m2::RectD const rect = mercator::RectByCenterXYAndSizeInMeters(myPosition.m_position, kSquareSideM / 2.0);
// Request traffic.
- UpdateActiveMwms(rect, m_lastRoutingMwmsByRect, m_activeRoutingMwms);
-
- // @TODO Do all routing stuff.
+ UpdateActiveMwms(rect, m_lastPositionMwmsByRect, m_activePositionMwms);
}
void TrafficManager::UpdateViewport(ScreenBase const & screen)
{
m_currentModelView = {screen, true /* initialized */};
- if (!IsEnabled() || IsInvalidState() || m_isPaused)
+ if (!m_currentModelViewLazy.second
+ || (mercator::DistanceOnEarth(m_currentModelViewLazy.first.ClipRect().Center(), screen.ClipRect().Center()) > kPositionThreshold))
+ {
+ m_isFeedQueueSortInvalid = true;
+ m_currentModelViewLazy = m_currentModelView;
+ }
+
+ if (!IsEnabled() || IsInvalidState() || IsPausedAndNotRouting())
return;
if (df::GetZoomLevel(screen.GetScale()) < df::kRoadClass0ZoomLevel)
@@ -222,287 +424,658 @@ void TrafficManager::UpdateViewport(ScreenBase const & screen)
UpdateActiveMwms(screen.ClipRect(), m_lastDrapeMwmsByRect, m_activeDrapeMwms);
}
-void TrafficManager::ThreadRoutine()
+void TrafficManager::SubscribeOrChangeSubscription()
{
- std::vector mwms;
- while (WaitForRequest(mwms))
+ std::set activeMwms;
+
+ if (m_activeMwmsChanged)
{
- for (auto const & mwm : mwms)
{
- if (!mwm.IsAlive())
- continue;
-
- traffic::TrafficInfo info(mwm, m_currentDataVersion);
-
- std::string tag;
- {
- std::lock_guard lock(m_mutex);
- tag = m_trafficETags[mwm];
- }
-
- if (info.ReceiveTrafficData(tag))
- {
- OnTrafficDataResponse(std::move(info));
- }
- else
- {
- LOG(LWARNING, ("Traffic request failed. Mwm =", mwm));
- OnTrafficRequestFailed(std::move(info));
- }
+ std::lock_guard lock(m_mutex);
+ m_activeMwmsChanged = false;
+ UniteActiveMwms(activeMwms);
+ }
- {
- std::lock_guard lock(m_mutex);
- m_trafficETags[mwm] = tag;
- }
+ {
+ std::lock_guard lock(m_trafficSourceMutex);
+ for (auto & source : m_trafficSources)
+ source->SubscribeOrChangeSubscription(activeMwms);
}
- mwms.clear();
}
}
-bool TrafficManager::WaitForRequest(std::vector & mwms)
+void TrafficManager::Unsubscribe()
{
- std::unique_lock lock(m_mutex);
-
- bool const timeout =
- !m_condition.wait_for(lock, kUpdateInterval, [this] { return !m_isRunning || !m_requestedMwms.empty(); });
+ std::lock_guard lock(m_trafficSourceMutex);
+ for (auto & source : m_trafficSources)
+ source->Unsubscribe();
+}
- if (!m_isRunning)
+bool TrafficManager::RestoreCache()
+{
+ ASSERT(m_storage, ("m_storage cannot be null"));
+ pugi::xml_document document;
+ if (!m_storage->Load(document))
+ {
+ LOG(LWARNING, ("Failed to reload cache from storage"));
return false;
+ }
- if (timeout)
- RequestTrafficData();
+ traffxml::TraffFeed feedIn;
+ traffxml::TraffFeed feedOut;
+ bool hasDecoded = false;
+ bool hasUndecoded = false;
+ if (traffxml::ParseTraff(document, m_dataSource, feedIn))
+ {
+ while (!feedIn.empty())
+ {
+ traffxml::TraffMessage message;
+ std::swap(message, feedIn.front());
+ feedIn.erase(feedIn.begin());
- if (!m_requestedMwms.empty())
- mwms.swap(m_requestedMwms);
+ if (!message.IsExpired(traffxml::IsoTime::Now()))
+ {
+ if (!message.m_decoded.empty())
+ {
+ hasDecoded = true;
+ // store message in cache
+ m_messageCache.insert_or_assign(message.m_id, message);
+ }
+ else
+ {
+ hasUndecoded = true;
+ // message needs decoding, prepare to enqueue
+ feedOut.push_back(message);
+ }
+ }
+ }
+ if (!feedOut.empty())
+ m_feedQueue.insert(m_feedQueue.begin(), feedOut);
+ // update notification is caller’s responsibility
+ return hasDecoded && !hasUndecoded;
+ }
+ else
+ {
+ LOG(LWARNING, ("An error occurred parsing the cache file"));
+ }
+ return false;
+}
- return true;
+void TrafficManager::Poll()
+{
+ std::lock_guard lock(m_trafficSourceMutex);
+ for (auto & source : m_trafficSources)
+ if (source->IsPollNeeded())
+ source->Poll();
}
-void TrafficManager::RequestTrafficData(MwmSet::MwmId const & mwmId, bool force)
+void TrafficManager::ReceiveFeed(traffxml::TraffFeed feed)
{
- bool needRequesting = false;
- auto const currentTime = steady_clock::now();
- auto const it = m_mwmCache.find(mwmId);
- if (it == m_mwmCache.end())
{
- needRequesting = true;
- m_mwmCache.insert(std::make_pair(mwmId, CacheEntry(currentTime)));
+ std::lock_guard lock(m_mutex);
+ m_feedQueue.push_back(feed);
+ m_isFeedQueueSortInvalid = true;
}
- else
+ m_condition.notify_one();
+}
+
+void TrafficManager::RegisterSource(std::unique_ptr source)
+{
+ if (IsEnabled())
{
- auto const passedSeconds = currentTime - it->second.m_lastRequestTime;
- if (passedSeconds >= kUpdateInterval || force)
+ std::set activeMwms;
+
{
- needRequesting = true;
- it->second.m_isWaitingForResponse = true;
- it->second.m_lastRequestTime = currentTime;
+ std::lock_guard lock(m_mutex);
+ UniteActiveMwms(activeMwms);
}
- if (!force)
- it->second.m_lastActiveTime = currentTime;
+
+ if (!activeMwms.empty())
+ source->SubscribeOrChangeSubscription(activeMwms);
}
- if (needRequesting)
{
- m_requestedMwms.push_back(mwmId);
- m_condition.notify_one();
+ std::lock_guard lock(m_trafficSourceMutex);
+ m_trafficSources.push_back(std::move(source));
}
+
+ m_isPollNeeded = IsEnabled();
}
-void TrafficManager::RequestTrafficData()
+void TrafficManager::PurgeExpiredMessages()
{
- if ((m_activeDrapeMwms.empty() && m_activeRoutingMwms.empty()) || !IsEnabled() || IsInvalidState() || m_isPaused)
- return;
-
- ForEachActiveMwm([this](MwmSet::MwmId const & mwmId)
- {
- ASSERT(mwmId.IsAlive(), ());
- RequestTrafficData(mwmId, false /* force */);
- });
- UpdateState();
+ PurgeExpiredMessagesImpl();
+ OnTrafficDataUpdate();
}
-void TrafficManager::OnTrafficRequestFailed(traffic::TrafficInfo && info)
+bool TrafficManager::PurgeExpiredMessagesImpl()
{
std::lock_guard lock(m_mutex);
-
- auto it = m_mwmCache.find(info.GetMwmId());
- if (it == m_mwmCache.end())
- return;
-
- it->second.m_isWaitingForResponse = false;
- it->second.m_lastAvailability = info.GetAvailability();
-
- if (info.GetAvailability() == traffic::TrafficInfo::Availability::Unknown && !it->second.m_isLoaded)
+ bool result = false;
+ LOG(LINFO, ("before:", m_messageCache.size(), "message(s)"));
+ traffxml::IsoTime now = traffxml::IsoTime::Now();
+ for (auto it = m_messageCache.begin(); it != m_messageCache.end(); )
{
- if (m_activeDrapeMwms.find(info.GetMwmId()) != m_activeDrapeMwms.cend() ||
- m_activeRoutingMwms.find(info.GetMwmId()) != m_activeRoutingMwms.cend())
+ if (it->second.IsExpired(now))
{
- if (it->second.m_retriesCount < kMaxRetriesCount)
- RequestTrafficData(info.GetMwmId(), true /* force */);
- ++it->second.m_retriesCount;
+ it = m_messageCache.erase(it);
+ result = true;
}
else
+ ++it;
+ }
+ LOG(LINFO, ("after:", m_messageCache.size(), "message(s)"));
+ return result;
+}
+
+void TrafficManager::ConsolidateFeedQueue()
+{
+ std::lock_guard lock(m_mutex);
+ if (m_feedQueue.empty())
+ return;
+ for (size_t i = m_feedQueue.size() - 1; i <= 0; i--)
+ for (size_t j = m_feedQueue.size() - 1; j <= 0; j--)
{
- it->second.m_retriesCount = 0;
+ if (i == j)
+ continue;
+ for (auto it_i = m_feedQueue[i].begin(); it_i != m_feedQueue[i].end(); )
+ for (auto it_j = m_feedQueue[j].end(); it_j != m_feedQueue[j].end(); )
+ if (it_i->m_id == it_j->m_id)
+ {
+ // dupe, remove older
+ if (it_i->m_updateTime < it_j->m_updateTime)
+ {
+ // standard case: i has the newer one
+ ++it_i;
+ it_j = m_feedQueue[j].erase(it_j);
+ }
+ else if (it_i->m_updateTime < it_j->m_updateTime)
+ {
+ // j has the newer one
+ it_i = m_feedQueue[i].erase(it_i);
+ ++it_j;
+ }
+ else if (i > j)
+ {
+ // same time, but feed i was received after j, keep i
+ ++it_i;
+ it_j = m_feedQueue[j].erase(it_j);
+ }
+ else
+ {
+ // same time, but feed j was received after i, keep j
+ ASSERT(i != j, ());
+ it_i = m_feedQueue[i].erase(it_i);
+ ++it_j;
+ }
+ }
}
+ // remove empty feeds from the beginning of the queue
+ while (!m_feedQueue.empty() && m_feedQueue.front().empty())
+ m_feedQueue.erase(m_feedQueue.begin());
+ // merge everything into the first vector
+ for (size_t i = 1; i < m_feedQueue.size(); i++)
+ {
+ if (m_feedQueue[i].empty())
+ continue;
+ m_feedQueue[0].insert(m_feedQueue[0].end(),
+ std::make_move_iterator(m_feedQueue[i].begin()),
+ std::make_move_iterator(m_feedQueue[i].end()));
+ m_feedQueue[i].clear();
+ }
+ // remove empty feeds (any feeds after the first one are empty at this point)
+ if (!m_feedQueue.empty())
+ {
+ if (!m_feedQueue.front().empty())
+ m_feedQueue.resize(1);
+ else
+ m_feedQueue.clear();
}
-
- UpdateState();
}
-void TrafficManager::OnTrafficDataResponse(traffic::TrafficInfo && info)
+void TrafficManager::DecodeFirstMessage()
{
+ traffxml::TraffMessage message;
{
+ // Lock the mutex while iterating over the feed queue
std::lock_guard lock(m_mutex);
-
- auto it = m_mwmCache.find(info.GetMwmId());
- if (it == m_mwmCache.end())
+ // remove empty feeds from the beginning of the queue
+ while (!m_feedQueue.empty() && m_feedQueue.front().empty())
+ m_feedQueue.erase(m_feedQueue.begin());
+ // if we have no more feeds, return (nothing to do)
+ if (m_feedQueue.empty())
return;
+ if (m_isFeedQueueSortInvalid
+ && (m_currentPositionLazy.second || m_currentModelViewLazy.second))
+ {
+ std::sort(m_feedQueue.front().begin(), m_feedQueue.front().end(),
+ [this](const traffxml::TraffMessage & a, const traffxml::TraffMessage & b){
+ // return a < b
+ // put messages first which decode quickly: cancellations, and updates with same location
+ if (a.m_cancellation)
+ return !b.m_cancellation;
+ else if (b.m_cancellation)
+ return false;
+ auto aIt = m_messageCache.find(a.m_id);
+ auto bIt = m_messageCache.find(b.m_id);
+ if ((aIt != m_messageCache.end()) && (aIt->second.m_location == a.m_location))
+ return !((bIt != m_messageCache.end()) && (bIt->second.m_location == b.m_location));
+ else if ((bIt != m_messageCache.end()) && (bIt->second.m_location == b.m_location))
+ return false;
+ // sort by shortest distance between reference point and position/viewport
+ std::vector locations;
+ if (m_currentPositionLazy.second)
+ locations.push_back(mercator::ToLatLon(m_currentPositionLazy.first.m_position));
+ if (m_currentModelViewLazy.second)
+ locations.push_back(mercator::ToLatLon(m_currentModelViewLazy.first.ClipRect().Center()));
+ double aDist = 4.5e+7;
+ double bDist = 4.5e+7;
+ for (auto const & [message, dist] : { std::pair{a, std::ref(aDist)}, {b, std::ref(bDist)} })
+ {
+ // messages without location first
+ if (!message.m_location)
+ {
+ dist.get() = 0;
+ continue;
+ }
+ // for simplification, we are skipping the via point
+ for (auto const & point : { message.m_location.value().m_from,
+ message.m_location.value().m_at,
+ message.m_location.value().m_to })
+ {
+ if (!point)
+ continue;
+ for (auto const & location : locations)
+ {
+ auto newdist = ms::DistanceOnEarth(point.value().m_coordinates, location);
+ if (newdist < dist.get())
+ dist.get() = newdist;
+ }
+ }
+ }
+ return aDist < bDist;
+ });
+ m_isFeedQueueSortInvalid = false;
+ }
+ // retrieve the first message from the first feed, remove it from the feed
+ std::swap(message, m_feedQueue.front().front());
+ m_feedQueue.front().erase(m_feedQueue.front().begin());
+ // if the feed has no more messages, erase it (eager erase, as an empty queue is used as a condition later)
+ if (m_feedQueue.front().empty())
+ m_feedQueue.erase(m_feedQueue.begin());
+ }
- it->second.m_isLoaded = true;
- it->second.m_lastResponseTime = steady_clock::now();
- it->second.m_isWaitingForResponse = false;
- it->second.m_lastAvailability = info.GetAvailability();
+ {
+ std::lock_guard lock(m_mutex);
- if (!info.GetColoring().empty())
+ // check if message is actually newer
+ auto it = m_messageCache.find(message.m_id);
+ bool process = (it == m_messageCache.end());
+ if (!process)
+ process = (it->second.m_updateTime < message.m_updateTime);
+ if (!process)
{
- // Update cache.
- size_t constexpr kElementSize = sizeof(traffic::TrafficInfo::RoadSegmentId) + sizeof(traffic::SpeedGroup);
- size_t const dataSize = info.GetColoring().size() * kElementSize;
- m_currentCacheSizeBytes += (dataSize - it->second.m_dataSize);
- it->second.m_dataSize = dataSize;
- ShrinkCacheToAllowableSize();
+ LOG(LINFO, ("message", message.m_id, "is already in cache, skipping"));
+ return;
}
-
- UpdateState();
}
- if (!info.GetColoring().empty())
+ LOG(LINFO, (" ", message.m_id, ":", message));
+ m_traffDecoder->DecodeMessage(message);
{
- m_drapeEngine.SafeCall(&df::DrapeEngine::UpdateTraffic, static_cast(info));
+ std::lock_guard lock(m_mutex);
+
+ // store message in cache
+ m_messageCache.insert_or_assign(message.m_id, message);
- // Update traffic colors for routing.
- m_observer.OnTrafficInfoAdded(std::move(info));
+ for (auto & replaced : message.m_replaces)
+ {
+ auto it = m_messageCache.find(replaced);
+ if (it != m_messageCache.cend())
+ m_messageCache.erase(it);
+ }
}
+ /*
+ * TODO detect if we can do a quick update:
+ * - new message which does not replace any existing message
+ * - coloring “wins” over replaced message:
+ * - contains all the segments of the previous message (always true when location is the same)
+ * - speed groups are the same or lower as in previous message (always true when all members of
+ * traffic impact are unchanged or have worsened) – for this purpose, closure is considered
+ * lower than any other speed group
+ * In this case, run:
+ * traffxml::MergeMultiMwmColoring(message.m_decoded, m_allMwmColoring);
+ * Otherwise, set a flag indicating we need to process coloring in full.
+ */
}
-void TrafficManager::UniteActiveMwms(std::set & activeMwms) const
+void TrafficManager::ThreadRoutine()
{
- activeMwms.insert(m_activeDrapeMwms.cbegin(), m_activeDrapeMwms.cend());
- activeMwms.insert(m_activeRoutingMwms.cbegin(), m_activeRoutingMwms.cend());
+ // initially, treat last purge and drape/observer update as having just happened
+ auto lastPurged = steady_clock::now();
+ m_lastDrapeUpdate = steady_clock::now();
+ m_lastObserverUpdate = steady_clock::now();
+
+ while (WaitForRequest())
+ {
+ if (!IsEnabled() || IsPausedAndNotRouting())
+ continue;
+
+ /*
+ * Whether to call OnTrafficDataUpdate() at the end of the current round.
+ * The logic may fail to catch cases in which the first message in queue replaces another
+ * message without changing coloring. This would usually occur in a larger feed, where other
+ * messages would likely require an announcement, making this a minor issue. A single round
+ * (after a timeout) with no messages expired and an empty queue would not trigger an update.
+ */
+ bool hasUpdates = false;
+
+ if (!IsTestMode())
+ {
+ if (steady_clock::now() - lastPurged >= kPurgeInterval)
+ {
+ lastPurged = steady_clock::now();
+ hasUpdates |= PurgeExpiredMessagesImpl();
+ }
+
+ LOG(LINFO, ("active MWMs changed:", m_activeMwmsChanged, ", poll needed:", m_isPollNeeded));
+
+ // this is a no-op if active MWMs have not changed
+ SubscribeOrChangeSubscription();
+
+ /*
+ * Poll sources if needed.
+ * m_isPollNeeded may be set by WaitForRequest() and set/unset by SubscribeOrChangeSubscription().
+ */
+ if (m_isPollNeeded)
+ {
+ m_lastResponseTime = steady_clock::now();
+ m_isPollNeeded = false;
+ Poll();
+ }
+ }
+ LOG(LINFO, (m_feedQueue.size(), "feed(s) in queue"));
+
+ // consolidate feed queue (remove older messages in favor of newer ones)
+ ConsolidateFeedQueue();
+ hasUpdates |= !m_feedQueue.empty();
+
+ // decode one message and add it to the cache
+ DecodeFirstMessage();
+
+ // set new coloring for MWMs
+ // `m_mutex` is obtained inside the method, no need to do it here
+ if (hasUpdates)
+ OnTrafficDataUpdate();
+ }
+ Unsubscribe();
}
-void TrafficManager::ShrinkCacheToAllowableSize()
+bool TrafficManager::WaitForRequest()
{
- // Calculating number of different active mwms.
- std::set activeMwms;
- UniteActiveMwms(activeMwms);
- size_t const numActiveMwms = activeMwms.size();
+ std::unique_lock lock(m_mutex);
- if (m_currentCacheSizeBytes > m_maxCacheSizeBytes && m_mwmCache.size() > numActiveMwms)
+ /*
+ * if we got terminated, return false immediately
+ * (don’t wait until sleep, we might not get much sleep if we’re busy processing a long feed)
+ */
+ if (!m_isRunning)
+ return false;
+
+ if (IsEnabled() && !IsPausedAndNotRouting())
{
- std::multimap, MwmSet::MwmId> seenTimings;
- for (auto const & mwmInfo : m_mwmCache)
- seenTimings.insert(std::make_pair(mwmInfo.second.m_lastActiveTime, mwmInfo.first));
+ // if we have feeds in the queue, return immediately
+ if (!m_feedQueue.empty())
+ {
+ LOG(LINFO, ("feed queue not empty, returning immediately"));
+ return true;
+ }
- auto itSeen = seenTimings.begin();
- while (m_currentCacheSizeBytes > m_maxCacheSizeBytes && m_mwmCache.size() > numActiveMwms)
+ if (!IsTestMode())
{
- ClearCache(itSeen->second);
- ++itSeen;
+ // if update interval has elapsed, return immediately
+ auto const currentTime = steady_clock::now();
+ auto const passedSeconds = currentTime - m_lastResponseTime;
+ if (passedSeconds >= kUpdateInterval)
+ {
+ LOG(LINFO, ("last response was", passedSeconds, "ago, returning immediately"));
+ m_isPollNeeded = true;
+ return true;
+ }
}
}
+
+ LOG(LINFO, ("nothing to do for now, waiting for timeout or notification"));
+ bool const timeout = !m_condition.wait_for(lock, kUpdateInterval, [this]
+ {
+ // return false to continue waiting, true for any condition we want to process immediately
+ // return immediately if we got terminated
+ if (!m_isRunning)
+ return true;
+ // otherwise continue waiting if we are paused or disabled
+ if (!IsEnabled() || m_isPaused)
+ return false;
+ return (m_activeMwmsChanged && !IsTestMode()) || !m_feedQueue.empty();
+ });
+
+ // check again if we got terminated while waiting (or woken up because we got terminated)
+ if (!m_isRunning)
+ return false;
+
+ // this works as long as wait timeout is at least equal to the poll interval
+ if (IsEnabled() && !m_isPaused)
+ m_isPollNeeded |= timeout;
+
+ LOG(LINFO, ("timeout:", timeout, "active MWMs changed:", m_activeMwmsChanged, "test mode:", IsTestMode()));
+ return true;
}
-void TrafficManager::ClearCache(MwmSet::MwmId const & mwmId)
+void TrafficManager::OnTrafficDataUpdate()
{
- auto const it = m_mwmCache.find(mwmId);
- if (it == m_mwmCache.end())
- return;
+ bool feedQueueEmpty = false;
- if (it->second.m_isLoaded)
{
- ASSERT_GREATER_OR_EQUAL(m_currentCacheSizeBytes, it->second.m_dataSize, ());
- m_currentCacheSizeBytes -= it->second.m_dataSize;
+ std::lock_guard lock(m_mutex);
+ feedQueueEmpty = m_feedQueue.empty();
+ }
+ // Whether to notify the Drape engine of the update.
+ bool notifyDrape = (feedQueueEmpty);
+
+ // Whether to notify the observer of the update.
+ bool notifyObserver = (feedQueueEmpty);
- m_drapeEngine.SafeCall(&df::DrapeEngine::ClearTrafficCache, mwmId);
+ // Whether to update the cache file.
+ bool updateStorage = (feedQueueEmpty);
- GetPlatform().RunTask(Platform::Thread::Gui, [this, mwmId]() { m_observer.OnTrafficInfoRemoved(mwmId); });
+ if (!feedQueueEmpty)
+ {
+ auto const currentTime = steady_clock::now();
+ auto const drapeAge = currentTime - m_lastDrapeUpdate;
+ auto const storageAge = currentTime - m_lastStorageUpdate;
+ notifyDrape = (drapeAge >= kDrapeUpdateInterval);
+ updateStorage = (storageAge >= kStorageUpdateInterval);
+ if (!IsObserverInhibited())
+ {
+ /*
+ * To avoid resetting the route over and over again while building, inhibit periodic updates
+ * to the router while a route is being built. Periodic updates will resume once the route is
+ * fully built. During route calculation, traffic updates are sent only if the queue is empty.
+ * TODO test this with TMC, where messages arrive one by one, or in short bursts, and the
+ * queue may run empty multiple times while a route is being calculated.
+ */
+ auto const observerAge = currentTime - m_lastObserverUpdate;
+ notifyObserver = (observerAge >= kObserverUpdateInterval);
+ }
}
- m_mwmCache.erase(it);
- m_trafficETags.erase(mwmId);
- m_activeDrapeMwms.erase(mwmId);
- m_activeRoutingMwms.erase(mwmId);
- m_lastDrapeMwmsByRect.clear();
- m_lastRoutingMwmsByRect.clear();
-}
-bool TrafficManager::IsEnabled() const
-{
- return m_state != TrafficState::Disabled;
-}
+ if (!m_storage || IsTestMode())
+ updateStorage = false;
-bool TrafficManager::IsInvalidState() const
-{
- return m_state == TrafficState::NetworkError;
-}
+ if (updateStorage)
+ {
+ std::lock_guard lock(m_mutex);
-void TrafficManager::UpdateState()
-{
- if (!IsEnabled() || IsInvalidState())
- return;
+ pugi::xml_document document;
- auto const currentTime = steady_clock::now();
- auto maxPassedTime = steady_clock::duration::zero();
+ traffxml::GenerateTraff(m_messageCache, document);
+ if (!m_storage->Save(document))
+ LOG(LWARNING, ("Storing message cache to file failed."));
- bool waiting = false;
- bool networkError = false;
- bool expiredApp = false;
- bool expiredData = false;
- bool noData = false;
+ m_lastStorageUpdate = steady_clock::now();
+ }
- for (MwmSet::MwmId const & mwmId : m_activeDrapeMwms)
+ if (m_trafficUpdateCallbackFn)
+ m_trafficUpdateCallbackFn.value()(feedQueueEmpty);
+
+ if (!notifyDrape && !notifyObserver)
+ return;
+
+ LOG(LINFO, ("Announcing traffic update, notifyDrape:", notifyDrape, "notifyObserver:", notifyObserver));
+
+ /*
+ * TODO introduce a flag to indicate we need to fully reprocess coloring, skip if it is false.
+ * The flag would get set when messages get deleted (including any clear/purge operations),
+ * or when a new message is added without indicating a simplified update in `DecodeFirstMessage()`.
+ * When we reprocess coloring in full (the block below), reset this flag.
+ */
{
- auto it = m_mwmCache.find(mwmId);
- ASSERT(it != m_mwmCache.end(), ());
+ std::lock_guard lock(m_mutex);
+
+ m_allMwmColoring.clear();
+ for (const auto & [id, message] : m_messageCache)
+ traffxml::MergeMultiMwmColoring(message.m_decoded, m_allMwmColoring);
+ }
+
+ /*
+ * Much of this code is copied and pasted together from old MWM code, with some minor adaptations:
+ *
+ * ForEachMwm (not the body) is from RequestTrafficData() (now RequestTrafficSubscription()),
+ * modification: cycle over all MWMs (active or not).
+ * Handling dead MWMs and traffic cache lookup is original code.
+ * TrafficInfo construction is taken fron ThreadRoutine(), with modifications (different constructor).
+ * The remainder of the loop is from OnTrafficDataResponse(traffic::TrafficInfo &&), with some modifications
+ * (removed CacheEntry logic; deciding whether to notify a component and managing timestamps is original code).
+ * Existing coloring deletion (if there is no new coloring) is original code.
+ */
+ ForEachMwm([this, notifyDrape, notifyObserver](std::shared_ptr info) {
+ std::lock_guard lock(m_mutex);
+
+ if (info->GetCountryName().starts_with(WORLD_FILE_NAME))
+ return;
- if (it->second.m_isWaitingForResponse)
+ MwmSet::MwmId const mwmId(info);
+
+ auto tcit = m_allMwmColoring.find(mwmId);
+ if (!mwmId.IsAlive())
{
- waiting = true;
+ // MWM was deleted or replaced during decoding
+ if (tcit != m_allMwmColoring.end())
+ m_allMwmColoring.erase(tcit);
+ return;
}
- else
+ if (tcit != m_allMwmColoring.end())
{
- expiredApp |= it->second.m_lastAvailability == traffic::TrafficInfo::Availability::ExpiredApp;
- expiredData |= it->second.m_lastAvailability == traffic::TrafficInfo::Availability::ExpiredData;
- noData |= it->second.m_lastAvailability == traffic::TrafficInfo::Availability::NoData;
+ traffic::TrafficInfo::Coloring coloring = tcit->second;
+ LOG(LINFO, ("Setting new coloring for", mwmId, "with", coloring.size(), "entries"));
+ traffic::TrafficInfo info(mwmId, std::move(coloring));
- if (it->second.m_isLoaded)
+ if (notifyDrape)
{
- auto const timeSinceLastResponse = currentTime - it->second.m_lastResponseTime;
- if (timeSinceLastResponse > maxPassedTime)
- maxPassedTime = timeSinceLastResponse;
+ /*
+ * TODO calling ClearTrafficCache before UpdateTraffic is a workaround for a bug in the
+ * Drape engine: some segments found in the old coloring but not in the new one may get
+ * left behind. This was not a problem for MapsWithMe as the set of segments never
+ * changed, but is an issue wherever the segment set is dynamic. Workaround is to clear
+ * before sending an update. Ultimately, the processing logic for UpdateTraffic needs to
+ * be fixed, but the code is hard to read (involves multiple messages getting thrown back
+ * and forth between threads).
+ */
+ m_drapeEngine.SafeCall(&df::DrapeEngine::ClearTrafficCache,
+ static_cast(mwmId));
+ m_drapeEngine.SafeCall(&df::DrapeEngine::UpdateTraffic,
+ static_cast(info));
+ m_lastDrapeUpdate = steady_clock::now();
}
- else if (it->second.m_retriesCount >= kMaxRetriesCount)
+
+ if (notifyObserver)
{
- networkError = true;
+ // Update traffic colors for routing.
+ m_routingSession.OnTrafficInfoAdded(std::move(info));
+ m_lastObserverUpdate = steady_clock::now();
}
}
+ else
+ {
+ if (notifyDrape)
+ {
+ m_drapeEngine.SafeCall(&df::DrapeEngine::ClearTrafficCache,
+ static_cast(mwmId));
+ m_lastDrapeUpdate = steady_clock::now();
+ }
+
+ if (notifyObserver)
+ {
+ // Update traffic colors for routing.
+ m_routingSession.OnTrafficInfoRemoved(mwmId);
+ m_lastObserverUpdate = steady_clock::now();
+ }
+ }
+ });
+}
+
+void TrafficManager::GetActiveMwms(std::set & activeMwms)
+{
+ std::lock_guard lock(m_mutex);
+ UniteActiveMwms(activeMwms);
+}
+
+void TrafficManager::UniteActiveMwms(std::set & activeMwms) const
+{
+ activeMwms.insert(m_activeDrapeMwms.cbegin(), m_activeDrapeMwms.cend());
+ activeMwms.insert(m_activePositionMwms.cbegin(), m_activePositionMwms.cend());
+ activeMwms.insert(m_activeRoutingMwms.cbegin(), m_activeRoutingMwms.cend());
+}
+
+bool TrafficManager::IsEnabled() const
+{
+ return m_state != TrafficState::Disabled;
+}
+
+void TrafficManager::SetHttpTraffSource(bool enabled, std::string url)
+{
+ if (IsTestMode())
+ return;
+
+ {
+ std::lock_guard lock(m_trafficSourceMutex);
+
+ for (auto it = m_trafficSources.begin(); it != m_trafficSources.end(); )
+ if (traffxml::HttpTraffSource* httpSource = dynamic_cast(it->get()))
+ {
+ httpSource->Close();
+ m_trafficSources.erase(it);
+ }
+ else
+ ++it;
}
+ if (enabled)
+ traffxml::HttpTraffSource::Create(*this, url);
+}
- if (networkError || maxPassedTime >= kNetworkErrorTimeout)
- ChangeState(TrafficState::NetworkError);
- else if (waiting)
- ChangeState(TrafficState::WaitingData);
- else if (expiredApp)
- ChangeState(TrafficState::ExpiredApp);
- else if (expiredData)
- ChangeState(TrafficState::ExpiredData);
- else if (noData)
- ChangeState(TrafficState::NoData);
- else if (maxPassedTime >= kOutdatedDataTimeout)
- ChangeState(TrafficState::Outdated);
- else
- ChangeState(TrafficState::Enabled);
+void TrafficManager::RemoveTraffSourceIf(const std::function &pred)
+{
+ std::lock_guard lock(m_trafficSourceMutex);
+
+ for (auto it = m_trafficSources.begin(); it != m_trafficSources.end(); )
+ if (pred(it->get()))
+ m_trafficSources.erase(it);
+ else
+ ++it;
+}
+
+bool TrafficManager::IsInvalidState() const
+{
+ return m_state == TrafficState::NetworkError;
}
void TrafficManager::ChangeState(TrafficState newState)
@@ -540,7 +1113,7 @@ void TrafficManager::Resume()
return;
m_isPaused = false;
- Invalidate();
+ RecalculateSubscription(false);
}
void TrafficManager::SetSimplifiedColorScheme(bool simplified)
@@ -549,6 +1122,16 @@ void TrafficManager::SetSimplifiedColorScheme(bool simplified)
m_drapeEngine.SafeCall(&df::DrapeEngine::SetSimplifiedTrafficColors, simplified);
}
+void TrafficManager::SetTestMode()
+{
+ if (!m_canSetMode)
+ {
+ LOG(LWARNING, ("Mode cannot be set once the traffic manager has been enabled"));
+ return;
+ }
+ m_mode = Mode::Test;
+}
+
std::string DebugPrint(TrafficManager::TrafficState state)
{
switch (state)
diff --git a/libs/map/traffic_manager.hpp b/libs/map/traffic_manager.hpp
index e015b3dd1..31ce2a4a2 100644
--- a/libs/map/traffic_manager.hpp
+++ b/libs/map/traffic_manager.hpp
@@ -9,6 +9,15 @@
#include "indexer/mwm_set.hpp"
+#include "routing/routing_session.hpp"
+
+#include "storage/country_info_getter.hpp"
+
+#include "traffxml/traff_decoder.hpp"
+#include "traffxml/traff_model.hpp"
+#include "traffxml/traff_source.hpp"
+#include "traffxml/traff_storage.hpp"
+
#include "geometry/point2d.hpp"
#include "geometry/polyline2d.hpp"
#include "geometry/screenbase.hpp"
@@ -28,21 +37,68 @@
#include
#include
-class TrafficManager final
+class TrafficManager final : public traffxml::TraffSourceManager
{
public:
+ using CountryInfoGetterFn = std::function;
+ using CountryParentNameGetterFn = std::function;
+ using TrafficUpdateCallbackFn = std::function;
+
+ /**
+ * @brief Global state of traffic information.
+ */
+ /*
+ * TODO clean out obsolete states.
+ * Only `Disabled` and `Enabled` are currently used, but some might be reactivated in the future
+ * and platforms (android/iphone) still evaluate all states.
+ * `ExpiredData` is definitely obsolete, as traffic data is no longer dependent on a particular
+ * map version, but still evaluated by android/iphone code.
+ */
enum class TrafficState
{
+ /** Traffic is disabled, no traffic data will be retrieved or considered for routing. */
Disabled,
+ /** Traffic is enabled and working normally (the first request may not have been scheduled yet). */
Enabled,
+ /** At least one request is currently pending. */
WaitingData,
+ /** At least one MWM has stale traffic data. */
Outdated,
+ /** Traffic data for at least one MWM was invalid or not found on the server. */
NoData,
+ /** At least one request failed or timed out. */
NetworkError,
+ /** Traffic data could not be retrieved because the map data is outdated. */
ExpiredData,
+ /** Traffic data could not be retrieved because the app version is outdated. */
ExpiredApp
};
+ /**
+ * @brief The mode for the traffic manager.
+ *
+ * Future versions may introduce further test modes. Therefore, always use `TrafficManager::IsTestMode()`
+ * to verify if the traffic manager is running in test mode.
+ */
+ enum class Mode
+ {
+ /**
+ * Traffic manager mode for normal operation.
+ *
+ * This is the default mode unless something else is explicitly set.
+ */
+ Normal,
+ /**
+ * Test mode.
+ *
+ * This mode will prevent the traffic manager from automatically subscribing to sources and
+ * polling them. It will still receive and process push feeds.
+ *
+ * Future versions may introduce further behavior changes, and/or introduce more test modes.
+ */
+ Test
+ };
+
struct MyPosition
{
m2::PointD m_position = m2::PointD(0.0, 0.0);
@@ -55,25 +111,122 @@ class TrafficManager final
using TrafficStateChangedFn = std::function;
using GetMwmsByRectFn = std::function(m2::RectD const &)>;
- TrafficManager(GetMwmsByRectFn const & getMwmsByRectFn, size_t maxCacheSizeBytes,
- traffic::TrafficObserver & observer);
+ TrafficManager(DataSource & dataSource,
+ CountryInfoGetterFn countryInfoGetter,
+ CountryParentNameGetterFn const & countryParentNameGetter,
+ GetMwmsByRectFn const & getMwmsByRectFn, size_t maxCacheSizeBytes,
+ routing::RoutingSession & routingSession);
~TrafficManager();
void Teardown();
+ /**
+ * @brief Returns a copy of the cache of all currently active TraFF messages.
+ *
+ * For testing purposes.
+ *
+ * Keys are message IDs, values are messages.
+ *
+ * This method is safe to call from any thread.
+ */
+ std::map GetMessageCache();
+
TrafficState GetState() const;
void SetStateListener(TrafficStateChangedFn const & onStateChangedFn);
void SetDrapeEngine(ref_ptr engine);
+ /**
+ * @brief Sets the version of the MWM used locally.
+ */
void SetCurrentDataVersion(int64_t dataVersion);
+ /**
+ * @brief Enables or disables the traffic manager.
+ *
+ * This sets the internal state and notifies the drape engine.
+ *
+ * Upon creation, the traffic manager is disabled. MWMs must be loaded before first enabling the
+ * traffic manager.
+ *
+ * While disabled, the traffic manager will not update its subscription area (upon being enabled
+ * again, it will do so if necessary). It will not poll any sources or process any messages. Feeds
+ * added via `ReceiveFeed()` will be added to the queue but will not be processed until the
+ * traffic manager is re-enabled.
+ *
+ * Calling this function with `enabled` identical to the current state is a no-op.
+ *
+ * @todo Currently, all MWMs must be loaded before calling `SetEnabled()`, as MWMs loaded after
+ * that will not get picked up. We need to extend `TrafficManager` to react to MWMs being added
+ * (and removed) – note that this affects the `DataSource`, not the set of active MWMs.
+ * See `Framework::OnMapDeregistered()` implementation for the opposite case (MWM deregistered).
+ *
+ * @param enabled True to enable, false to disable
+ */
void SetEnabled(bool enabled);
+
+ /**
+ * @brief Whether the traffic manager is enabled.
+ *
+ * @return True if enabled, false if not
+ */
bool IsEnabled() const;
+ /**
+ * @brief Sets the enabled state and URL for the `HttpTraffSource`.
+ *
+ * If the traffic manager is in test mode, this function is a no-op.
+ *
+ * Otherwise this function is expected to be called only if the enabled state and/or URL have
+ * actually changed. Setting both to the current state will remove the current source and create
+ * a new one with identical settings.
+ *
+ * This function currently assumes that there is never more than one `HttpTraffSource` configured
+ * at the same time.
+ *
+ * @param enabled Whether the HTTP TraFF source is enabled.
+ * @param url The URL for the TraFF API.
+ */
+ void SetHttpTraffSource(bool enabled, std::string url);
+
+ /**
+ * @brief Removes all `TraffSource` instances which satisfy a predicate.
+ *
+ * This method iterates over all currently configured `TraffSource` instances and calls the
+ * caller-suppplied predicate function `pred` on each of them. If `pred` returns true, the source
+ * is removed, else it is kept.
+ *
+ * @todo For now, `pred` deliberately takes a non-const argument so we can do cleanup inside
+ * `pred`. If we manage to move any such cleanup into the destructor of the `TraffSource` subclass
+ * and get rid of any `Close()` methods in subclasses (which is preferable for other reasons as
+ * well), the argument can be made const.
+ *
+ * @param pred The predicate function, see description.
+ */
+ void RemoveTraffSourceIf(const std::function& pred);
+
+ /**
+ * @brief Starts the traffic manager.
+ *
+ */
+ void Start();
+
void UpdateViewport(ScreenBase const & screen);
void UpdateMyPosition(MyPosition const & myPosition);
- void Invalidate();
+ /**
+ * @brief Invalidates traffic information for the specified MWM.
+ *
+ * Invalidation of traffic data is always per MWM and affects locations which refer to any version
+ * of this MWM, or whose enclosing rectangle overlaps with that of the MWM. The decoded segments
+ * for these locations are discarded and decoded again, ensuring they are based on the new MWM.
+ * The TraFF messages themselves remain unchanged.
+ *
+ * This method must either be called from a lambda function passed to `RunSynchronized()`,
+ * or the caller must explicitly lock the private `m_mutex` prior to calling this method.
+ *
+ * @param mwmId The newly addded MWM.
+ */
+ void Invalidate(MwmSet::MwmId const & mwmId);
void OnDestroySurface();
void OnRecoverSurface();
@@ -85,57 +238,295 @@ class TrafficManager final
void SetSimplifiedColorScheme(bool simplified);
bool HasSimplifiedColorScheme() const { return m_hasSimplifiedColorScheme; }
-private:
- struct CacheEntry
- {
- CacheEntry();
- explicit CacheEntry(std::chrono::time_point const & requestTime);
-
- bool m_isLoaded;
- size_t m_dataSize;
-
- std::chrono::time_point m_lastActiveTime;
- std::chrono::time_point m_lastRequestTime;
- std::chrono::time_point m_lastResponseTime;
+ /**
+ * @brief Whether the traffic manager is operating in test mode.
+ */
+ bool IsTestMode() { return m_mode != Mode::Normal; }
+
+ /**
+ * @brief Switches the traffic manager into test mode.
+ *
+ * The mode can only be set before the traffic manager is first enabled. After that, this method
+ * will log a warning but otherwise do nothing.
+ *
+ * In test mode, the traffic manager will not subscribe to sources or poll them automatically.
+ * Expired messages will not get purged automatically, but `PurgeExpiredMessages()` can be called
+ * to purge expired messages once. The traffic manager will still receive and process push feeds.
+ *
+ * Future versions may introduce further behavior changes.
+ */
+ void SetTestMode();
+
+ /**
+ * @brief Processes a traffic feed.
+ *
+ * The feed may be a result of a pull operation, or received through a push operation.
+ * (Push operations are not supported by all sources.)
+ *
+ * This method is safe to call from any thread.
+ *
+ * @param feed The traffic feed.
+ */
+ virtual void ReceiveFeed(traffxml::TraffFeed feed) override;
+
+ /**
+ * @brief Registers a `TraffSource`.
+ * @param source The source.
+ */
+ virtual void RegisterSource(std::unique_ptr source) override;
+
+ /**
+ * @brief Retrieves all currently active MWMs.
+ *
+ * This method retrieves all MWMs in the viewport, within a certain distance of the current
+ * position (if there is a valid position) or part of the route (if any), and stores them in
+ * `activeMwms`.
+ *
+ * This method locks `m_mutex` and is therefore safe to call from any thread. Callers which
+ * already hold `m_mutex` can use the private `UniteActiveMwms()` method instead.
+ *
+ * @param activeMwms Retrieves the list of active MWMs.
+ */
+ virtual void GetActiveMwms(std::set & activeMwms) override;
+
+ /**
+ * @brief Purges expired messages from the cache.
+ *
+ * This method is safe to call from any thread, except for the traffic worker thread.
+ */
+ void PurgeExpiredMessages();
+
+ /**
+ * @brief Clears the traffic message cache and feed queue.
+ *
+ * This is intended for testing purposes and clears the message cache, as well as the feed queue.
+ * Subscriptions are not changed.
+ */
+ void Clear();
- int m_retriesCount;
- bool m_isWaitingForResponse;
+ /**
+ * @brief Registers a callback function which gets called on traffic updates.
+ *
+ * Intended for testing.
+ *
+ * @param fn The callback function.
+ */
+ void SetTrafficUpdateCallbackFn(TrafficUpdateCallbackFn && fn);
+
+ /**
+ * @brief Runs a function guarded by the traffic manager mutex.
+ *
+ * This locks `m_mutex`, then runs `f` and releases the mutex.
+ *
+ * @param f
+ */
+ void RunSynchronized(std::function f)
+ {
+ std::lock_guard lock(m_mutex);
+ f();
+ }
- traffic::TrafficInfo::Availability m_lastAvailability;
- };
+private:
+ /**
+ * @brief Recalculates the TraFF subscription area.
+ *
+ * The subscription area needs to be recalculated when the traffic manager goes from disabled to
+ * enabled, or when it is resumed after being paused, as the subscription area is not updated
+ * while the traffic manager is disabled or paused.
+ *
+ * If the subscription area has changed, or if `forceRenewal` is true, TraFF subscriptions are
+ * renewed by calling `SubscribeOrChangeSubscription()`.
+ *
+ * No traffic data is discarded, but sources will be polled for an update, which may turn out
+ * larger than usual if the traffic manager was in disabled/paused state for an extended period of
+ * time or the subscription area has changed.
+ *
+ * @param forceRenewal If true, renew subscriptions even if the subscription area has not changed.
+ */
+ void RecalculateSubscription(bool forceRenewal);
+
+ /**
+ * @brief Ensures every TraFF source has a subscription covering all currently active MWMs.
+ *
+ * This method cycles through all TraFF sources in `m_trafficSources` and calls
+ * `SubscribeOrChangeSubscription()` on each of them.
+ */
+ void SubscribeOrChangeSubscription();
+
+ /**
+ * @brief Unsubscribes from all traffic services we are subscribed to.
+ *
+ * This method cycles through all TraFF sources in `m_trafficSources` and calls `Unsubscribe()`
+ * on each of them.
+ */
+ void Unsubscribe();
+
+ /**
+ * @brief Restores the message cache from file storage.
+ *
+ * @note The caller must lock `m_mutex` prior to calling this function, as it makes unprotected
+ * changes to shared data structures.
+ *
+ * @note The return value indicates whether actions related to a traffic update should be taken,
+ * such as notifying the routing and drape engine. It is true if at least one message with a
+ * decoded location was read, and no messages without decoded locations. If messages without a
+ * decoded location were read, the return value is false, as the location decoding will trigger
+ * updates by itself. If errors occurred and no messages are read, the return value is also false.
+ *
+ * @return True if a traffic update needs to be sent, false if not
+ */
+ bool RestoreCache();
+
+ /**
+ * @brief Polls all traffic services for updates.
+ *
+ * This method cycles through all TraFF sources in `m_trafficSources` and calls `IsPollNeeded()`
+ * on each of them. If this method returns true, it then calls `Poll()` on the source.
+ */
+ void Poll();
+
+ /**
+ * @brief Purges expired messages from the cache.
+ *
+ * This is the internal conterpart of `PurgeExpiredMessages()`. It is safe to call from any
+ * thread. Unlike `PurgeExpiredMessages()`, it does not wake the worker thread, making it suitable
+ * for use on the worker thread.
+ *
+ * @return true if messages were purged, false if not
+ */
+ bool PurgeExpiredMessagesImpl();
+
+ /**
+ * @brief Consolidates the feed queue.
+ *
+ * If multiple feeds in the queue have the same message ID, only the message with the newest
+ * update time is kept (if two messages have the same ID and update time, the one in the feed
+ * with the higher index is kept); other messages with the same ID are discarded. Empty feeds
+ * are discarded.
+ */
+ void ConsolidateFeedQueue();
+
+ /**
+ * @brief Removes the first message from the first feed and decodes it.
+ */
+ void DecodeFirstMessage();
+
+ /**
+ * @brief Event loop for the traffic worker thread.
+ *
+ * This method runs an event loop, which blocks until woken up or a timeout equivalent to the
+ * update interval elapses. It cycles through the list of MWMs for which updates have been
+ * scheduled, triggering a network request for each and processing the result.
+ */
void ThreadRoutine();
- bool WaitForRequest(std::vector & mwms);
-
- void OnTrafficDataResponse(traffic::TrafficInfo && info);
- void OnTrafficRequestFailed(traffic::TrafficInfo && info);
- /// \brief Updates |activeMwms| and request traffic data.
- /// \param rect is a rectangle covering a new active mwm set.
- /// \note |lastMwmsByRect|/|activeMwms| may be either |m_lastDrapeMwmsByRect/|m_activeDrapeMwms|
- /// or |m_lastRoutingMwmsByRect|/|m_activeRoutingMwms|.
- /// \note |m_mutex| is locked inside the method. So the method should be called without |m_mutex|.
+ /**
+ * @brief Blocks until a request for traffic data is received or a timeout expires.
+ *
+ * This method acts as the loop condition for `ThreadRoutine()`. It blocks until woken up or the
+ * update interval expires. In the latter case, it calls `RequestTrafficData()` to insert all
+ * currently active MWMs into the list of MWMs to update; otherwise, it leaves the list as it is.
+ * In either case, it populates `mwms` with the list and returns.
+ *
+ * @return `true` during normal operation, `false` during teardown (signaling the event loop to exit).
+ */
+ bool WaitForRequest();
+
+ /**
+ * @brief Processes new traffic data.
+ *
+ * The new per-MWM colorings (preprocessed traffic information) are taken from `m_allMmColoring`.
+ * `m_allMwmColoring` is rebuilt from per-message colorings in `m_messageCache` as needed.
+ *
+ * This method is normally called from the traffic worker thread. Test tools may also call it from
+ * other threads.
+ */
+ void OnTrafficDataUpdate();
+
+ /**
+ * @brief Updates `activeMwms` and requests traffic data.
+ *
+ * The old and new list of active MWMs may refer either to those used by the rendering engine
+ * (`m_lastDrapeMwmsByRect`/`m_activeDrapeMwms`) or to those around the current position.
+ * (`m_lastPositionMwmsByRect`/`m_activePositionMwms`).
+ *
+ * The method first determines the list of MWMs overlapping with `rect`. If it is identical to
+ * `lastMwmsByRect`, the method returns immediately. Otherwise, it stores the new set in
+ * `lastMwmsByRect` and populates `activeMwms` with the elements.
+ *
+ * This method locks `m_mutex` while populating `activeMwms`. There is no need for the caller to
+ * do that.
+ *
+ * @param rect Rectangle covering the new active MWM set.
+ * @param lastMwmsByRect Set of active MWMs, see description.
+ * @param activeMwms Vector of active MWMs, see description.
+ */
void UpdateActiveMwms(m2::RectD const & rect, std::vector & lastMwmsByRect,
std::set & activeMwms);
// This is a group of methods that haven't their own synchronization inside.
- void RequestTrafficData();
- void RequestTrafficData(MwmSet::MwmId const & mwmId, bool force);
-
- void Clear();
- void ClearCache(MwmSet::MwmId const & mwmId);
- void ShrinkCacheToAllowableSize();
- void UpdateState();
void ChangeState(TrafficState newState);
bool IsInvalidState() const;
+ void OnChangeRoutingSessionState(routing::SessionState previous, routing::SessionState current);
+
+ /**
+ * @brief Retrieves all currently active MWMs.
+ *
+ * This method retrieves all MWMs in the viewport, within a certain distance of the current
+ * position (if there is a valid position) or part of the route (if any), and stores them in
+ * `activeMwms`.
+ *
+ * The caller must hold `m_mutex` prior to calling this method. `GetActiveMwms()` is available
+ * as a convenience wrapper which locks `m_mutex`, calls this method and releases it.
+ *
+ * @param activeMwms Retrieves the list of active MWMs.
+ */
void UniteActiveMwms(std::set & activeMwms) const;
+ /**
+ * @brief Pauses the traffic manager.
+ *
+ * Upon creation, the traffic manager is not paused.
+ *
+ * While the traffic manager is paused and no route is active, the traffic manager will not update
+ * its subscription area (upon resuming, it will do so if necessary). It will not poll any sources
+ * or process any messages. Feeds added via `ReceiveFeed()` will be added to the queue but will
+ * not be processed until the traffic manager is resumed.
+ *
+ * Pausing and resuming is similar in effect to disabling and enabling the traffic manager, except
+ * it does not change the external state, and an active route effectively overrides the paused
+ * state. It is intended for internal use by the framework.
+ */
void Pause();
+
+ /**
+ * @brief Resumes the traffic manager.
+ *
+ * Upon creation, the traffic manager is not paused. Resuming a traffic manager that is not paused
+ * is a no-op.
+ *
+ * Upon resume, the traffic manager will recalculate its subscription area and change its
+ * subscription if necessary. It will continue processing feeds in the queue, including those
+ * received before or while the traffic manager was paused.
+ *
+ * Pausing and resuming is similar in effect to disabling and enabling the traffic manager, except
+ * it does not change the external state, and an active route effectively overrides the paused
+ * state. It is intended for internal use by the framework.
+ */
void Resume();
+ template
+ void ForEachMwm(F && f) const
+ {
+ std::vector> allMwmInfo;
+ m_dataSource.GetMwmsInfo(allMwmInfo);
+ std::for_each(allMwmInfo.begin(), allMwmInfo.end(), std::forward(f));
+ }
+
template
void ForEachActiveMwm(F && f) const
{
@@ -144,44 +535,233 @@ class TrafficManager final
std::for_each(activeMwms.begin(), activeMwms.end(), std::forward(f));
}
+ /**
+ * @brief Whether updates to the observer are currently inhibited.
+ *
+ * Updates are inhibited while a route calculation is in progress. In this state, the observer
+ * receives traffic updates only if the queue has run empty, not if nore locations are waiting
+ * to be decoded.
+ *
+ * Inhibtiting the observer is necessary as traffic updates during route calculation will cause
+ * it to restart from scratch. Once the route has been calculated, updates will trigger a
+ * recalculation, which is much faster (seconds or less).
+ */
+ bool IsObserverInhibited() const { return (m_routingSessionState == routing::SessionState::RouteBuilding)
+ || (m_routingSessionState == routing::SessionState::RouteRebuilding); }
+
+ /**
+ * @brief Whether we are currently routing.
+ */
+ bool IsRouting() const { return m_routingSessionState != routing::SessionState::NoValidRoute; }
+
+ /**
+ * @brief Whether the traffic manager is paused and not routing.
+ *
+ * This is used to inhibit polling and message decoding.
+ */
+ bool IsPausedAndNotRouting() const { return m_isPaused && !IsRouting(); }
+
+ DataSource & m_dataSource;
+ CountryInfoGetterFn m_countryInfoGetterFn;
+ CountryParentNameGetterFn m_countryParentNameGetterFn;
GetMwmsByRectFn m_getMwmsByRectFn;
- traffic::TrafficObserver & m_observer;
+
+ /*
+ * Originally this was m_observer, of type traffic::TrafficObserver. Since routing::RoutingSession
+ * inherits from that class, and an interface to the routing session is needed in order to
+ * determine the MWMs for which we need traffic information, the type was changed and the member
+ * renamed to reflect that.
+ */
+ routing::RoutingSession & m_routingSession;
+
+ /**
+ * @brief Cached state of the routing session.
+ *
+ * `m_routingSession` methods which query the state may only be called from the GUI thread,
+ * therefore we are caching this value when we get notified of a change.
+ */
+ routing::SessionState m_routingSessionState = routing::SessionState::NoValidRoute;
df::DrapeEngineSafePtr m_drapeEngine;
std::atomic m_currentDataVersion;
// These fields have a flag of their initialization.
+ /*
+ * The lazy ones get updated only if they are not initialized, or if their new position is more
+ * than a certain distance from the previously stored one.
+ */
std::pair m_currentPosition = {MyPosition(), false};
+ std::pair m_currentPositionLazy = m_currentPosition;
std::pair m_currentModelView = {ScreenBase(), false};
+ std::pair m_currentModelViewLazy = m_currentModelView;
+
+ /**
+ * The mode in which the traffic manager is running.
+ */
+ Mode m_mode = Mode::Normal;
+
+ /**
+ * Whether the traffic manager accepts mode changes.
+ *
+ * Mode cannot be set after the traffic manager has been enabled for the first time.
+ */
+ bool m_canSetMode = true;
std::atomic m_state;
TrafficStateChangedFn m_onStateChangedFn;
bool m_hasSimplifiedColorScheme = true;
- size_t m_maxCacheSizeBytes;
- size_t m_currentCacheSizeBytes = 0;
-
- std::map m_mwmCache;
+ /**
+ * @brief The TraFF sources from which we get traffic information.
+ *
+ * Threads must lock `m_trafficSourceMutex` prior to accessing this member.
+ */
+ std::vector> m_trafficSources;
bool m_isRunning;
std::condition_variable m_condition;
+ /*
+ * To determine for which MWMs we need traffic data, we need to keep track of 3 groups of MWMs:
+ * those used by the renderer (i.e. in or just around the viewport), those within a certain area
+ * around the current position, and those used by the routing engine (only if currently routing).
+ *
+ * Routing MWMs are stored as a set.
+ *
+ * The other groups are stored twice: as a set and as a vector. The set always holds the MWMs which
+ * were last seen in use. Both get updated together when active MWMs are added or removed.
+ * However, the vector is used as a reference to detect changes. Clear() clears the vector but not
+ * the set, invalidating the set without destroying its contents.
+ *
+ * Methods which use only the set:
+ *
+ * * RequestTrafficSubscription(), exits if empty, otherwise cycles through the set.
+ * * UniteActiveMwms(), build the list of active MWMs (used by RequestTrafficSubscription()).
+ *
+ * Methods which use both, but in a different way:
+ *
+ * * UpdateActiveMwms(), uses the vector to detect changes (not for routing MWMs). If so, it
+ * updates both vector and set, but adds MWMs to the set only if they are alive.
+ */
std::vector m_lastDrapeMwmsByRect;
std::set m_activeDrapeMwms;
- std::vector m_lastRoutingMwmsByRect;
+ std::vector m_lastPositionMwmsByRect;
+ std::set m_activePositionMwms;
std::set m_activeRoutingMwms;
- // The ETag or entity tag is part of HTTP, the protocol for the World Wide Web.
- // It is one of several mechanisms that HTTP provides for web cache validation,
- // which allows a client to make conditional requests.
- std::map m_trafficETags;
+ /**
+ * @brief Whether active MWMs have changed since the last request.
+ */
+ bool m_activeMwmsChanged = false;
std::atomic m_isPaused;
- std::vector m_requestedMwms;
+ /**
+ * @brief Mutex for access to shared members.
+ *
+ * Threads which access shared members (see documentation) must lock this mutex while doing so.
+ *
+ * @note To access `m_trafficSource`, lock `m_trafficSourceMutex`, not this mutex.
+ */
std::mutex m_mutex;
+
+ /**
+ * @brief Mutex for access to `m_trafficSources`.
+ *
+ * Threads which access `m_trafficSources` must lock this mutex while doing so.
+ */
+ std::mutex m_trafficSourceMutex;
+
+ /**
+ * @brief Worker thread which fetches traffic updates.
+ */
threads::SimpleThread m_thread;
+
+ /**
+ * @brief When the last response was received.
+ */
+ std::chrono::time_point m_lastResponseTime;
+
+ /**
+ * @brief When the last update notification to the Drape engine was posted.
+ */
+ std::chrono::time_point m_lastDrapeUpdate;
+
+ /**
+ * @brief When the last update notification to the traffic observer was posted.
+ */
+ std::chrono::time_point m_lastObserverUpdate;
+
+ /**
+ * @brief When the cache file was last updated.
+ */
+ std::chrono::time_point m_lastStorageUpdate;
+
+ /**
+ * @brief Whether a poll operation is needed.
+ *
+ * Used in the worker thread to indicate we need to poll all sources. The poll operation may still
+ * be inhibited for individual sources.
+ */
+ bool m_isPollNeeded;
+
+ /**
+ * @brief Queue of feeds waiting to be processed.
+ *
+ * Threads must lock `m_mutex` before accessing `m_feedQueue`, as some platforms may receive feeds
+ * on multiple threads.
+ */
+ std::vector m_feedQueue;
+
+ /**
+ * @brief Whether the feed queue needs to be resorted.
+ *
+ * Resorting is needed when a new feed is added, or the current position or the viewport center
+ * has changed by more than a certain threshold.
+ */
+ std::atomic m_isFeedQueueSortInvalid = false;
+
+ /**
+ * @brief Cache of all currently active TraFF messages.
+ *
+ * Keys are message IDs, values are messages.
+ *
+ * Threads must lock `m_mutex` before accessing `m_messageCache`, as access can happen from
+ * multiple threads (messages are added by the worker thread, `Clear()` can be called from the UI
+ * thread).
+ */
+ std::map m_messageCache;
+
+ /**
+ * @brief The storage instance.
+ *
+ * Used to persist the TraFF message cache between sessions.
+ */
+ std::unique_ptr m_storage;
+
+ /**
+ * @brief The TraFF decoder instance.
+ *
+ * Used to decode TraFF locations into road segments on the map.
+ */
+ std::unique_ptr m_traffDecoder;
+
+ /**
+ * @brief Map between MWM IDs and their colorings.
+ *
+ * Threads must lock `m_mutex` before accessing `m_allMwmColoring`, as access can happen from
+ * multiple threads (messages are added by the worker thread, `Clear()` can be called from the UI
+ * thread).
+ */
+ std::map m_allMwmColoring;
+
+ /**
+ * @brief Callback function which gets called on traffic updates.
+ *
+ * Intended for testing.
+ */
+ std::optional m_trafficUpdateCallbackFn;
};
extern std::string DebugPrint(TrafficManager::TrafficState state);
diff --git a/libs/map/user_mark.hpp b/libs/map/user_mark.hpp
index a9ff23a9b..71fbced5d 100644
--- a/libs/map/user_mark.hpp
+++ b/libs/map/user_mark.hpp
@@ -36,20 +36,61 @@ class UserMark : public df::UserPointMark
RoadWarningFirstFerry,
};
+ /**
+ * @brief User mark types.
+ *
+ * `UserMark` subclasses are assigned a value from this enum.
+ */
enum Type : uint32_t
{
+ /**
+ * `Bookmark`
+ */
BOOKMARK, // Should always be the first one
+ /**
+ * `ApiMarkPoint`
+ */
API,
+ /**
+ * `SearchMarkPoint`
+ */
SEARCH,
+ /**
+ * `StaticMarkPoint`
+ */
STATIC,
+ /**
+ * `RouteMarkPoint`
+ */
ROUTING,
+ /**
+ * `SpeedCameraMark`
+ */
SPEED_CAM,
+ /**
+ * `RoadWarningMark`
+ */
ROAD_WARNING,
+ /**
+ * `TransitMark`
+ */
TRANSIT,
LOCAL_ADS,
+ /**
+ * `TrackInfoMark`
+ */
TRACK_INFO,
+ /**
+ * `TrackSelectionMark`
+ */
TRACK_SELECTION,
+ /**
+ * `DebugMarkPoint`
+ */
DEBUG_MARK, // Plain "DEBUG" results in a name collision.
+ /**
+ * `ColoredMarkPoint`
+ */
COLORED,
USER_MARK_TYPES_COUNT,
USER_MARK_TYPES_COUNT_MAX = 1000,
@@ -133,6 +174,9 @@ class MyPositionMarkPoint : public StaticMarkPoint
bool m_hasPosition = false;
};
+/**
+ * @brief A mark in the shape of a dot.
+ */
class DebugMarkPoint : public UserMark
{
public:
@@ -141,6 +185,9 @@ class DebugMarkPoint : public UserMark
drape_ptr GetSymbolNames() const override;
};
+/**
+ * @brief A mark in the shape of a dot, of caller-defined color and radius.
+ */
class ColoredMarkPoint : public UserMark
{
public:
diff --git a/libs/platform/local_country_file.hpp b/libs/platform/local_country_file.hpp
index 20a8495d3..3f95349c6 100644
--- a/libs/platform/local_country_file.hpp
+++ b/libs/platform/local_country_file.hpp
@@ -13,53 +13,88 @@
namespace platform
{
-// This class represents a path to disk files corresponding to some
-// country region.
-//
-// This class also wraps World.mwm and WorldCoasts.mwm
-// files from resource bundle, when they can't be found in a data
-// directory. In this exceptional case, directory will be empty and
-// SyncWithDisk()/DeleteFromDisk()/GetPath()/GetSize() will return
-// incorrect results.
-//
-// In any case, when you're going to read a file LocalCountryFile points to,
-// use platform::GetCountryReader().
+/**
+ * @brief Represents a path to disk files corresponding to some country region.
+ *
+ * This class also wraps World.mwm and WorldCoasts.mwm files from resource bundle, when they can't
+ * be found in a data directory. In this exceptional case, directory will be empty and
+ * `SyncWithDisk()`/`DeleteFromDisk()`/`GetPath()`/`GetSize()` will return incorrect results.
+ *
+ * In any case, when you're going to read a file LocalCountryFile points to, use
+ * `platform::GetCountryReader()`.
+ */
class LocalCountryFile
{
public:
LocalCountryFile();
- // Creates an instance holding a path to countryFile's in a
- // directory. Note that no disk operations are not performed until
- // SyncWithDisk() is called.
- // The directory must contain a full path to the country file.
+ /**
+ * @brief Creates an instance holding a path to countryFile's in a directory.
+ *
+ * Note that no disk operations are performed until `SyncWithDisk()` is called.
+ *
+ * @param directory full path to the country file
+ * @param countryFile
+ * @param version
+ */
LocalCountryFile(std::string directory, CountryFile countryFile, int64_t version);
- // Syncs internal state like availability of files, their sizes etc. with disk.
- // Generality speaking it's not always true. To know it for sure it's necessary to read a mwm in
- // this method but it's not implemented by performance reasons. This check is done on
- // building routes stage.
+ /**
+ * @brief Syncs internal state like availability of files, their sizes etc. with disk.
+ *
+ * Generality speaking it's not always true. To know it for sure it's necessary to read a mwm in
+ * this method but it's not implemented by performance reasons. This check is done on
+ * building routes stage.
+ */
void SyncWithDisk();
- // Removes specified file from disk if it is known for LocalCountryFile, i.e.
- // it was found by a previous SyncWithDisk() call.
+ /**
+ * @brief Deletes a file from disk.
+ *
+ * Removes the specified file from disk for `LocalCountryFile`, if it is known, i.e. it was found
+ * by a previous SyncWithDisk() call.
+ * @param type
+ */
void DeleteFromDisk(MapFileType type) const;
- // Returns path to a file.
- // Return value may be empty until SyncWithDisk() is called.
+ /**
+ * @brief Returns the path to a file.
+ *
+ * Return value may be empty until SyncWithDisk() is called.
+ *
+ * @param type
+ * @return
+ */
std::string GetPath(MapFileType type) const;
std::string GetFileName(MapFileType type) const;
- // Returns size of a file.
- // Return value may be zero until SyncWithDisk() is called.
+ /**
+ * @brief Returns the size of a file.
+ *
+ * Return value may be zero until SyncWithDisk() is called.
+ *
+ * @param type
+ * @return
+ */
uint64_t GetSize(MapFileType type) const;
- // Returns true when some files are found during SyncWithDisk.
- // Return value is false until SyncWithDisk() is called.
+ /**
+ * @brief Returns true when files are found during `SyncWithDisk()`.
+ *
+ * Return value is false until `SyncWithDisk()` is called.
+ *
+ * @return
+ */
bool HasFiles() const;
- // Checks whether files specified in filesMask are on disk.
- // Return value will be false until SyncWithDisk() is called.
+ /**
+ * @brief Checks whether files specified in filesMask are on disk.
+ *
+ * Return value will be false until SyncWithDisk() is called.
+ *
+ * @param type
+ * @return
+ */
bool OnDisk(MapFileType type) const;
bool IsInBundle() const { return m_directory.empty(); }
@@ -74,8 +109,17 @@ class LocalCountryFile
bool ValidateIntegrity() const;
- // Creates LocalCountryFile for test purposes, for a country region
- // with countryFileName (without any extensions). Automatically performs sync with disk.
+ //
+ /**
+ * @brief Creates a `LocalCountryFile` for test purposes.
+ *
+ * Creates a `LocalCountryFile` for test purposes, for a country region with `countryFileName`.
+ * Automatically performs sync with disk.
+ *
+ * @param countryFileName The filename, without any extension.
+ * @param version The data version.
+ * @return
+ */
static LocalCountryFile MakeForTesting(std::string countryFileName, int64_t version = 0);
// Used in generator only to simplify getting instance from path.
diff --git a/libs/platform/mwm_version.hpp b/libs/platform/mwm_version.hpp
index f58944f24..dc69ce80c 100644
--- a/libs/platform/mwm_version.hpp
+++ b/libs/platform/mwm_version.hpp
@@ -13,6 +13,20 @@ DECLARE_EXCEPTION(CorruptedMwmFile, RootException);
namespace version
{
+/**
+ * @brief The MWM format version.
+ *
+ * This is the global versioning for the MWM format. Some structures in the MWM file may have their
+ * own versioning in addition to the MWM format version.
+ *
+ * When a new version is introduced, add a new member to this `enum` and bump `lastFormat` to the
+ * new version.
+ *
+ * @todo Document where checks for the format version happen, and criteria for adding or not adding
+ * a new version when changes to the format are made. See #4414.
+ *
+ * Versions up to v11 were introduced by Maps.me; v10 and earlier were deprecated by OrganicMaps.
+ */
enum class Format
{
unknownFormat = -1,
@@ -30,7 +44,8 @@ enum class Format
// header, sdx section with header, dat section renamed to features, features section with
// header).
v11, // September 2020 (compressed string storage for metadata).
- lastFormat = v11
+ v12, // June 2026 (additional vehicle model DecoderModel, construction types allowed in routing section).
+ lastFormat = v12
};
std::string DebugPrint(Format f);
diff --git a/libs/routing/absent_regions_finder.cpp b/libs/routing/absent_regions_finder.cpp
index d3267d6df..25d335cd8 100644
--- a/libs/routing/absent_regions_finder.cpp
+++ b/libs/routing/absent_regions_finder.cpp
@@ -17,6 +17,9 @@ AbsentRegionsFinder::AbsentRegionsFinder(CountryFileGetterFn const & countryFile
void AbsentRegionsFinder::GenerateAbsentRegions(Checkpoints const & checkpoints, RouterDelegate const & delegate)
{
+ std::lock_guard lock(m_mutex);
+ m_regions.clear();
+
if (m_routerThread)
{
m_routerThread->Cancel();
@@ -48,18 +51,22 @@ void AbsentRegionsFinder::GetAbsentRegions(std::set & regions)
void AbsentRegionsFinder::GetAllRegions(std::set & countries)
{
- countries.clear();
-
- if (!m_routerThread)
- return;
+ std::lock_guard lock(m_mutex);
+ // Note: if called from `RoutingSession` callback, m_state will still have its pre-update value.
+ if (m_routerThread)
+ {
+ m_routerThread->Join();
- m_routerThread->Join();
+ for (auto const & mwmName : m_routerThread->GetRoutineAs()->GetMwmNames())
+ {
+ if (!mwmName.empty())
+ m_regions.emplace(mwmName);
+ }
- for (auto const & mwmName : m_routerThread->GetRoutineAs()->GetMwmNames())
- if (!mwmName.empty())
- countries.emplace(mwmName);
+ m_routerThread.reset();
+ }
- m_routerThread.reset();
+ countries = m_regions;
}
bool AbsentRegionsFinder::AreCheckpointsInSameMwm(Checkpoints const & checkpoints) const
diff --git a/libs/routing/absent_regions_finder.hpp b/libs/routing/absent_regions_finder.hpp
index 4a2d8734f..6f5d42611 100644
--- a/libs/routing/absent_regions_finder.hpp
+++ b/libs/routing/absent_regions_finder.hpp
@@ -14,19 +14,45 @@ namespace routing
{
using LocalFileCheckerFn = std::function;
-// Encapsulates generation of mwm names of absent regions needed for building the route between
-// |checkpoints|. For this purpose the new thread is used.
+/**
+ * @brief Generates a list of MWMs needed to build a route.
+ *
+ * The `AbsentRegionsFinder` class encapsulates generation of MWM names of absent regions needed
+ * for building the route between `checkpoints`. For this purpose a separate worker thread is used.
+ */
class AbsentRegionsFinder
{
public:
AbsentRegionsFinder(CountryFileGetterFn const & countryFileGetter, LocalFileCheckerFn const & localFileChecker,
std::shared_ptr numMwmIds, DataSource & dataSource);
- // Creates new thread |m_routerThread| and starts routing in it.
+ /**
+ * @brief Creates new thread `m_routerThread` and starts routing in it.
+ * @param checkpoints The checkpoints of the route (start, optional intermediate points, destination)
+ * @param delegate
+ */
void GenerateAbsentRegions(Checkpoints const & checkpoints, RouterDelegate const & delegate);
- // Waits for the routing thread |m_routerThread| to finish and returns results from it.
+
+ /**
+ * @brief Retrieves the MWMs needed to build the route.
+ *
+ * When called for the first time after `GenerateAbsentRegions()`, this method waits for the
+ * routing thread `m_routerThread` to finish and returns results from it. Results are cached and
+ * subsequent calls are served from the cache.
+ *
+ * @param countries Receives the list of MWM names.
+ */
void GetAllRegions(std::set & countries);
- // Waits for the results from GetAllRegions() and returns only regions absent on the device.
+
+ /**
+ * @brief Retrieves the missing MWMs needed to build the route.
+ *
+ * This calls `GetAllRegions()` and strips from the result all regions already present on the
+ * device, leaving only the missing ones. If the call to `GetAllRegions()` is the first one after
+ * calling `GenerateAbsentRegions()`, this involves waiting for the router thread to finish.
+ *
+ * @param absentCountries Receives the list of missing MWM names.
+ */
void GetAbsentRegions(std::set & absentCountries);
private:
@@ -39,5 +65,20 @@ class AbsentRegionsFinder
DataSource & m_dataSource;
std::unique_ptr m_routerThread;
+
+ /**
+ * @brief Mutex for access to member variables.
+ *
+ * Methods which access any of the non-`const` class member variables must lock this mutex while
+ * doing so.
+ */
+ std::mutex m_mutex;
+
+ /**
+ * @brief Regions required for building the last route.
+ *
+ * This member is cleared by `GenerateAbsentRegions()` and populated by `GetAllRegions()`.
+ */
+ std::set m_regions;
};
} // namespace routing
diff --git a/libs/routing/async_router.cpp b/libs/routing/async_router.cpp
index cbd9036ea..b129a3d72 100644
--- a/libs/routing/async_router.cpp
+++ b/libs/routing/async_router.cpp
@@ -80,6 +80,13 @@ bool AsyncRouter::FindClosestProjectionToRoad(m2::PointD const & point, m2::Poin
return m_router->FindClosestProjectionToRoad(point, direction, radius, proj);
}
+void AsyncRouter::GetAllRegions(std::set & countries)
+{
+ if (!m_absentRegionsFinder)
+ return;
+ m_absentRegionsFinder->GetAllRegions(countries);
+}
+
void AsyncRouter::RouterDelegateProxy::OnProgress(float progress)
{
ProgressCallback onProgress = nullptr;
diff --git a/libs/routing/async_router.hpp b/libs/routing/async_router.hpp
index 20209914d..81217bbcd 100644
--- a/libs/routing/async_router.hpp
+++ b/libs/routing/async_router.hpp
@@ -23,11 +23,15 @@
namespace routing
{
-/// Dispatches a route calculation on a worker thread
+/**
+ * @brief The AsyncRouter class is a wrapper class to run routing routines in a different thread.
+ *
+ * It encapsulates an `IRouter` (or subclass) instance, set with `SetRouter()`, and runs it in a
+ * separate worker thread to calculate the route.
+ */
class AsyncRouter final
{
public:
- /// AsyncRouter is a wrapper class to run routing routines in the different thread
AsyncRouter(PointCheckCallback const & pointCheckCallback);
~AsyncRouter();
@@ -59,6 +63,15 @@ class AsyncRouter final
bool FindClosestProjectionToRoad(m2::PointD const & point, m2::PointD const & direction, double radius,
EdgeProj & proj);
+ /**
+ * @brief Retrieves the MWMs needed to build the route.
+ *
+ * Waits for the routing thread to finish and returns the list of MWM names from it.
+ *
+ * @param countries Receives the list of MWM names.
+ */
+ void GetAllRegions(std::set & countries);
+
private:
/// Worker thread function
void ThreadFunc();
diff --git a/libs/routing/directions_engine.cpp b/libs/routing/directions_engine.cpp
index 321654452..ac924524f 100644
--- a/libs/routing/directions_engine.cpp
+++ b/libs/routing/directions_engine.cpp
@@ -85,6 +85,7 @@ void DirectionsEngine::LoadPathAttributes(FeatureID const & featureId, LoadedPat
pathSegment.m_isOneWay = m_onewayChecker(types);
pathSegment.m_roadNameInfo.m_isLink = pathSegment.m_isLink;
+ pathSegment.m_roadNameInfo.m_onRoundabout = pathSegment.m_onRoundabout;
pathSegment.m_roadNameInfo.m_junction_ref = ft->GetMetadata(feature::Metadata::FMD_JUNCTION_REF);
pathSegment.m_roadNameInfo.m_destination_ref = ft->GetMetadata(feature::Metadata::FMD_DESTINATION_REF);
pathSegment.m_roadNameInfo.m_destination = ft->GetMetadata(feature::Metadata::FMD_DESTINATION);
diff --git a/libs/routing/directions_engine.hpp b/libs/routing/directions_engine.hpp
index c7dc61840..c5babdb30 100644
--- a/libs/routing/directions_engine.hpp
+++ b/libs/routing/directions_engine.hpp
@@ -33,9 +33,18 @@ class DirectionsEngine
// @TODO(bykoianko) Method Generate() should fill
// vector instead of corresponding arguments.
- /// \brief Generates all args which are passed by reference.
- /// \param path is points of the route. It should not be empty.
- /// \returns true if fields passed by reference are filled correctly and false otherwise.
+ /**
+ * @brief Calculates segments from a path on a route graph.
+ *
+ * Segments are calculated from `graph` (the route graph) and `path` (points on the route); each
+ * pair of consecutive points becomes a segment.
+ *
+ * @param graph The route graph
+ * @param path The route path, an ordered list of points on the route
+ * @param cancellable
+ * @param routeSegments Receives the list of segments
+ * @return true on successful completion, false if cancelled or an error occurred
+ */
bool Generate(IndexRoadGraph const & graph, std::vector const & path,
base::Cancellable const & cancellable, std::vector & routeSegments);
void Clear();
diff --git a/libs/routing/edge_estimator.cpp b/libs/routing/edge_estimator.cpp
index ed9df5336..881a94a53 100644
--- a/libs/routing/edge_estimator.cpp
+++ b/libs/routing/edge_estimator.cpp
@@ -826,6 +826,11 @@ shared_ptr EdgeEstimator::Create(VehicleType vehicleType, double
case VehicleType::Bicycle: return make_shared(maxWeighSpeedKMpH, offroadSpeedKMpH);
case VehicleType::Car:
return make_shared(dataSourcePtr, numMwmIds, trafficStash, maxWeighSpeedKMpH, offroadSpeedKMpH);
+ /*
+ * VehicleType::Decoder is for use with the TraFF decoder, which creates its EdgeEstimator by
+ * explicitly calling the constructor for the appropriate subclass.
+ */
+ case VehicleType::Decoder: CHECK(false, ("Creating EdgeEstimator for Decoder is not supported")); return nullptr;
case VehicleType::Count: CHECK(false, ("Can't create EdgeEstimator for", vehicleType)); return nullptr;
}
UNREACHABLE();
diff --git a/libs/routing/edge_estimator.hpp b/libs/routing/edge_estimator.hpp
index 4729f9d3f..f224c44c8 100644
--- a/libs/routing/edge_estimator.hpp
+++ b/libs/routing/edge_estimator.hpp
@@ -23,41 +23,173 @@ class TrafficStash;
class EdgeEstimator
{
public:
+ /**
+ * @brief The purpose for which cost calculations are to be used.
+ *
+ * A number of cost estimation functions take `Purpose` as an argument and may return different
+ * values depending on the value of that argument.
+ */
enum class Purpose
{
+ /**
+ * @brief Indicates that cost calculations are for the purpose of choosing the best route.
+ */
Weight,
+ /**
+ * @brief Indicates that cost calculations are for the purpose of calculating the estimated time
+ * of arrival.
+ */
ETA
};
+ /**
+ * @brief Constructs a new `EdgeEstimator`.
+ *
+ * @param vehicleType The vehicle type.
+ * @param maxWeightSpeedKMpH The maximum speed for the vehicle on a road.
+ * @param offroadSpeedKMpH The maximum speed for the vehicle on an off-road link.
+ * @param dataSourcePtr
+ * @param numMwmIds
+ */
EdgeEstimator(VehicleType vehicleType, double maxWeightSpeedKMpH, SpeedKMpH const & offroadSpeedKMpH,
DataSource * dataSourcePtr = nullptr, std::shared_ptr numMwmIds = nullptr);
virtual ~EdgeEstimator() = default;
+ /**
+ * @brief Calculates the heuristic for two points.
+ *
+ * The heuristic is used by the A* routing algorithm when choosing the next point to examine. It
+ * must be less than, or equal to, the lowest possible cost of traveling from one point to the
+ * other. Zero is an admissible heuristic, but effectively downgrades the A* algorithm to behave
+ * exactly like the Dijkstra algorithm, of which A* is an improved version. A good heuristic is as
+ * close as possible to the actual cost, without violating the aforementioned requirement.
+ *
+ * @param from The start point for the part of the route for which the heuristic is to be calculated.
+ * @param to The destination point for the part of the route for which the heuristic is to be calculated.
+ * @return The heuristic, expressed as travel time in seconds.
+ */
double CalcHeuristic(ms::LatLon const & from, ms::LatLon const & to) const;
- // Estimates time in seconds it takes to go from point |from| to point |to| along a leap (fake)
- // edge |from|-|to| using real features.
- // Note 1. The result of the method should be used if it's necessary to add a leap (fake) edge
- // (|from|, |to|) in road graph.
- // Note 2. The result of the method should be less or equal to CalcHeuristic(|from|, |to|).
- // Note 3. It's assumed here that CalcLeapWeight(p1, p2) == CalcLeapWeight(p2, p1).
+
+ /**
+ * @brief Estimates travel time between two points along a leap (fake) edge using real features.
+ *
+ * Estimates time in seconds it takes to go from point `from` to point `to` along a leap (fake)
+ * edge `from`-`to` using real features.
+ *
+ * Note 1. The result of the method should be used if it is necessary to add a leap (fake) edge
+ * (`from`, `to`) in road graph.
+ *
+ * Note 2. The result of the method should be less or equal to `CalcHeuristic(from, to)`.
+ *
+ * Note 3. It is assumed here that `CalcLeapWeight(p1, p2) == CalcLeapWeight(p2, p1)`.
+ *
+ * @todo Note 2 looks like a typo, presumably the result of this method should be no less than the
+ * heuristic (otherwise the heuristic might not satisfy the requirements of A*).
+ *
+ * @param from The start point.
+ * @param to The destination point.
+ * @param mwmId
+ * @return Travel time in seconds.
+ */
double CalcLeapWeight(ms::LatLon const & from, ms::LatLon const & to, NumMwmId mwmId = kFakeNumMwmId);
+ /**
+ * @brief Returns the maximum speed this `EdgeEstimator` instance assumes for any road.
+ * @return The speed in m/s.
+ */
double GetMaxWeightSpeedMpS() const;
- // Estimates time in seconds it takes to go from point |from| to point |to| along direct fake edge.
- double CalcOffroad(ms::LatLon const & from, ms::LatLon const & to, Purpose purpose) const;
+ /**
+ * @brief Estimates travel time between two points along a direct fake edge.
+ *
+ * Estimates time in seconds it takes to go from point `from` to point `to` along direct fake edge.
+ *
+ * @param from The start point.
+ * @param to The destination point.
+ * @param purpose The purpose for which the result is to be used.
+ * @return Travel time in seconds.
+ */
+ virtual double CalcOffroad(ms::LatLon const & from, ms::LatLon const & to, Purpose purpose) const;
+ /**
+ * @brief Returns the travel time along a segment.
+ *
+ * @param segment The segment.
+ * @param road The road geometry (speed, restrictions, points) for the road which the segment is a part of.
+ * @param purpose The purpose for which the result is to be used.
+ * @return Travel time in seconds.
+ */
virtual double CalcSegmentWeight(Segment const & segment, RoadGeometry const & road, Purpose purpose) const = 0;
+
+ /**
+ * @brief Returns the penalty for making a U turn.
+ *
+ * The penalty is a fixed amount of time, determined by the implementation.
+ *
+ * U turns are determined and the penalty is applied in `IndexGraph::GetPenalties()`,
+ * in (`index_graph.cpp`). Actual U turn detection is deferred to `IsUTurn()` in the same file.
+ *
+ * @param purpose The purpose for which the result is to be used.
+ * @return The penalty in seconds.
+ */
virtual double GetUTurnPenalty(Purpose purpose) const = 0;
+
virtual double GetTurnPenalty(Purpose purpose, double angle, RoadGeometry const & from_road,
RoadGeometry const & to_road, bool is_left_hand_traffic = false) const = 0;
+
+ /**
+ * @brief Returns the penalty for using a ferry or rail transit link.
+ *
+ * The penalty is a fixed amount of time, determined by the implementation. It applies once per
+ * link, hence it needs to cover the sum of the time for boarding and unboarding.
+ *
+ * @param purpose The purpose for which the result is to be used.
+ * @return The penalty in seconds.
+ */
virtual double GetFerryLandingPenalty(Purpose purpose) const = 0;
+ /**
+ * @brief Whether access restrictions are ignored.
+ *
+ * A return value of false indicates that access restrictions should be observed, which is the
+ * default behavior for a routing use case. If true, it indicates that routing should ignore
+ * access restrictions. This is needed to resolve traffic message locations; it could also be
+ * used e.g. for emergency vehicle use cases.
+ *
+ * This implementation always returns false.
+ */
+ virtual bool IsAccessIgnored() const { return false; }
+
+ /**
+ * @brief Creates an `EdgeEstimator` based on maximum speeds.
+ *
+ * @param vehicleType The vehicle type.
+ * @param maxWeighSpeedKMpH The maximum speed for the vehicle on a road.
+ * @param offroadSpeedKMpH The maximum speed for the vehicle on an off-road link.
+ * @param trafficStash The traffic stash (used only for some vehicle types).
+ * @param dataSourcePtr
+ * @param numMwmIds
+ * @return The `EdgeEstimator` instance.
+ */
static std::shared_ptr Create(VehicleType vehicleType, double maxWeighSpeedKMpH,
SpeedKMpH const & offroadSpeedKMpH,
std::shared_ptr trafficStash, DataSource * dataSourcePtr,
std::shared_ptr numMwmIds);
+ /**
+ * @brief Creates an `EdgeEstimator` based on a vehicle model.
+ *
+ * This is a convenience wrapper around `Create(VehicleType, double, SpeedKMpH const &,
+ * std::shared_ptr, DataSource *, std::shared_ptr)`, which takes a
+ * `VehicleModel` and derives the maximum speeds for the vehicle from that.
+ *
+ * @param vehicleType The vehicle type.
+ * @param vehicleModel
+ * @param trafficStash The traffic stash (used only for some vehicle types).
+ * @param dataSourcePtr
+ * @param numMwmIds
+ * @return The `EdgeEstimator` instance.
+ */
static std::shared_ptr Create(VehicleType vehicleType, VehicleModelInterface const & vehicleModel,
std::shared_ptr trafficStash, DataSource * dataSourcePtr,
std::shared_ptr numMwmIds);
@@ -75,12 +207,54 @@ class EdgeEstimator
// std::shared_ptr m_numMwmIds;
// ankerl::unordered_dense::map m_leapWeightSpeedMpS;
+ /**
+ * @brief Computes the default speed for leap (fake) segments.
+ *
+ * The result is used by `GetLeapWeightSpeed()`.
+ *
+ * @return Speed in m/s.
+ */
double ComputeDefaultLeapWeightSpeed() const;
+
+ /**
+ * @brief Returns the deafult speed for leap (fake) segments for a given MWM.
+ * @param mwmId
+ * @return Speed in m/s.
+ */
double GetLeapWeightSpeed(NumMwmId mwmId);
// double LoadLeapWeightSpeed(NumMwmId mwmId);
};
+/**
+ * @brief Calculates the climb penalty for pedestrians.
+ *
+ * The climb penalty is a factor which can be multiplied with the cost of an edge which goes uphill
+ * or downhill. The factor for no penalty is 1, i.e. the cost of the edge is not changed.
+ *
+ * The climb penalty may depend on the mode of transportation, the ascent or descent, as well as the
+ * altitude (allowing for different penalties at greater altitudes).
+ *
+ * @param purpose The purpose for which the result is to be used.
+ * @param tangent The tangent of the ascent or descent (10% would be 0.1 for ascent, -0.1 for descent).
+ * @param altitudeM The altitude in meters.
+ * @return The climb penalty, as a factor.
+ */
double GetPedestrianClimbPenalty(EdgeEstimator::Purpose purpose, double tangent, geometry::Altitude altitudeM);
+
+/**
+ * @brief Calculates the climb penalty for cyclists.
+ *
+ * The climb penalty is a factor which can be multiplied with the cost of an edge which goes uphill
+ * or downhill. The factor for no penalty is 1, i.e. the cost of the edge is not changed.
+ *
+ * The climb penalty may depend on the mode of transportation, the ascent or descent, as well as the
+ * altitude (allowing for different penalties at greater altitudes).
+ *
+ * @param purpose The purpose for which the result is to be used.
+ * @param tangent The tangent of the ascent or descent (10% would be 0.1 for ascent, -0.1 for descent).
+ * @param altitudeM The altitude in meters.
+ * @return The climb penalty, as a factor.
+ */
double GetBicycleClimbPenalty(EdgeEstimator::Purpose purpose, double tangent, geometry::Altitude altitudeM);
} // namespace routing
diff --git a/libs/routing/fake_ending.cpp b/libs/routing/fake_ending.cpp
index 683bad7e3..4a32f439f 100644
--- a/libs/routing/fake_ending.cpp
+++ b/libs/routing/fake_ending.cpp
@@ -18,12 +18,12 @@ using namespace routing;
using namespace std;
LatLonWithAltitude CalcProjectionToSegment(LatLonWithAltitude const & begin, LatLonWithAltitude const & end,
- m2::PointD const & point)
+ m2::PointD const & point, bool snapToEnds)
{
m2::ParametrizedSegment segment(mercator::FromLatLon(begin.GetLatLon()),
mercator::FromLatLon(end.GetLatLon()));
- auto const projectedPoint = segment.ClosestPointTo(point);
+ auto const projectedPoint = segment.ClosestPointTo(point, snapToEnds);
auto const distBeginToEnd = ms::DistanceOnEarth(begin.GetLatLon(), end.GetLatLon());
auto const projectedLatLon = mercator::ToLatLon(projectedPoint);
@@ -45,7 +45,8 @@ bool Projection::operator==(Projection const & other) const
tie(other.m_segment, other.m_isOneWay, other.m_segmentFront, other.m_segmentBack, other.m_junction);
}
-FakeEnding MakeFakeEnding(vector const & segments, m2::PointD const & point, WorldGraph & graph)
+FakeEnding MakeFakeEnding(vector const & segments, m2::PointD const & point,
+ WorldGraph & graph, bool snapToEnds)
{
FakeEnding ending;
double averageAltitude = 0.0;
@@ -57,7 +58,7 @@ FakeEnding MakeFakeEnding(vector const & segments, m2::PointD const & p
bool const oneWay = graph.IsOneWay(segment.GetMwmId(), segment.GetFeatureId());
auto const & frontJunction = graph.GetJunction(segment, true /* front */);
auto const & backJunction = graph.GetJunction(segment, false /* front */);
- auto const & projectedJunction = CalcProjectionToSegment(backJunction, frontJunction, point);
+ auto const & projectedJunction = CalcProjectionToSegment(backJunction, frontJunction, point, snapToEnds);
ending.m_projections.emplace_back(segment, oneWay, frontJunction, backJunction, projectedJunction);
@@ -69,13 +70,14 @@ FakeEnding MakeFakeEnding(vector const & segments, m2::PointD const & p
return ending;
}
-FakeEnding MakeFakeEnding(Segment const & segment, m2::PointD const & point, IndexGraph & graph)
+FakeEnding MakeFakeEnding(Segment const & segment, m2::PointD const & point, IndexGraph & graph,
+ bool snapToEnds)
{
auto const & road = graph.GetRoadGeometry(segment.GetFeatureId());
bool const oneWay = road.IsOneWay();
auto const & frontJunction = road.GetJunction(segment.GetPointId(true /* front */));
auto const & backJunction = road.GetJunction(segment.GetPointId(false /* front */));
- auto const & projectedJunction = CalcProjectionToSegment(backJunction, frontJunction, point);
+ auto const & projectedJunction = CalcProjectionToSegment(backJunction, frontJunction, point, snapToEnds);
FakeEnding ending;
ending.m_originJunction = LatLonWithAltitude(mercator::ToLatLon(point), projectedJunction.GetAltitude());
diff --git a/libs/routing/fake_ending.hpp b/libs/routing/fake_ending.hpp
index f12215603..5cc59a7d0 100644
--- a/libs/routing/fake_ending.hpp
+++ b/libs/routing/fake_ending.hpp
@@ -40,9 +40,11 @@ struct FakeEnding final
std::vector m_projections;
};
-FakeEnding MakeFakeEnding(std::vector const & segments, m2::PointD const & point, WorldGraph & graph);
-FakeEnding MakeFakeEnding(Segment const & segment, m2::PointD const & point, IndexGraph & graph);
+FakeEnding MakeFakeEnding(std::vector const & segments, m2::PointD const & point,
+ WorldGraph & graph, bool snapToEnds = false);
+FakeEnding MakeFakeEnding(Segment const & segment, m2::PointD const & point, IndexGraph & graph,
+ bool snapToEnds = false);
LatLonWithAltitude CalcProjectionToSegment(LatLonWithAltitude const & begin, LatLonWithAltitude const & end,
- m2::PointD const & point);
+ m2::PointD const & point, bool snapToEnds = false);
} // namespace routing
diff --git a/libs/routing/features_road_graph.cpp b/libs/routing/features_road_graph.cpp
index 13c894983..f0bd2ec08 100644
--- a/libs/routing/features_road_graph.cpp
+++ b/libs/routing/features_road_graph.cpp
@@ -143,9 +143,10 @@ void FeaturesRoadGraphBase::ForEachFeatureClosestToCross(m2::PointD const & cros
}
void FeaturesRoadGraphBase::FindClosestEdges(m2::RectD const & rect, uint32_t count,
- vector> & vicinities) const
+ vector> & vicinities,
+ bool snapToEnds) const
{
- NearestEdgeFinder finder(rect.Center(), nullptr /* IsEdgeProjGood */);
+ NearestEdgeFinder finder(rect.Center(), nullptr /* IsEdgeProjGood */, snapToEnds);
m_dataSource.ForEachStreet([&](FeatureType & ft)
{
diff --git a/libs/routing/features_road_graph.hpp b/libs/routing/features_road_graph.hpp
index 712236bad..9a323ab3b 100644
--- a/libs/routing/features_road_graph.hpp
+++ b/libs/routing/features_road_graph.hpp
@@ -84,8 +84,21 @@ class FeaturesRoadGraphBase : public IRoadGraph
/// @name IRoadGraph overrides
/// @{
void ForEachFeatureClosestToCross(m2::PointD const & cross, ICrossEdgesLoader & edgesLoader) const override;
+
+ /**
+ * @brief Finds the closest edges to a reference point within a given distance.
+ *
+ * @param rect A rectangle. Its center is the reference point; the search distance is expressed
+ * through the height and width.
+ * @param count The number of results to return.
+ * @param vicinities Receives the results.
+ * @param snapToEnds If true, the projection point (the point on the edge closest to the reference
+ * point) is constrained to one of the edge endpoints; if false, it can be anywhere on the edge.
+ */
void FindClosestEdges(m2::RectD const & rect, uint32_t count,
- std::vector> & vicinities) const override;
+ std::vector> & vicinities,
+ bool snapToEnds = false) const override;
+
std::vector FindRoads(m2::RectD const & rect,
IsGoodFeatureFn const & isGoodFeature) const override;
void GetFeatureTypes(FeatureID const & featureId, feature::TypesHolder & types) const override;
diff --git a/libs/routing/index_graph.hpp b/libs/routing/index_graph.hpp
index 5b7db8980..56c0cec12 100644
--- a/libs/routing/index_graph.hpp
+++ b/libs/routing/index_graph.hpp
@@ -28,6 +28,20 @@
namespace routing
{
+/**
+ * @brief Whether a maneuver between two segments is a U turn.
+ *
+ * A maneuver between two segments `u` and `v` is a U turn if, and only if, both segments differ in
+ * direction but are otherwise identical.
+ *
+ * For U turns on multiple-carriageway roads, i.e. from one carriageway onto a crossing road (or
+ * connecting cariageway) and further to the opposite cariageway, this function returns false.
+ *
+ * @param u The first segment
+ * @param v The second segment
+ *
+ * @return True if the maneuver is a U turn on the same segment, false otherwise.
+ */
bool IsUTurn(Segment const & u, Segment const & v);
enum class WorldGraphMode;
@@ -218,6 +232,9 @@ template
bool IndexGraph::IsAccessNoForSure(AccessPositionType const & accessPositionType, RouteWeight const & weight,
bool useAccessConditional) const
{
+ if (m_estimator->IsAccessIgnored())
+ return false;
+
auto const [accessType, confidence] = useAccessConditional
? m_roadAccess.GetAccess(accessPositionType, weight)
: m_roadAccess.GetAccessWithoutConditional(accessPositionType);
diff --git a/libs/routing/index_graph_loader.cpp b/libs/routing/index_graph_loader.cpp
index 56c7a47b2..959e7b811 100644
--- a/libs/routing/index_graph_loader.cpp
+++ b/libs/routing/index_graph_loader.cpp
@@ -227,7 +227,12 @@ bool ReadRoadPenaltyFromMwm(MwmValue const & mwmValue, VehicleType vehicleType,
// Read number of vehicle types
uint32_t numVehicleTypes = ReadPrimitiveFromSource(src);
- CHECK_EQUAL(numVehicleTypes, static_cast(VehicleType::Count), ());
+ if (numVehicleTypes <= static_cast(vehicleType)
+ && vehicleType == VehicleType::Decoder
+ && numVehicleTypes > static_cast(VehicleType::Car))
+ // This is expected for older mwm files (up to v11) - not an error
+ vehicleType = VehicleType::Car;
+ CHECK(numVehicleTypes > static_cast(vehicleType), ());
// Skip to the correct vehicle type
for (uint32_t i = 0; i < static_cast(vehicleType); ++i)
diff --git a/libs/routing/index_router.cpp b/libs/routing/index_router.cpp
index b524a99d5..37879ba2a 100644
--- a/libs/routing/index_router.cpp
+++ b/libs/routing/index_router.cpp
@@ -27,6 +27,7 @@
#include "routing_common/bicycle_model.hpp"
#include "routing_common/car_model.hpp"
+#include "routing_common/decoder_model.hpp"
#include "routing_common/pedestrian_model.hpp"
#include "indexer/data_source.hpp"
@@ -104,6 +105,7 @@ shared_ptr CreateVehicleModelFactory(
case VehicleType::Transit: return make_shared(countryParentNameGetterFn);
case VehicleType::Bicycle: return make_shared(countryParentNameGetterFn);
case VehicleType::Car: return make_shared(countryParentNameGetterFn);
+ case VehicleType::Decoder: return make_shared(countryParentNameGetterFn);
case VehicleType::Count: CHECK(false, ("Can't create VehicleModelFactoryInterface for", vehicleType)); return nullptr;
}
UNREACHABLE();
@@ -117,16 +119,23 @@ unique_ptr CreateDirectionsEngine(VehicleType vehicleType, sha
case VehicleType::Pedestrian:
case VehicleType::Transit: return make_unique(dataSource, numMwmIds);
case VehicleType::Bicycle:
- case VehicleType::Car: return make_unique(dataSource, numMwmIds);
+ case VehicleType::Car:
+ case VehicleType::Decoder:
+ /*
+ * For `VehicleType::Decoder` the directions engine serves no useful purpose.
+ * We could return `nullptr` here, but we would have to go through every usage of
+ * `m_directionsEngine` and ensure it can handle a null pointer.
+ */
+ return make_unique(dataSource, numMwmIds);
case VehicleType::Count: CHECK(false, ("Can't create DirectionsEngine for", vehicleType)); return nullptr;
}
UNREACHABLE();
}
-shared_ptr CreateTrafficStash(VehicleType, shared_ptr, traffic::TrafficCache const &)
+shared_ptr CreateTrafficStash(VehicleType vehicleType, shared_ptr numMwmIds,
+ traffic::TrafficCache const & trafficCache)
{
- return nullptr;
- // return (vehicleType == VehicleType::Car ? make_shared(trafficCache, numMwmIds) : nullptr);
+ return (vehicleType == VehicleType::Car ? make_shared(trafficCache, numMwmIds) : nullptr);
}
void PushPassedSubroutes(Checkpoints const & checkpoints, vector & subroutes)
@@ -262,6 +271,37 @@ IndexRouter::IndexRouter(VehicleType vehicleType, bool loadAltitudes,
CHECK(m_directionsEngine, ());
}
+IndexRouter::IndexRouter(VehicleType vehicleType, bool loadAltitudes,
+ CountryParentNameGetterFn const & countryParentNameGetterFn,
+ TCountryFileFn const & countryFileFn, CountryRectFn const & countryRectFn,
+ shared_ptr numMwmIds, unique_ptr> numMwmTree,
+ std::shared_ptr estimator, DataSource & dataSource)
+ : m_vehicleType(vehicleType)
+ , m_loadAltitudes(loadAltitudes)
+ , m_name("astar-bidirectional-" + ToString(m_vehicleType))
+ , m_dataSource(dataSource, numMwmIds)
+ , m_vehicleModelFactory(CreateVehicleModelFactory(m_vehicleType, countryParentNameGetterFn))
+ , m_countryFileFn(countryFileFn)
+ , m_countryRectFn(countryRectFn)
+ , m_numMwmIds(std::move(numMwmIds))
+ , m_numMwmTree(std::move(numMwmTree))
+ , m_trafficStash(nullptr)
+ , m_roadGraph(m_dataSource,
+ vehicleType == VehicleType::Pedestrian || vehicleType == VehicleType::Transit
+ ? IRoadGraph::Mode::IgnoreOnewayTag
+ : IRoadGraph::Mode::ObeyOnewayTag,
+ m_vehicleModelFactory)
+ , m_estimator(estimator)
+ , m_directionsEngine(CreateDirectionsEngine(m_vehicleType, m_numMwmIds, m_dataSource))
+ , m_countryParentNameGetterFn(countryParentNameGetterFn)
+{
+ CHECK(!m_name.empty(), ());
+ CHECK(m_numMwmIds, ());
+ CHECK(m_numMwmTree, ());
+ CHECK(m_estimator, ());
+ CHECK(m_directionsEngine, ());
+}
+
unique_ptr IndexRouter::MakeSingleMwmWorldGraph()
{
auto worldGraph = MakeWorldGraph();
@@ -282,8 +322,9 @@ bool IndexRouter::FindClosestProjectionToRoad(m2::PointD const & point, m2::Poin
auto const rect = mercator::RectByCenterXYAndSizeInMeters(point, radius);
std::vector candidates;
+ // TODO should we increase the count in decoding mode?
uint32_t const count = direction.IsAlmostZero() ? 1 : 4;
- m_roadGraph.FindClosestEdges(rect, count, candidates);
+ m_roadGraph.FindClosestEdges(rect, count, candidates, (GetMode() == Mode::Decoding));
if (candidates.empty())
return false;
@@ -516,7 +557,7 @@ RouterResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints,
guidesMwmId = m_numMwmIds->GetId(country);
}
- if (!route.GetAbsentCountries().empty())
+ if ((GetMode() == Mode::Navigation) && !route.GetAbsentCountries().empty())
return RouterResultCode::NeedMoreMaps;
TrafficStash::Guard guard(m_trafficStash);
@@ -549,7 +590,7 @@ RouterResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints,
FakeEnding startFakeEnding = m_guides.GetFakeEnding(i);
FakeEnding finishFakeEnding = m_guides.GetFakeEnding(i + 1);
- bool isStartSegmentStrictForward = (m_vehicleType == VehicleType::Car);
+ bool isStartSegmentStrictForward = ((m_vehicleType == VehicleType::Car) || (m_vehicleType == VehicleType::Decoder));
if (startFakeEnding.m_projections.empty() || finishFakeEnding.m_projections.empty())
{
bool const isFirstSubroute = (i == checkpoints.GetPassedIdx());
@@ -1060,10 +1101,15 @@ RouterResultCode IndexRouter::AdjustRoute(Checkpoints const & checkpoints, m2::P
return RouterResultCode::NoError;
}
+RoutingOptions IndexRouter::GetRoutingOptions()
+{
+ return RoutingOptions::LoadCarOptionsFromSettings();
+}
+
unique_ptr IndexRouter::MakeWorldGraph()
{
// Use saved routing options for all types (car, bicycle, pedestrian).
- RoutingOptions const routingOptions = RoutingOptions::LoadCarOptionsFromSettings();
+ RoutingOptions const routingOptions = GetRoutingOptions();
/// @DebugNote
// Add avoid roads here for debug purpose.
// routingOptions.Add(RoutingOptions::Road::Motorway);
@@ -1110,10 +1156,10 @@ int IndexRouter::PointsOnEdgesSnapping::Snap(m2::PointD const & start, m2::Point
// One of startEnding or finishEnding will be empty here.
if (startEnding.m_projections.empty())
- startEnding = MakeFakeEnding(m_startSegments, start, m_graph);
+ startEnding = MakeFakeEnding(m_startSegments, start, m_graph, (m_router.GetMode() == Mode::Decoding));
if (finishEnding.m_projections.empty())
- finishEnding = MakeFakeEnding(finishSegments, finish, m_graph);
+ finishEnding = MakeFakeEnding(finishSegments, finish, m_graph, (m_router.GetMode() == Mode::Decoding));
return 0;
}
@@ -1195,12 +1241,11 @@ bool IndexRouter::PointsOnEdgesSnapping::IsFencedOff(m2::PointD const & point, E
return false;
}
-// static
void IndexRouter::PointsOnEdgesSnapping::RoadsToNearestEdges(m2::PointD const & point, vector const & roads,
IsEdgeProjGood const & isGood,
vector & edgeProj)
{
- NearestEdgeFinder finder(point, isGood);
+ NearestEdgeFinder finder(point, isGood, (m_router.GetMode() == Mode::Decoding));
for (auto const & road : roads)
finder.AddInformationSource(road);
@@ -1324,7 +1369,7 @@ bool IndexRouter::PointsOnEdgesSnapping::FindBestEdges(m2::PointD const & checkp
}
// Removing all candidates which are fenced off by the road graph (|closestRoads|) from |checkpoint|.
- return !IsFencedOff(checkpoint, edgeProj, closestRoads);
+ return (m_router.GetMode() == Mode::Decoding) || !IsFencedOff(checkpoint, edgeProj, closestRoads);
};
// Getting closest edges from |closestRoads| if they are correct according to isGood() function.
@@ -1800,6 +1845,7 @@ void IndexRouter::SetupAlgorithmMode(IndexGraphStarter & starter, bool guidesAct
case VehicleType::Bicycle: starter.GetGraph().SetMode(WorldGraphMode::Joints); break;
case VehicleType::Transit: starter.GetGraph().SetMode(WorldGraphMode::NoLeaps); break;
case VehicleType::Car:
+ case VehicleType::Decoder:
starter.GetGraph().SetMode(AreMwmsNear(starter) ? WorldGraphMode::Joints : WorldGraphMode::LeapsOnly);
break;
case VehicleType::Count: CHECK(false, ("Unknown vehicle type:", m_vehicleType)); break;
diff --git a/libs/routing/index_router.hpp b/libs/routing/index_router.hpp
index eccc81bc7..3647f1690 100644
--- a/libs/routing/index_router.hpp
+++ b/libs/routing/index_router.hpp
@@ -43,6 +43,24 @@ class IndexGraphStarter;
class IndexRouter : public IRouter
{
public:
+ /**
+ * @brief Indicates the mode in which the router is operating.
+ *
+ * The mode controls some aspects of router behavior, such as asking for additional maps or how
+ * checkpoints are matched to nearby segments.
+ */
+ enum Mode
+ {
+ /**
+ * Router mode for navigation, i.e. user-initiated route guidance.
+ */
+ Navigation,
+ /**
+ * Router mode for location decoding.
+ */
+ Decoding
+ };
+
class BestEdgeComparator final
{
public:
@@ -67,6 +85,24 @@ class IndexRouter : public IRouter
m2::PointD const m_direction;
};
+ /**
+ * @brief Creates a new `IndexRouter` instance.
+ *
+ * This is the constructor intended for normal routing. It requires a `TrafficCache` argument,
+ * from which it may create a traffic stash so the traffic situation can be considered for the
+ * route, depending on the vehicle type.
+ *
+ * @param vehicleType The vehichle type
+ * @param loadAltitudes Whether to load altitudes
+ * @param countryParentNameGetterFn Function which converts a country name into the name of its parent country)
+ * @param countryFileFn Function which converts a pointer to its country name
+ * @param countryRectFn Function which returns the rect for a country
+ * @param numMwmIds MWMs to use for route calculation (this should include all MWMs, whether or
+ * not we have the file locally, but not World or WorldCoasts)
+ * @param numMwmTree
+ * @param trafficCache The traffic cache (used only if `vehicleType` is `VehicleType::Car`)
+ * @param dataSource The MWM data source
+ */
IndexRouter(VehicleType vehicleType, bool loadAltitudes, CountryParentNameGetterFn const & countryParentNameGetterFn,
TCountryFileFn const & countryFileFn, CountryRectFn const & countryRectFn,
std::shared_ptr