mirror of
https://codeberg.org/comaps/comaps
synced 2026-01-09 22:04:43 +00:00
Compare commits
237 Commits
7b5878b010
...
traffic
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
426a9661ab | ||
|
|
d473361e54 | ||
|
|
76d58e4a05 | ||
|
|
6dbde46dca | ||
|
|
7ed4a6ddd9 | ||
|
|
645ca792f7 | ||
|
|
cd64d14830 | ||
|
|
67b1d918ba | ||
|
|
48a3e5d4b0 | ||
|
|
d4c002851b | ||
|
|
9d4801886e | ||
|
|
5a26b72431 | ||
|
|
859b89e127 | ||
|
|
8fcf00b0ca | ||
|
|
5c2cedb19a | ||
|
|
67a3866529 | ||
|
|
90fdf0ed72 | ||
|
|
2b7ee4ba44 | ||
|
|
cd1201cb02 | ||
|
|
e6937b211a | ||
|
|
27ad70d729 | ||
|
|
a9700156db | ||
|
|
1d87e0e987 | ||
|
|
f7882636cd | ||
|
|
40fbea11e7 | ||
|
|
e87727c168 | ||
|
|
d82b545e30 | ||
|
|
781d973faa | ||
|
|
d6818786f7 | ||
|
|
615f57c604 | ||
|
|
af20e2b42b | ||
|
|
05f6dfad7b | ||
|
|
2b867a64a0 | ||
|
|
c0fd405798 | ||
|
|
207d6c833d | ||
|
|
d47aa09053 | ||
|
|
fbc150fae2 | ||
|
|
d88ed01bc1 | ||
|
|
3ec32e4415 | ||
|
|
9065f45b21 | ||
|
|
46d363ae24 | ||
|
|
0dc62c47dd | ||
|
|
3c9eeb9a75 | ||
|
|
ae23afd72e | ||
|
|
800cc0641b | ||
|
|
1b74062447 | ||
|
|
6a694c5d3e | ||
|
|
dda13b8d3d | ||
|
|
c1340a9941 | ||
|
|
a313526aed | ||
|
|
32d1a3a36e | ||
|
|
3a45740884 | ||
|
|
4e624bd04b | ||
|
|
a3d1ed83c3 | ||
|
|
38dbad0f7e | ||
|
|
7fe5823140 | ||
|
|
091d510ba1 | ||
|
|
f07c8d66d8 | ||
|
|
c9f50cdc72 | ||
|
|
2ed9bc1880 | ||
|
|
d098ecae15 | ||
|
|
9e06ec815e | ||
|
|
bf6cf27f8c | ||
|
|
bf36c875d5 | ||
|
|
30b2df89cd | ||
|
|
ba2d653a30 | ||
|
|
d6eacd7364 | ||
|
|
07c75e627e | ||
|
|
e7bde9aa05 | ||
|
|
5ba708caff | ||
|
|
48e8f32e01 | ||
|
|
38e98df6cc | ||
|
|
0713e22328 | ||
|
|
3ed31a575f | ||
|
|
ac87e3c585 | ||
|
|
ef806cf18a | ||
|
|
d46c0fec76 | ||
|
|
1d42d3b431 | ||
|
|
2663eda820 | ||
|
|
16708aae7f | ||
|
|
6963637b1b | ||
|
|
57f55f1022 | ||
|
|
01476d3dc7 | ||
|
|
90d7cadc3f | ||
|
|
3a6f21dbd1 | ||
|
|
221fe69840 | ||
|
|
167f0b8377 | ||
|
|
df7d507e1b | ||
|
|
fe737602d8 | ||
|
|
daa147a721 | ||
|
|
bd555afe61 | ||
|
|
73a70d943e | ||
|
|
dde50bd0a1 | ||
|
|
0106dc3fe5 | ||
|
|
034856f033 | ||
|
|
f53c794fdd | ||
|
|
dcab6ee5a0 | ||
|
|
edc15ac982 | ||
|
|
871cd73592 | ||
|
|
958be3dee6 | ||
|
|
06f63dcb9a | ||
|
|
bebac8d8a7 | ||
|
|
a25602dbe0 | ||
|
|
98796cd6f8 | ||
|
|
2729d07732 | ||
|
|
61b15d623d | ||
|
|
03d6847be3 | ||
|
|
3b1fca01e3 | ||
|
|
be3792b93a | ||
|
|
7283e4ecb4 | ||
|
|
75c7d146af | ||
|
|
d988ab3326 | ||
|
|
798affe0ef | ||
|
|
93a1f9d1a6 | ||
|
|
3f58c6ee20 | ||
|
|
a20d1453e0 | ||
|
|
e825753487 | ||
|
|
cc58eaa50a | ||
|
|
75197a11a8 | ||
|
|
d03b47bee0 | ||
|
|
daf344b27f | ||
|
|
121bdc4af8 | ||
|
|
6656c7e441 | ||
|
|
f32493faaa | ||
|
|
4f4d376a4a | ||
|
|
4324e329e5 | ||
|
|
9eeac05fdf | ||
|
|
b418cf659c | ||
|
|
964368f5d4 | ||
|
|
23922f1c2b | ||
|
|
f02b1538e7 | ||
|
|
dd65e89f8f | ||
|
|
f132022e60 | ||
|
|
247f88254e | ||
|
|
81a31d6b42 | ||
|
|
89d1365fee | ||
|
|
9fb08bdc56 | ||
|
|
371a58f6f9 | ||
|
|
af8b748c59 | ||
|
|
a43e83d280 | ||
|
|
04b2059ca0 | ||
|
|
ed15925251 | ||
|
|
173b5e1718 | ||
|
|
26aa5e5f54 | ||
|
|
f31541efb2 | ||
|
|
db3ed87b92 | ||
|
|
fbaa5470fd | ||
|
|
0681171d69 | ||
|
|
e3d86be324 | ||
|
|
ef3de2c781 | ||
|
|
d574b536ba | ||
|
|
2d3ca8014b | ||
|
|
df13e279b6 | ||
|
|
b48310e6a5 | ||
|
|
b98fe1999c | ||
|
|
d47713516d | ||
|
|
d72bd9e00e | ||
|
|
8cffe8fa64 | ||
|
|
ae5dea4a53 | ||
|
|
588332a23b | ||
|
|
3eb99e952c | ||
|
|
9f4b6d73ce | ||
|
|
488159e2f9 | ||
|
|
f30316d868 | ||
|
|
daaf52d27d | ||
|
|
ba9980ba36 | ||
|
|
5531b1129b | ||
|
|
394a6673e5 | ||
|
|
62ee9d5b46 | ||
|
|
2592bcf042 | ||
|
|
dd7ed98c1a | ||
|
|
76fce016bb | ||
|
|
7db32a9922 | ||
|
|
fa5608d874 | ||
|
|
185febd8d8 | ||
|
|
7a5ea64ea0 | ||
|
|
a4106505af | ||
|
|
083845a502 | ||
|
|
c6de2a25aa | ||
|
|
4c5fb21c33 | ||
|
|
f566f6f0ef | ||
|
|
9afb28aaa1 | ||
|
|
bd178932c1 | ||
|
|
2894218573 | ||
|
|
63f0799161 | ||
|
|
5b67d668bd | ||
|
|
f7adea08a2 | ||
|
|
c0c8d5da58 | ||
|
|
2ed300ca08 | ||
|
|
52a915211e | ||
|
|
18f1dfac45 | ||
|
|
a7897e2347 | ||
|
|
e3f5dd3ca8 | ||
|
|
de03995e77 | ||
|
|
74d79e5c8e | ||
|
|
e2aff53291 | ||
|
|
a39bdee0d1 | ||
|
|
136293c308 | ||
|
|
356b051036 | ||
|
|
c8d5a07262 | ||
|
|
e94c23d538 | ||
|
|
2ba3030366 | ||
|
|
3455050876 | ||
|
|
cf57942a0b | ||
|
|
3a713c477a | ||
|
|
edb1b7e784 | ||
|
|
53e80b9283 | ||
|
|
7107314e2f | ||
|
|
fafec070c9 | ||
|
|
d7facd5732 | ||
|
|
382e46af63 | ||
|
|
d0a9c564e4 | ||
|
|
a9ceec3995 | ||
|
|
73d61ff655 | ||
|
|
5cdf14386d | ||
|
|
2f6a8564cb | ||
|
|
80a7ed503e | ||
|
|
6e65e60c3d | ||
|
|
f041f910e7 | ||
|
|
dbf253c9d1 | ||
|
|
24d65bd37f | ||
|
|
16cb70a952 | ||
|
|
8827ec3c09 | ||
|
|
7be0b8a256 | ||
|
|
f0f847b214 | ||
|
|
2017907b1f | ||
|
|
bb410fc3bc | ||
|
|
6e8d400611 | ||
|
|
7b420def17 | ||
|
|
737d7b5643 | ||
|
|
9c93f421ac | ||
|
|
1574b5b7cb | ||
|
|
932dda6552 | ||
|
|
6f2f61b30a | ||
|
|
20c9fc5f45 | ||
|
|
be3e3d773b | ||
|
|
0fd7f8d573 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -21,6 +21,7 @@ data/symbols/**/symbols.sdf
|
||||
|
||||
data/bookmarks
|
||||
data/edits.xml
|
||||
data/traffic.xml
|
||||
data/World.mwm
|
||||
data/WorldCoasts.mwm
|
||||
data/world_mwm/*
|
||||
|
||||
5
android/app/proguard-rules.pro
vendored
5
android/app/proguard-rules.pro
vendored
@@ -28,3 +28,8 @@
|
||||
# R8 crypts the source line numbers in all log messages.
|
||||
# https://github.com/organicmaps/organicmaps/issues/6559#issuecomment-1812039926
|
||||
-dontoptimize
|
||||
|
||||
# Keep classes for Android TraFF support
|
||||
-keep class app.organicmaps.sdk.traffxml.SourceImplV0_7 { *; }
|
||||
-keep class app.organicmaps.sdk.traffxml.SourceImplV0_8 { *; }
|
||||
|
||||
|
||||
10
android/app/src/fdroid/play/listings/de-DE/release-notes.txt
Normal file
10
android/app/src/fdroid/play/listings/de-DE/release-notes.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
• OpenStreetMap-Daten vom 9. Dezember
|
||||
• Material 3 Design
|
||||
• Im OSM-Editor können nun Ladestationen hinzugefügt werden
|
||||
• Schuko und Typ E Ladestationen hinzugefügt
|
||||
• Verbesserte Suchvorschläge
|
||||
• Litauische und lettische Sprachankündigungen
|
||||
• Die Fahranweisungen wurden vergrößert
|
||||
• Der Zoomlevel passt sich an die Distanz zur nächsten Abbiegung an
|
||||
• Neue Anordnung der Einstellungen
|
||||
Weitere Einzelheiten auf codeberg.org/comaps/comaps/releases
|
||||
@@ -1,13 +1,10 @@
|
||||
• Fixed voice directions pronouncing weird symbols in the beginning
|
||||
• OpenStreetMap data as of November 23
|
||||
|
||||
Changes in the previous release:
|
||||
• Added trees
|
||||
• Made bus stop icons smaller and show up earlier
|
||||
• Reduce visibility of entrances
|
||||
• Added several other POI types
|
||||
• Show sand areas on the map
|
||||
• Add business is vacant option to the OSM editor
|
||||
• Improved road shields in Europe
|
||||
• Avoid paved roads routing option
|
||||
• Added icons to the settings page
|
||||
• OpenStreetMap data as of December 9
|
||||
• Use Material 3 themes
|
||||
• Support charging sockets in OSM Editor
|
||||
• Added schuko/type-E charge sockets
|
||||
• Improved search results ranking
|
||||
• Enabled Lithuanian and Latvian in voice announcements
|
||||
• Improved size of driving indications
|
||||
• Base zoom level on distance to next turn
|
||||
• Reordered settings
|
||||
More details on codeberg.org/comaps/comaps/releases
|
||||
|
||||
11
android/app/src/fdroid/play/listings/es-ES/release-notes.txt
Normal file
11
android/app/src/fdroid/play/listings/es-ES/release-notes.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
• Datos de OpenStreetMap a fecha 9/12.
|
||||
• Uso de temas Material 3.
|
||||
• Compatibilidad con enchufes de recarga en Editor.
|
||||
• Se añaden enchufes de recarga schuko/tipo E.
|
||||
• Se mejora la búsqueda.
|
||||
• Se habilitan el lituano y el letón en las indicaciones de voz.
|
||||
• Se aumenta el tamaño de las indicaciones de conducción.
|
||||
• Nivel de zoom base según la distancia al siguiente giro.
|
||||
• Se han reordenado los ajustes.
|
||||
|
||||
Más detalles en codeberg.org/comaps/comaps/releases
|
||||
10
android/app/src/fdroid/play/listings/fr-FR/release-notes.txt
Normal file
10
android/app/src/fdroid/play/listings/fr-FR/release-notes.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
• Données OpenStreetMap du 9 Décembre
|
||||
• Utilisation de Material 3
|
||||
• Support de l'édition des bornes de recharge dans l'éditeur OSM
|
||||
• Ajout du type de prise schuko/type-E
|
||||
• Amélioration de l'ordre des résultats de recherche
|
||||
• Ajout du lituanien et du letton dans le guidage vocal
|
||||
• Amélioration de la taille des instructions dans la navigation
|
||||
• Niveau de zoom basé sur la distance jusqu’au prochain virage
|
||||
• Réorganisation des paramètres
|
||||
Plus de détails sur codeberg.org/comaps/comaps/releases
|
||||
10
android/app/src/fdroid/play/listings/hr/release-notes.txt
Normal file
10
android/app/src/fdroid/play/listings/hr/release-notes.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
• Podaci OpenStreetMap karte od 9. prosinca
|
||||
• Korištenje Material 3 tema
|
||||
• Podrška za utičnice za punjenje u OSM Editoru
|
||||
• Dodane šuko/tip-E utičnice
|
||||
• Poboljšano rangiranje rezultata pretraživanja
|
||||
• Omogućeni litvanski i latvijski jezici u glasovnim najavama
|
||||
• Povećana veličina indikatora vožnje
|
||||
• Razina zumiranja se mijenja ovisno o udaljenosti do sljedećeg skretanja
|
||||
• Promijenjen redoslijed postavki
|
||||
Više detalja na codeberg.org/comaps/comaps/releases
|
||||
10
android/app/src/fdroid/play/listings/pl-PL/release-notes.txt
Normal file
10
android/app/src/fdroid/play/listings/pl-PL/release-notes.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
• Dane OpenStreetMap z 9 grudnia
|
||||
• Użycie motywów Material 3
|
||||
• Obsługa gniazd ładowania w Edytorze OSM
|
||||
• Dodane gniazda ładowania schuko/type-E
|
||||
• Poprawiony ranking wyników wyszukiwania
|
||||
• Dodane litewskie i łotewskie komunikaty głosowe
|
||||
• Poprawiony rozmiar znaków drogowych
|
||||
• Poziom powiększenia oparty na odległości do następnego manewru
|
||||
• Zmieniona kolejność ustawień
|
||||
Więcej szczegółów na codeberg.org/comaps/comaps/releases
|
||||
10
android/app/src/fdroid/play/listings/pt-BR/release-notes.txt
Normal file
10
android/app/src/fdroid/play/listings/pt-BR/release-notes.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
• Dados OpenStreetMap atualizados em 9 de dezembro
|
||||
• Uso do estilo Material 3
|
||||
• Suporte para tomadas de carregamento no Editor OSM
|
||||
• Adição de tomadas de carregamento Schuko/Tipo E
|
||||
• Melhoria na classificação dos resultados de busca
|
||||
• Adição dos idiomas letão e lituano nas orientações por voz
|
||||
• Melhoria no tamanho das indicações de direção
|
||||
• Nível de zoom baseado em distância até a próxima curva
|
||||
• Configurações reordenadas
|
||||
Mais detalhes em codeberg.org/comaps/comaps/releases
|
||||
@@ -1 +1 @@
|
||||
version: 2025.03.02-7-FDroid+25030207
|
||||
version: 2025.07.23-4-FDroid+25072304
|
||||
|
||||
@@ -62,6 +62,21 @@
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="https" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="org.traffxml.traff.GET_CAPABILITIES"/>
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="org.traffxml.traff.POLL"/>
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="org.traffxml.traff.SUBSCRIBE"/>
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="org.traffxml.traff.SUBSCRIPTION_CHANGE"/>
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="org.traffxml.traff.UNSUBSCRIBE"/>
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<supports-screens
|
||||
|
||||
@@ -12,6 +12,7 @@ public class LayersUtils
|
||||
availableLayers.add(Mode.OUTDOORS);
|
||||
availableLayers.add(Mode.ISOLINES);
|
||||
availableLayers.add(Mode.SUBWAY);
|
||||
availableLayers.add(Mode.TRAFFIC);
|
||||
return availableLayers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,19 @@ package app.organicmaps.settings;
|
||||
import static app.organicmaps.leftbutton.LeftButtonsHolder.DISABLE_BUTTON_CODE;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.preference.EditTextPreference;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.MultiSelectListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.TwoStatePreference;
|
||||
@@ -28,6 +35,7 @@ import app.organicmaps.sdk.routing.RoutingOptions;
|
||||
import app.organicmaps.sdk.search.SearchRecents;
|
||||
import app.organicmaps.sdk.settings.MapLanguageCode;
|
||||
import app.organicmaps.sdk.settings.UnitLocale;
|
||||
import app.organicmaps.sdk.traffxml.AndroidTransport;
|
||||
import app.organicmaps.sdk.util.Config;
|
||||
import app.organicmaps.sdk.util.NetworkPolicy;
|
||||
import app.organicmaps.sdk.util.PowerManagment;
|
||||
@@ -35,11 +43,13 @@ import app.organicmaps.sdk.util.SharedPropertiesUtils;
|
||||
import app.organicmaps.sdk.util.log.LogsManager;
|
||||
import app.organicmaps.util.ThemeSwitcher;
|
||||
import app.organicmaps.util.Utils;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public class SettingsPrefsFragment extends BaseXmlSettingsFragment implements LanguagesFragment.Listener
|
||||
{
|
||||
@@ -61,6 +71,10 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment implements La
|
||||
initAutoDownloadPrefsCallbacks();
|
||||
initLargeFontSizePrefsCallbacks();
|
||||
initTransliterationPrefsCallbacks();
|
||||
initTrafficHttpEnabledPrefsCallbacks();
|
||||
initTrafficHttpUrlPrefsCallbacks();
|
||||
initTrafficAppsPrefs();
|
||||
initTrafficLegacyEnabledPrefsCallbacks();
|
||||
init3dModePrefsCallbacks();
|
||||
initPerspectivePrefsCallbacks();
|
||||
initAutoZoomPrefsCallbacks();
|
||||
@@ -136,6 +150,46 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment implements La
|
||||
pref.setSummary(locale.getDisplayLanguage());
|
||||
}
|
||||
|
||||
private void updateTrafficHttpUrlSummary()
|
||||
{
|
||||
final Preference pref = getPreference(getString(R.string.pref_traffic_http_url));
|
||||
String summary = Config.getTrafficHttpUrl();
|
||||
if (summary.length() == 0)
|
||||
pref.setSummary(R.string.traffic_http_url_not_set);
|
||||
else
|
||||
pref.setSummary(summary);
|
||||
}
|
||||
|
||||
private void updateTrafficAppsSummary()
|
||||
{
|
||||
final MultiSelectListPreference pref = getPreference(getString(R.string.pref_traffic_apps));
|
||||
/*
|
||||
* If the preference is disabled, it has not been initialized. This is the case if no TraFF
|
||||
* apps were found. The code below would crash when trying to access the entries, and there
|
||||
* is no need to update the summary if the setting cannot be changed.
|
||||
*/
|
||||
if (!pref.isEnabled())
|
||||
return;
|
||||
String[] apps = Config.getTrafficApps();
|
||||
if (apps.length == 0)
|
||||
pref.setSummary(R.string.traffic_apps_none_selected);
|
||||
else
|
||||
{
|
||||
String summary = "";
|
||||
for (int i = 0; i < apps.length; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
summary = summary + ", ";
|
||||
int index = pref.findIndexOfValue(apps[i]);
|
||||
if (i >= 0)
|
||||
summary = summary + pref.getEntries()[index];
|
||||
else
|
||||
summary = summary + apps[i];
|
||||
}
|
||||
pref.setSummary(summary);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRoutingSettingsPrefsSummary()
|
||||
{
|
||||
final Preference pref = getPreference(getString(R.string.prefs_routing));
|
||||
@@ -163,6 +217,8 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment implements La
|
||||
updateVoiceInstructionsPrefsSummary();
|
||||
updateRoutingSettingsPrefsSummary();
|
||||
updateMapLanguageCodeSummary();
|
||||
updateTrafficHttpUrlSummary();
|
||||
updateTrafficAppsSummary();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -224,6 +280,91 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment implements La
|
||||
});
|
||||
}
|
||||
|
||||
private void initTrafficHttpEnabledPrefsCallbacks()
|
||||
{
|
||||
final Preference pref = getPreference(getString(R.string.pref_traffic_http_enabled));
|
||||
|
||||
((TwoStatePreference)pref).setChecked(Config.getTrafficHttpEnabled());
|
||||
pref.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
final boolean oldVal = Config.getTrafficHttpEnabled();
|
||||
final boolean newVal = (Boolean) newValue;
|
||||
if (oldVal != newVal)
|
||||
Config.setTrafficHttpEnabled(newVal);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void initTrafficHttpUrlPrefsCallbacks()
|
||||
{
|
||||
final Preference pref = getPreference(getString(R.string.pref_traffic_http_url));
|
||||
|
||||
((EditTextPreference)pref).setText(Config.getTrafficHttpUrl());
|
||||
pref.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
final String oldVal = Config.getTrafficHttpUrl();
|
||||
final String newVal = (String) newValue;
|
||||
if (!oldVal.equals(newVal))
|
||||
Config.setTrafficHttpUrl(newVal);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void initTrafficAppsPrefs()
|
||||
{
|
||||
final MultiSelectListPreference pref = getPreference(getString(R.string.pref_traffic_apps));
|
||||
|
||||
PackageManager pm = getContext().getPackageManager();
|
||||
List<ResolveInfo> receivers = pm.queryBroadcastReceivers(new Intent(AndroidTransport.ACTION_TRAFF_GET_CAPABILITIES), 0);
|
||||
|
||||
if (receivers == null || receivers.isEmpty())
|
||||
{
|
||||
pref.setSummary(R.string.traffic_apps_not_available);
|
||||
pref.setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
pref.setEnabled(true);
|
||||
|
||||
List<String> entryList = new ArrayList<>(receivers.size());
|
||||
List<String> valueList = new ArrayList<>(receivers.size());
|
||||
|
||||
for (ResolveInfo receiver : receivers)
|
||||
{
|
||||
// friendly name
|
||||
entryList.add(receiver.loadLabel(pm).toString());
|
||||
// actual value (we just need the package name, broadcasts are sent to any receiver in the package)
|
||||
valueList.add(receiver.activityInfo.applicationInfo.packageName);
|
||||
}
|
||||
|
||||
pref.setEntries(entryList.toArray(new CharSequence[0]));
|
||||
pref.setEntryValues(valueList.toArray(new CharSequence[0]));
|
||||
|
||||
pref.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
// newValue is a Set<String>, each item is a package ID
|
||||
String[] apps = ((Set<String>)newValue).toArray(new String[0]);
|
||||
Config.setTrafficApps(apps);
|
||||
updateTrafficAppsSummary();
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void initTrafficLegacyEnabledPrefsCallbacks()
|
||||
{
|
||||
final Preference pref = getPreference(getString(R.string.pref_traffic_legacy_enabled));
|
||||
|
||||
((TwoStatePreference)pref).setChecked(Config.getTrafficLegacyEnabled());
|
||||
pref.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
final boolean oldVal = Config.getTrafficLegacyEnabled();
|
||||
final boolean newVal = (Boolean) newValue;
|
||||
if (oldVal != newVal)
|
||||
Config.setTrafficLegacyEnabled(newVal);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void initUseMobileDataPrefsCallbacks()
|
||||
{
|
||||
final ListPreference mobilePref = getPreference(getString(R.string.pref_use_mobile_data));
|
||||
|
||||
@@ -903,4 +903,6 @@
|
||||
<string name="editor_submit">Saada</string>
|
||||
<string name="power_management">Toitehaldus</string>
|
||||
<string name="charge_socket_schuko">EU-sisene</string>
|
||||
<string name="prefs_speed_cameras_information">Kiiruskaamerate teavitused on nendes riikides lülitatud välja, kus seda keelab kohalik seadusandlus.</string>
|
||||
<string name="navigation_start_tts_message">"Alustan tee juhatamist, hääljuhiste keel: "</string>
|
||||
</resources>
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
<string name="delete">سِتُردن</string>
|
||||
<string name="download_maps">بارگیری نقشهها</string>
|
||||
<!-- Settings/Downloader - info for country when download fails -->
|
||||
<string name="download_has_failed">دانلود ناموفق بود، برای تلاش مجدد لمس کنید</string>
|
||||
<string name="download_has_failed">بارگیری ناکام بود، برای تلاش دوباره بِپَرماسید</string>
|
||||
<!-- Settings/Downloader - info for country which started downloading -->
|
||||
<string name="downloading">درحال دانلود…</string>
|
||||
<string name="downloading">بارگیری میشود…</string>
|
||||
<!-- Choose measurement on first launch alert - choose metric system button -->
|
||||
<string name="kilometres">کیلومتر</string>
|
||||
<!-- Choose measurement on first launch alert - choose imperial system button -->
|
||||
@@ -21,9 +21,9 @@
|
||||
<!-- View and button titles for accessibility, please also edit it in iphone/plist.txt -->
|
||||
<string name="search">جستجو</string>
|
||||
<!-- Search box placeholder text; Used when searching on the map itself, not when searching for a map -->
|
||||
<string name="search_map">جستوجوی نقشه</string>
|
||||
<string name="search_map">جستجوی نقشه</string>
|
||||
<!-- Location services are disabled by user alert - message -->
|
||||
<string name="location_is_disabled_long_text">سرویس موقعیت مکانی شما غیر فعال است. لطفا جهت کارکرد صحیح نرم افزار آن را فعال کنید.</string>
|
||||
<string name="location_is_disabled_long_text">هماینک، شما همهی سامانههای گیاگیابی را برای این دستگاه یا این برنامه ناکارا کردهاید. خواهشمند است آن را در پیکربندیها کارا کنید.</string>
|
||||
<!-- A dialog title, that warns a user that Precise Location is disabled and suggests to turn it on -->
|
||||
<string name="limited_accuracy">دقت محدود</string>
|
||||
<!-- A dialog text, that warns a user that Precise Location is disabled and suggests to turn it on -->
|
||||
|
||||
@@ -255,4 +255,10 @@
|
||||
<string name="downloader_download_map">Preuzmi kartu</string>
|
||||
<string name="downloader_delete_map">Obriši kartu</string>
|
||||
<string name="downloader_update_map">Ažuriraj kartu</string>
|
||||
<string name="dialog_routing_select_closer_end">Molimo odaberite odredišnu točku koja se nalazi bliže cesti</string>
|
||||
<string name="dialog_routing_change_intermediate">Nije moguće pronaći međutočku</string>
|
||||
<string name="dialog_routing_intermediate_not_determined">Molimo prilagodite svoju međutočku</string>
|
||||
<string name="dialog_routing_system_error">Sistemska greška</string>
|
||||
<string name="dialog_routing_application_error">Nije moguće napraviti rutu zbog greške u aplikaciji</string>
|
||||
<string name="dialog_routing_try_again">Pokušajte ponovno</string>
|
||||
</resources>
|
||||
|
||||
374
android/app/src/main/res/values-kw/strings.xml
Normal file
374
android/app/src/main/res/values-kw/strings.xml
Normal file
@@ -0,0 +1,374 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_site_url">https://comaps.app/</string>
|
||||
<string name="back">Dehweles</string>
|
||||
<string name="cancel">Hedhi</string>
|
||||
<string name="delete">Dilea</string>
|
||||
<string name="download_maps">Iskarga Mappys</string>
|
||||
<string name="kilometres">Kilometrow</string>
|
||||
<string name="miles">Mildiryow</string>
|
||||
<string name="later">Wosa henna</string>
|
||||
<string name="search">Hwilas</string>
|
||||
<string name="search_map">Hwilas Mappa</string>
|
||||
<string name="limited_accuracy">Kewerder Strothys</string>
|
||||
<string name="zoom_to_country">Diskwedhes war dhe mappa</string>
|
||||
<string name="try_again">Assayewgh Arta</string>
|
||||
<string name="about_menu_title">A-dro CoMaps</string>
|
||||
<string name="about_proposition_3">• Dhywarlinen, uskis ha kesstrothys</string>
|
||||
<string name="location_settings">Settyansow GPS</string>
|
||||
<string name="close">Degea</string>
|
||||
<string name="download">Iskarga</string>
|
||||
<string name="download_resources_continue">Mos yn dhe Mappa</string>
|
||||
<string name="download_country_ask">Iskarga %1$s? (%2$s)</string>
|
||||
<string name="update_country_ask">Nowedhi %1$s? (%2$s)</string>
|
||||
<string name="pause">Powes</string>
|
||||
<string name="continue_button">Pesyewgh</string>
|
||||
<string name="bookmarks">Lyververkys</string>
|
||||
<string name="bookmarks_and_tracks">Lyververkys ha Lerghow</string>
|
||||
<string name="name">Hanow</string>
|
||||
<string name="address">Trigva</string>
|
||||
<string name="list">Rol</string>
|
||||
<string name="settings">Settyansow</string>
|
||||
<string name="move_maps">Gwaya mappys?</string>
|
||||
<string name="measurement_units">Systemow musurans</string>
|
||||
<string name="measurement_units_summary">Dewis yntra mildiryow ha kilometrow</string>
|
||||
<string name="category_food">Gwara boos</string>
|
||||
<string name="category_transport">Karyans</string>
|
||||
<string name="category_fuel">Petrol</string>
|
||||
<string name="category_parking">Parkyans</string>
|
||||
<string name="category_hotel">Ostel</string>
|
||||
<string name="category_tourism">Tornyaseth</string>
|
||||
<string name="category_entertainment">Didhan</string>
|
||||
<string name="category_atm">Toll y\'n fos</string>
|
||||
<string name="category_children">Dy\'gol teylu</string>
|
||||
<string name="category_bank">Arghanti</string>
|
||||
<string name="category_pharmacy">Ferylva</string>
|
||||
<string name="category_hospital">Klavji</string>
|
||||
<string name="category_toilet">Attesva</string>
|
||||
<string name="category_post">Post</string>
|
||||
<string name="category_police">Kreslu</string>
|
||||
<string name="category_recycling">Eylgylghyans</string>
|
||||
<string name="category_water">Dowr</string>
|
||||
<string name="category_rv">Komodytys Karavans</string>
|
||||
<string name="description">Notennow</string>
|
||||
<string name="load_kmz_title">Ow Karga Lyververkys</string>
|
||||
<string name="bookmark_share_email_subject">Dydh da, checkyewgh ow pynn yn CoMaps!</string>
|
||||
<string name="my_position_share_email_subject">Dydh da, checkyewgh ow tyller a\'n jydh war an mappa CoMaps!</string>
|
||||
<string name="share">Kevrenna</string>
|
||||
<string name="email">Ebost</string>
|
||||
<string name="done">Gwrys</string>
|
||||
<string name="data_version">Data OpenStreetMap: %s</string>
|
||||
<string name="tracks_title">Lerghow</string>
|
||||
<string name="length">Hys</string>
|
||||
<string name="share_my_location">Kevrennewgh Ow Tyller</string>
|
||||
<string name="prefs_group_general">Settyansow mayni</string>
|
||||
<string name="prefs_group_information">Kedhlow</string>
|
||||
<string name="prefs_group_route">Navygacyon</string>
|
||||
<string name="pref_zoom_summary">Displetya war an mappa</string>
|
||||
<string name="pref_map_style_title">Fordh Nos</string>
|
||||
<string name="auto">Awtomatek</string>
|
||||
<string name="pref_map_3d_title">Gwel gemusur</string>
|
||||
<string name="pref_map_3d_buildings_title">Drehevyansow lymnys tri</string>
|
||||
<string name="pref_tts_enable_title">Danvonadow dre lev</string>
|
||||
<string name="pref_tts_street_names_title">Deklarya Henwyn Stretow</string>
|
||||
<string name="pref_tts_language_title">Tavas Lev</string>
|
||||
<string name="pref_tts_test_voice_title">Prov Tuyow Dre Lev</string>
|
||||
<string name="pref_tts_unavailable">Nakavadow</string>
|
||||
<string name="search_show_on_map">Gweles war mappa</string>
|
||||
<string name="menu">Rol</string>
|
||||
<string name="website">Gwiasva</string>
|
||||
<string name="news">Nowodhow</string>
|
||||
<string name="panoramax">Imach Panoramax</string>
|
||||
<string name="feedback">Dasliv</string>
|
||||
<string name="rate_the_app">Breusyans an app</string>
|
||||
<string name="help">Gweres</string>
|
||||
<string name="faq">Govynnow Kemmyn</string>
|
||||
<string name="donate">Argevri</string>
|
||||
<string name="copyright">Gwirbryntyan</string>
|
||||
<string name="report_a_bug">Reportya unn kudyn</string>
|
||||
<string name="downloader_update_all_button">Nowedhi Oll</string>
|
||||
<string name="downloader_cancel_all">Dilea Oll</string>
|
||||
<string name="downloader_near_me_subtitle">Dhe dhorn</string>
|
||||
<string name="downloader_status_maps">Mappys</string>
|
||||
<string name="downloader_download_all_button">Iskarga Oll</string>
|
||||
<string name="downloader_download_map">Iskarga mappa</string>
|
||||
<string name="downloader_retry">Hwilewgh arta</string>
|
||||
<string name="downloader_delete_map">Dilea Mappa</string>
|
||||
<string name="downloader_update_map">Nowedhi Mappa</string>
|
||||
<string name="google_play_services">Servis GPS gans Google</string>
|
||||
<string name="routing_not_enough_space">Nyns eus lowr dalghuster</string>
|
||||
<string name="enable_location_services">Gallosegi Servisyow GPS mar pleg</string>
|
||||
<string name="create">Gul</string>
|
||||
<string name="red">Rudh</string>
|
||||
<string name="yellow">Melyn</string>
|
||||
<string name="blue">Blou</string>
|
||||
<string name="green">Gwyrdh</string>
|
||||
<string name="purple">Purpur</string>
|
||||
<string name="orange">Melynrudh</string>
|
||||
<string name="brown">Godhlos</string>
|
||||
<string name="pink">Kigliw</string>
|
||||
<string name="deep_purple">Rudhlas</string>
|
||||
<string name="light_blue">Glaswyn</string>
|
||||
<string name="teal">Gwerlas</string>
|
||||
<string name="lime">Gwerwyn</string>
|
||||
<string name="deep_orange">Rudhvelyn</string>
|
||||
<string name="gray">Loos</string>
|
||||
<string name="blue_gray">Glas</string>
|
||||
<string name="dialog_routing_check_gps">Checkya sinell GPS</string>
|
||||
<string name="dialog_routing_location_turn_on">Gallosegi servisyow GPS</string>
|
||||
<string name="dialog_routing_change_end">Desedha destnans</string>
|
||||
<string name="dialog_routing_system_error">Error gans an kevreyth</string>
|
||||
<string name="dialog_routing_try_again">Assayewgh arta mar pleg</string>
|
||||
<string name="not_now">Na Vynnav Lemmyn</string>
|
||||
<string name="search_select_map">Dewis Mappa</string>
|
||||
<string name="show">Showya</string>
|
||||
<string name="hide">Kudha</string>
|
||||
<string name="categories">Klassow</string>
|
||||
<string name="history">Istori</string>
|
||||
<string name="search_history_title">Istori Hwilas</string>
|
||||
<string name="clear_search">Dilea Istori Hwilas</string>
|
||||
<string name="read_in_wikipedia">Wikipedya</string>
|
||||
<string name="wikimedia_commons">Wikimedia Commons</string>
|
||||
<string name="p2p_your_location">Agas Tyller</string>
|
||||
<string name="p2p_start">Dalleth</string>
|
||||
<string name="next_button">Nessa</string>
|
||||
<string name="editor_time_add">Addya Rol Vetyansow</string>
|
||||
<string name="editor_time_delete">Dilea Rol Vetyansow</string>
|
||||
<string name="editor_time_allday">Dres an Jorna (24 ourys)</string>
|
||||
<string name="editor_time_open">Ygor</string>
|
||||
<string name="editor_time_close">Deges</string>
|
||||
<string name="editor_time_add_closed">Addya Ourys Nanegys</string>
|
||||
<string name="editor_time_title">Ourys Negys</string>
|
||||
<string name="editor_time_advanced">Fordh Avonsys</string>
|
||||
<string name="editor_time_simple">Fordh Sempel</string>
|
||||
<string name="editor_hours_closed">Ourys Nanegys</string>
|
||||
<string name="editor_correct_mistake">Ewna kammgemeryans</string>
|
||||
<string name="editor_add_select_location">Dewis Tyller</string>
|
||||
<string name="editor_report_problem_send_button">Danvon</string>
|
||||
<string name="editor_report_problem_title">Kudyn</string>
|
||||
<string name="daily">Dedhyek</string>
|
||||
<string name="twentyfour_seven">24/7</string>
|
||||
<string name="day_off_today">Deges hedhyw</string>
|
||||
<string name="day_off">Deges</string>
|
||||
<string name="closed">Deges</string>
|
||||
<string name="edit_opening_hours">Golegi ourys negys</string>
|
||||
<string name="no_osm_account">Vyth akont OpenStreetMap?</string>
|
||||
<string name="register_at_openstreetmap">Kovskrifa orth OpenStreetMap</string>
|
||||
<string name="login">Omgelmi</string>
|
||||
<string name="login_osm">Omgelmi dhe OpenStreetMap</string>
|
||||
<string name="logout">Omdenna</string>
|
||||
<string name="edit_place">Golegi Le</string>
|
||||
<string name="add_language">Addya unn tavas</string>
|
||||
<string name="street">Stret</string>
|
||||
<string name="house_number">Niver drehevyans</string>
|
||||
<string name="details">Manylyon</string>
|
||||
<string name="social_media">Media Socyal</string>
|
||||
<string name="building">Drehevyans</string>
|
||||
<string name="add_street">Addya unn stret</string>
|
||||
<string name="empty_street_name_error">Ynworra unn hanow street mar pleg</string>
|
||||
<string name="choose_language">Dewis unn tavas</string>
|
||||
<string name="choose_street">Dewis unn stret</string>
|
||||
<string name="editor_add_phone">Addya Niver Fon</string>
|
||||
<string name="level">Leur</string>
|
||||
<string name="level_value_generic">Leur: %s</string>
|
||||
<string name="downloader_update_maps">Nowedhi Mappys</string>
|
||||
<string name="downloader_search_field_hint">Kavos mappa</string>
|
||||
<string name="editor_login_error_dialog">Error dres omgelmi</string>
|
||||
<string name="editor_edit_place_name_hint">Hanow a an le</string>
|
||||
<string name="editor_default_language_hint">Dell yw skrifys yn an tavas teythiek</string>
|
||||
<string name="editor_edit_place_category_title">Klass</string>
|
||||
<string name="detailed_problem_description">Deskrifans manylys a an kudyn</string>
|
||||
<string name="editor_report_problem_other_title">Kudyn dyffrans</string>
|
||||
<string name="download_over_mobile_header">Iskarga dres unn kevren klapkodh?</string>
|
||||
<string name="error_enter_correct_house_number">Ynworra unn niver trigva ewn</string>
|
||||
<string name="editor_building_levels">Leur (ow komprehendya leur a-woles, a-der to)</string>
|
||||
<string name="editor_zip_code">Kod Post</string>
|
||||
<string name="error_enter_correct_zip_code">Ynworra unn kod post ewn</string>
|
||||
<string name="editor_other_info">Noten dhe bodhogyon OpenStreetMap (dre dhewis)</string>
|
||||
<string name="editor_more_about_osm">Moy a-dro OpenStreetMap</string>
|
||||
<string name="editor_osm_history">Agas istori golegi</string>
|
||||
<string name="editor_osm_notes">Agas notennow mappow</string>
|
||||
<string name="editor_operator">Oberador</string>
|
||||
<string name="operator">Oberador: %s</string>
|
||||
<string name="kilometers_per_hour">kdo</string>
|
||||
<string name="miles_per_hour">mdo</string>
|
||||
<string name="hour">o</string>
|
||||
<string name="minute">mynysen</string>
|
||||
<string name="placepage_more_button">Moy</string>
|
||||
<string name="placepage_edit_bookmark_button">Pennskrifa Lyververk</string>
|
||||
<string name="placepage_personal_notes_hint">Notennow privedh (tekst bo html)</string>
|
||||
<string name="editor_reset_edits_message">Dilea oll chanjyow dhywarlinen?</string>
|
||||
<string name="editor_reset_edits_button">Dilea</string>
|
||||
<string name="editor_remove_place_message">Dilea le keworrys?</string>
|
||||
<string name="editor_remove_place_button">Dilea</string>
|
||||
<string name="editor_place_doesnt_exist">Le na eksistya</string>
|
||||
<string name="delete_place_empty_comment_error">Provia an acheson rag dilea an le mar pleg</string>
|
||||
<string name="editor_business_vacant_button">Negys usi gwag</string>
|
||||
<string name="editor_mark_business_vacant_title">Sin negys dell gwag</string>
|
||||
<string name="editor_submit">Danvon</string>
|
||||
<string name="error_enter_correct_phone">Ynworra unn niver fon ewn</string>
|
||||
<string name="error_enter_correct_web">Ynworra unn trigva gwiasva ewn</string>
|
||||
<string name="error_enter_correct_email">Ynworra unn trigva ebost ewn</string>
|
||||
<string name="editor_level">Leur (0 usi leur a-woles)</string>
|
||||
<string name="error_enter_correct_level">Ynworra unn niver leur ewn</string>
|
||||
<string name="error_enter_correct_facebook_page">Ynworra unn trigva Lyverenep, akont, bo hanow folen ewn</string>
|
||||
<string name="error_enter_correct_instagram_page">Ynworra unn hanow usyer Instagram bo trigva gwiasva ewn</string>
|
||||
<string name="error_enter_correct_twitter_page">Ynworra unn hanow usyer Twytter bo trigva gwiasva ewn</string>
|
||||
<string name="error_enter_correct_vk_page">Ynworra unn hanow usyer VK bo trigva gwiasva ewn</string>
|
||||
<string name="error_enter_correct_line_page">Ynworra unn ID LINE bo trigva gwiasva ewn</string>
|
||||
<string name="error_enter_correct_fediverse_page">Ynworra unn hanow usyer Mastodon bo trigva gwiasva ewn</string>
|
||||
<string name="error_enter_correct_bluesky_page">Ynworra unn hanow usyer Bluesky bo trigva gwiasva ewn</string>
|
||||
<string name="placepage_add_place_button">Addya Le dhe OpenStreetMap</string>
|
||||
<string name="osm_note_hint">Bo, yn tereylys, skrifa unn noten dhe kemeneth OpenStreetMap, ha orth nebonan yll addya bo ewnhe unn le omma.</string>
|
||||
<string name="osm_note_toast">Noten a vydh danvon dhe OpenStreetMap</string>
|
||||
<string name="editor_share_to_all_dialog_title">A vynn\'ta mos danvon dhe oll usyoryon?</string>
|
||||
<string name="navigation_stop_button">Hedhi</string>
|
||||
<string name="track_recording">Y\'n eur ma rekordya an lergh</string>
|
||||
<string name="accept">Amyttya</string>
|
||||
<string name="decline">Nagha</string>
|
||||
<string name="mobile_data_option_today">Unn Hedhyw</string>
|
||||
<string name="traffic_update_app">Nowedhi CoMaps mar pleg</string>
|
||||
<string name="feedback_general">Dasliv Mayni</string>
|
||||
<string name="learn_more">Dyski moy</string>
|
||||
<string name="button_plan">Devisya</string>
|
||||
<string name="placepage_remove_stop">Dilea Powes</string>
|
||||
<string name="placepage_add_stop">Addya Powes</string>
|
||||
<string name="error_enter_correct_name">Ynworra unn hanow ewn mar pleg</string>
|
||||
<string name="bookmark_lists">Rolyow</string>
|
||||
<string name="bookmark_lists_hide_all">Keles oll</string>
|
||||
<string name="bookmark_lists_show_all">Diskwedhes oll</string>
|
||||
<string name="bookmarks_create_new_group">Gul unn rol nowydh</string>
|
||||
<string name="bookmarks_import">Ynperthi Lyververkys ha Lerghow</string>
|
||||
<string name="bookmarks_error_message_empty_list_name">Ynworra an hanow rol mar pleg</string>
|
||||
<string name="bookmarks_new_list_hint">Rol nowydh</string>
|
||||
<string name="bookmarks_error_title_list_name_already_taken">An hanow ma usi seulabrys yn usyans</string>
|
||||
<string name="bookmarks_error_message_list_name_already_taken">Dewis aral hanow mar pleg</string>
|
||||
<string name="please_wait">Gortos mar pleg…</string>
|
||||
<string name="phone_number">Niver fon</string>
|
||||
<string name="profile">Akont OpenStreetMap</string>
|
||||
<string name="restore">Astiveri</string>
|
||||
<string name="privacy">Privetter</string>
|
||||
<string name="privacy_policy">Polici privetter</string>
|
||||
<string name="power_management">Dyghtyans batri</string>
|
||||
<string name="terms_of_use">Termys usadow</string>
|
||||
<string name="button_layer_traffic">Data daromdak</string>
|
||||
<string name="subway">Metro</string>
|
||||
<string name="layers_title">Gisyow ha gwiskasow mappow</string>
|
||||
<string name="bookmarks_empty_list_title">An rol ma usi gwag</string>
|
||||
<string name="category_desc_more">…moy</string>
|
||||
<string name="export_file">Esperthi KMZ</string>
|
||||
<string name="export_file_gpx">Esperthi GPX</string>
|
||||
<string name="delete_list">Dilea rol</string>
|
||||
<string name="speedcams_alert_title">Kameras finweth tooth</string>
|
||||
<string name="place_description_title">Deskrifans Le</string>
|
||||
<string name="pref_tts_speedcams_auto">Gwarnya a tothya</string>
|
||||
<string name="define_to_avoid_btn">Styrya fordhow rag avodya</string>
|
||||
<string name="toll_road">Tollfordh</string>
|
||||
<string name="unpaved_road">Fordh dor</string>
|
||||
<string name="ferry_crossing">Kowbalhyns</string>
|
||||
<string name="yes">Ya</string>
|
||||
<string name="no">Na</string>
|
||||
<string name="yes_available">Ya</string>
|
||||
<string name="no_available">Na</string>
|
||||
<string name="capacity">Dalghuster: %s</string>
|
||||
<string name="network">Rosweyth: %s</string>
|
||||
<string name="trip_finished">Hwi re wrug!</string>
|
||||
<string name="ok">Da lowr</string>
|
||||
<string name="sort">Sortya…</string>
|
||||
<string name="sort_bookmarks">Sortya lyververkys</string>
|
||||
<string name="by_default">Dre defowt</string>
|
||||
<string name="by_type">Dre eghen</string>
|
||||
<string name="by_distance">Dre pellder</string>
|
||||
<string name="by_date">Dre dydh</string>
|
||||
<string name="by_name">Dre hanow</string>
|
||||
<string name="near_me_sorttype">Dhe dhorn</string>
|
||||
<string name="others_sorttype">Re erel</string>
|
||||
<string name="food_places">Boos</string>
|
||||
<string name="tourist_places">Tornyaseth</string>
|
||||
<string name="museums">Gwithtiow</string>
|
||||
<string name="parks">Parkow</string>
|
||||
<string name="swim_places">Neuvya</string>
|
||||
<string name="mountains">Menydhyow</string>
|
||||
<string name="animals">Bestes</string>
|
||||
<string name="hotels">Ostelyow</string>
|
||||
<string name="buildings">Drehevyansow</string>
|
||||
<string name="money">Mona</string>
|
||||
<string name="shops">Gwerthjiow</string>
|
||||
<string name="parkings">Parkyans</string>
|
||||
<string name="fuel_places">Petrolva</string>
|
||||
<string name="medicine">Medhegneth</string>
|
||||
<string name="search_in_the_list">Hwilas yn an rol</string>
|
||||
<string name="religious_places">Leow kryjyk</string>
|
||||
<string name="select_list">Dewis rol</string>
|
||||
<string name="transit_not_found">Navygacyon metro yn an pow ma usi nakavadow hwath</string>
|
||||
<string name="button_layer_isolines">Linen kettres</string>
|
||||
<string name="elevation_profile_min_elevation">Ughelder ispoyntel</string>
|
||||
<string name="elevation_profile_max_elevation">Ughelder ughboyntel</string>
|
||||
<string name="elevation_profile_difficulty">Kaletter</string>
|
||||
<string name="elevation_profile_distance">Pellder:</string>
|
||||
<string name="elevation_profile_time">Prys:</string>
|
||||
<string name="isolines_toast_zooms_1_10">Moghhe dhe furya linen kettres</string>
|
||||
<string name="download_map_title">Iskarga an mappa bys simpel</string>
|
||||
<string name="disk_error_title">Error plasen</string>
|
||||
<string name="connection_failure">Defowt kevren</string>
|
||||
<string name="change_map_locale">Tavas mappow</string>
|
||||
<string name="splash_subtitle">Data mappow a OpenStreetMap</string>
|
||||
<string name="telegram_url">https://t.me/CoMapsApp/</string>
|
||||
<string name="instagram_url">https://www.instagram.com/comaps.app/</string>
|
||||
<string name="tts_info_link">https://www.comaps.app/support/tts-configuration-guide-for-android/</string>
|
||||
<string name="osm_wiki_about_url">https://wiki.openstreetmap.org/wiki/About_OpenStreetMap</string>
|
||||
<string name="comma_separated_pair">%1$s, %2$s</string>
|
||||
<string name="app_tip_00">Meur ras rag usya a\'gan mappys drehevys gans an kemeneth!</string>
|
||||
<string name="app_tip_01">Gans agas argevrohow ha skoodhyans, ni a yll gul an mappys an gwella yn an Nor!</string>
|
||||
<string name="app_tip_02">Yw da genes a\'gan app? Argevri mar pleg dhe skoodhya an displegyans! Yw na da genes a\'n hwath? Mar pleg, kedhlewgh dhyn prag ha ni a ewnhe a\'n!</string>
|
||||
<string name="app_tip_06">Hwi a yll gweres treylya a\'gan app yn agas tavas.</string>
|
||||
<string name="aa_connected_title">Dha vos lemmyn rosweythys dhe Android Auto</string>
|
||||
<string name="aa_grant_permissions">Grontya grontys</string>
|
||||
<string name="button_layer_outdoor">Yn-mes</string>
|
||||
<string name="browser_not_available">Peurell nakavadow</string>
|
||||
<string name="volume">Ughelder</string>
|
||||
<string name="bookmarks_export">Esperthi oll Lyververkys ha Lerghow</string>
|
||||
<string name="clear_the_search">Dilea an hwithrans</string>
|
||||
<string name="zoom_in">Moghhe</string>
|
||||
<string name="zoom_out">Lehe</string>
|
||||
<string name="website_menu">Kevren Rol Voos</string>
|
||||
<string name="view_menu">Vu Rol Voos</string>
|
||||
<string name="open_in_app">Ygeri yn Aral App</string>
|
||||
<string name="select_option">Dewis dewis</string>
|
||||
<string name="outdoor_seating">Esedhow yn-mes</string>
|
||||
<string name="start_track_recording">Rekordya Lergh</string>
|
||||
<string name="choose_color">Dewis Kolor</string>
|
||||
<string name="edit_track">Golegi Lergh</string>
|
||||
<string name="nav_auto">Awtomatek yn navygacyon</string>
|
||||
<string name="pref_left_button_title">Boton kledh dewisyow</string>
|
||||
<string name="backup_interval_every_day">Dedhyek</string>
|
||||
<string name="backup_interval_every_week">Seythennyek</string>
|
||||
<string name="clear">Dilea</string>
|
||||
<string name="vehicle">Karr</string>
|
||||
<string name="pedestrian">Kerdher</string>
|
||||
<string name="bicycle">Diwros</string>
|
||||
<string name="ruler">Rewlell</string>
|
||||
<string name="bookmark_color">Kolor lyververk</string>
|
||||
<string name="about_help">A-dro ha Gweres</string>
|
||||
<string name="share_track">Kevren Lergh</string>
|
||||
<string name="delete_track_dialog_title">Dilea %s?</string>
|
||||
<string name="unknown_power_output">ankoth</string>
|
||||
<string name="charge_socket_type2">Eghen diw (na fun)</string>
|
||||
<string name="charge_socket_type2_cable">Eghen diw (gans fun)</string>
|
||||
<string name="charge_socket_type2_combo">Eghen diw kesunyans</string>
|
||||
<string name="charge_socket_type1">Eghen unn</string>
|
||||
<string name="charge_socket_nacs">NACS</string>
|
||||
<string name="charge_socket_chademo">CHAdeMO</string>
|
||||
<string name="charge_socket_schuko">Unyans Europek a-bervedh</string>
|
||||
<string name="unknown_socket_type">ankoth kraw</string>
|
||||
<string name="edit_socket_info_tooltip">Gul krawyow nowydh bo golegi krowyow a‑lemmyn.</string>
|
||||
<string name="charging_station_available_sockets">Krawyow kavadow</string>
|
||||
<string name="charge_socket_unknown_other">Aral bo anokth</string>
|
||||
<string name="charge_socket_count">Somm</string>
|
||||
<string name="charge_socket_power">Gallosedh (kW)</string>
|
||||
<string name="editor_socket">Golegi kraw</string>
|
||||
<string name="unknown_count">ankoth</string>
|
||||
<string name="error_value_must_be_positive">Res an niver positiv</string>
|
||||
<string name="error_invalid_number">Niver anewn</string>
|
||||
<string name="offline_explanation_title">Mappys Dhywarlinen</string>
|
||||
</resources>
|
||||
@@ -896,4 +896,7 @@
|
||||
<string name="editor_submit">Send</string>
|
||||
<string name="charge_socket_schuko">Schuko</string>
|
||||
<string name="power_management">Strømstyring</string>
|
||||
<string name="navigation_start_tts_disabled_message">Taleveiledninger deaktivert: TTS-motor ikke tilgjengelig</string>
|
||||
<string name="navigation_start_tts_message">"Starter navigering, taleveiledningsspråk: "</string>
|
||||
<string name="prefs_speed_cameras_information">Fotoboksvarsler er deaktivert i land hvor varsler er forbudt i henhold til lokal lovgivning.</string>
|
||||
</resources>
|
||||
|
||||
@@ -909,4 +909,5 @@
|
||||
<string name="charge_socket_schuko">Binnenlandse EU</string>
|
||||
<string name="navigation_start_tts_message">"Navigatie starten, taal voor steminstructie: "</string>
|
||||
<string name="prefs_speed_cameras_information">Waarschuwingen voor flitsers zijn uitgeschakeld in landen waar deze wettelijk zijn verboden.</string>
|
||||
<string name="navigation_start_tts_disabled_message">Spraakinstructies uitgeschakeld: TTS-engine niet beschikbaar</string>
|
||||
</resources>
|
||||
|
||||
@@ -915,4 +915,7 @@
|
||||
<string name="pref_tts_no_system_tts_short">Nie znaleziono silnika text-to-speech, sprawdź ustawienia aplikacji</string>
|
||||
<string name="list_description_empty">Edytuj listę, aby dodać opis</string>
|
||||
<string name="editor_submit">Wyślij</string>
|
||||
<string name="prefs_speed_cameras_information">Ostrzeżenia o fotoradarach są wyłączone w krajach, gdzie takie alerty są zabronione prawnie.</string>
|
||||
<string name="navigation_start_tts_message">"Startowanie nawigacji, język komunikatów głosowych: "</string>
|
||||
<string name="navigation_start_tts_disabled_message">Komunikaty głosowe są wyłączone: silnik TTS jest niedostępny</string>
|
||||
</resources>
|
||||
|
||||
@@ -911,4 +911,5 @@
|
||||
<string name="charge_socket_schuko">欧盟标准</string>
|
||||
<string name="navigation_start_tts_message">"启动导航,语音指令语言: "</string>
|
||||
<string name="prefs_speed_cameras_information">在当地法律禁止发出警报的国家/地区,测速摄像头警告功能将被禁用。</string>
|
||||
<string name="navigation_start_tts_disabled_message">语音指令已禁用:TTS 引擎不可用</string>
|
||||
</resources>
|
||||
|
||||
@@ -35,6 +35,11 @@
|
||||
<string name="pref_settings_general" translatable="false">GeneralSettings</string>
|
||||
<string name="pref_navigation" translatable="false">Navigation</string>
|
||||
<string name="pref_information" translatable="false">Information</string>
|
||||
<string name="pref_traffic" translatable="false">Traffic</string>
|
||||
<string name="pref_traffic_http_enabled" translatable="false">TrafficHttpEnabled</string>
|
||||
<string name="pref_traffic_http_url" translatable="false">TrafficHttpUrl</string>
|
||||
<string name="pref_traffic_apps" translatable="false">TrafficApps</string>
|
||||
<string name="pref_traffic_legacy_enabled" translatable="false">TrafficLegacyEnabled</string>
|
||||
<string name="pref_transliteration" translatable="false">Transliteration</string>
|
||||
<string name="pref_power_management" translatable="false">PowerManagment</string>
|
||||
<string name="pref_keep_screen_on" translatable="false">KeepScreenOn</string>
|
||||
|
||||
@@ -215,6 +215,7 @@
|
||||
<!-- Settings information group in settings screen -->
|
||||
<string name="prefs_group_information">Information</string>
|
||||
<string name="prefs_group_route">Navigation</string>
|
||||
<string name="prefs_group_traffic">Traffic information</string>
|
||||
<string name="pref_zoom_title">Zoom buttons</string>
|
||||
<string name="pref_zoom_summary">Display on the map</string>
|
||||
<!-- Settings «Map» category: «Night style» title -->
|
||||
@@ -789,6 +790,24 @@
|
||||
<string name="enable_show_on_lock_screen_description">When enabled, the app will work on the lockscreen even when the device is locked.</string>
|
||||
<!-- Current language of the map! -->
|
||||
<string name="change_map_locale">Map language</string>
|
||||
<!-- Enable live traffic data via HTTP (title) -->
|
||||
<string name="traffic_http_enabled">Enable live traffic data</string>
|
||||
<!-- Enable live traffic data via HTTP (description) -->
|
||||
<string name="traffic_http_enabled_description">When enabled, the app will periodically retrieve traffic information from the configured URL.</string>
|
||||
<!-- URL for live traffic data -->
|
||||
<string name="traffic_http_url">Traffic service URL</string>
|
||||
<!-- Status message indicating that user did not set a traffic URL yet. -->
|
||||
<string name="traffic_http_url_not_set">Not set</string>
|
||||
<!-- TraFF 0.8 apps from which to receive data (title) -->
|
||||
<string name="traffic_apps">Use data from TraFF applications</string>
|
||||
<!-- Status message indicating that no TraFF 0.8 apps are installed -->
|
||||
<string name="traffic_apps_not_available">No apps installed</string>
|
||||
<!-- Status message indicating that no TraFF 0.8 apps are currently selected -->
|
||||
<string name="traffic_apps_none_selected">No apps salected</string>
|
||||
<!-- Enable traffic data from TraFF 0.7 apps (title) -->
|
||||
<string name="traffic_legacy_enabled">Use data from legacy TraFF applications</string>
|
||||
<!-- Enable traffic data from TraFF 0.7 apps (description) -->
|
||||
<string name="traffic_legacy_enabled_description">When enabled, the app will receive and process traffic data from legacy TraFF applications.</string>
|
||||
<!-- OpenStreetMap text on splash screen -->
|
||||
<string name="splash_subtitle">Map data from OpenStreetMap</string>
|
||||
<!-- Telegram group url for the "?" About page -->
|
||||
|
||||
@@ -191,6 +191,36 @@
|
||||
android:widgetLayout="@layout/preference_switch"
|
||||
android:order="5"/>
|
||||
</PreferenceCategory>
|
||||
<androidx.preference.PreferenceCategory
|
||||
android:key="@string/pref_traffic"
|
||||
android:title="@string/prefs_group_traffic"
|
||||
android:order="4">
|
||||
<SwitchPreferenceCompat
|
||||
android:key="@string/pref_traffic_http_enabled"
|
||||
android:title="@string/traffic_http_enabled"
|
||||
app:singleLineTitle="false"
|
||||
android:summary="@string/traffic_http_enabled_description"
|
||||
android:defaultValue="true"
|
||||
android:order="1"/>
|
||||
<EditTextPreference
|
||||
android:key="@string/pref_traffic_http_url"
|
||||
android:title="@string/traffic_http_url"
|
||||
app:singleLineTitle="false"
|
||||
android:order="2"/>
|
||||
<MultiSelectListPreference
|
||||
android:key="@string/pref_traffic_apps"
|
||||
android:title="@string/traffic_apps"
|
||||
app:singleLineTitle="false"
|
||||
android:order="3"/>
|
||||
<SwitchPreferenceCompat
|
||||
android:key="@string/pref_traffic_legacy_enabled"
|
||||
android:title="@string/traffic_legacy_enabled"
|
||||
app:singleLineTitle="false"
|
||||
android:summary="@string/traffic_legacy_enabled_description"
|
||||
android:defaultValue="true"
|
||||
android:order="4"/>
|
||||
</androidx.preference.PreferenceCategory>
|
||||
|
||||
<androidx.preference.PreferenceCategory
|
||||
android:key="@string/pref_privacy"
|
||||
android:title="@string/privacy"
|
||||
|
||||
@@ -17,6 +17,7 @@ set(SRC
|
||||
app/organicmaps/sdk/opengl/gl3stub.h
|
||||
app/organicmaps/sdk/platform/GuiThread.hpp
|
||||
app/organicmaps/sdk/platform/AndroidPlatform.hpp
|
||||
app/organicmaps/sdk/traffxml/AndroidTraffSource.hpp
|
||||
app/organicmaps/sdk/util/Distance.hpp
|
||||
app/organicmaps/sdk/util/FeatureIdBuilder.hpp
|
||||
app/organicmaps/sdk/vulkan/android_vulkan_context_factory.hpp
|
||||
@@ -76,6 +77,8 @@ set(SRC
|
||||
app/organicmaps/sdk/platform/PThreadImpl.cpp
|
||||
app/organicmaps/sdk/platform/SecureStorage.cpp
|
||||
app/organicmaps/sdk/platform/SocketImpl.cpp
|
||||
app/organicmaps/sdk/traffxml/AndroidTraffSource.cpp
|
||||
app/organicmaps/sdk/traffxml/SourceImpl.cpp
|
||||
app/organicmaps/sdk/util/Config.cpp
|
||||
app/organicmaps/sdk/util/GeoUtils.cpp
|
||||
app/organicmaps/sdk/util/HttpClient.cpp
|
||||
@@ -127,6 +130,7 @@ target_link_libraries(${PROJECT_NAME}
|
||||
# icu
|
||||
# agg
|
||||
# vulkan_wrapper
|
||||
traffxml
|
||||
|
||||
# Android libs
|
||||
log
|
||||
|
||||
@@ -182,6 +182,8 @@ public:
|
||||
void Set3dMode(bool allow3d, bool allow3dBuildings);
|
||||
void Get3dMode(bool & allow3d, bool & allow3dBuildings);
|
||||
|
||||
TrafficManager & GetTrafficManager() { return m_work.GetTrafficManager(); }
|
||||
|
||||
void SetMapLanguageCode(std::string const & languageCode);
|
||||
std::string GetMapLanguageCode();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "app/organicmaps/sdk/Framework.hpp"
|
||||
|
||||
#include "app/organicmaps/sdk/platform/AndroidPlatform.hpp"
|
||||
#include "app/organicmaps/sdk/traffxml/AndroidTraffSource.hpp"
|
||||
|
||||
#include "app/organicmaps/sdk/core/jni_helper.hpp"
|
||||
|
||||
@@ -34,6 +35,26 @@ JNIEXPORT void JNICALL Java_app_organicmaps_sdk_OrganicMaps_nativeInitFramework(
|
||||
JNIEnv * env = jni::GetEnv();
|
||||
jmethodID const methodId = jni::GetMethodID(env, *onComplete, "run", "()V");
|
||||
env->CallVoidMethod(*onComplete, methodId);
|
||||
|
||||
ASSERT(g_framework, ("g_framework must be non-null"));
|
||||
|
||||
/*
|
||||
* Add traffic sources for Android.
|
||||
*/
|
||||
jclass configClass = env->FindClass("app/organicmaps/sdk/util/Config");
|
||||
jmethodID const getTrafficLegacyEnabledId = jni::GetStaticMethodID(env, configClass,
|
||||
"getTrafficLegacyEnabled", "()Z");
|
||||
jmethodID const applyTrafficLegacyEnabledId = jni::GetStaticMethodID(env, configClass,
|
||||
"applyTrafficLegacyEnabled", "(Z)V");
|
||||
jmethodID const getTrafficAppsId = jni::GetStaticMethodID(env, configClass,
|
||||
"getTrafficApps", "()[Ljava/lang/String;");
|
||||
jmethodID const applyTrafficAppsId = jni::GetStaticMethodID(env, configClass,
|
||||
"applyTrafficApps", "([Ljava/lang/String;)V");
|
||||
|
||||
env->CallStaticVoidMethod(configClass, applyTrafficLegacyEnabledId,
|
||||
env->CallStaticBooleanMethod(configClass, getTrafficLegacyEnabledId));
|
||||
env->CallStaticVoidMethod(configClass, applyTrafficAppsId,
|
||||
(jobjectArray)env->CallStaticObjectMethod(configClass, getTrafficAppsId));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
#include "AndroidTraffSource.hpp"
|
||||
|
||||
#include "app/organicmaps/sdk/core/jni_helper.hpp"
|
||||
|
||||
namespace traffxml {
|
||||
void AndroidTraffSourceV0_7::Create(TraffSourceManager & manager)
|
||||
{
|
||||
std::unique_ptr<AndroidTraffSourceV0_7> source = std::unique_ptr<AndroidTraffSourceV0_7>(new AndroidTraffSourceV0_7(manager));
|
||||
manager.RegisterSource(std::move(source));
|
||||
}
|
||||
|
||||
AndroidTraffSourceV0_7::AndroidTraffSourceV0_7(TraffSourceManager & manager)
|
||||
: TraffSource(manager)
|
||||
{
|
||||
JNIEnv * env = jni::GetEnv();
|
||||
|
||||
static jclass const implClass = jni::GetGlobalClassRef(env, "app/organicmaps/sdk/traffxml/SourceImplV0_7");
|
||||
|
||||
static jmethodID const implConstructor = jni::GetConstructorID(env, implClass, "(Landroid/content/Context;J)V");
|
||||
|
||||
jlong nativeManager = reinterpret_cast<jlong>(&manager);
|
||||
|
||||
jobject implObject = env->NewObject(
|
||||
implClass, implConstructor, android::Platform::Instance().GetContext(), nativeManager);
|
||||
|
||||
m_implObject = env->NewGlobalRef(implObject);
|
||||
|
||||
m_subscribeImpl = jni::GetMethodID(env, m_implObject, "subscribe", "(Ljava/lang/String;)V");
|
||||
m_unsubscribeImpl = jni::GetMethodID(env, m_implObject, "unsubscribe", "()V");
|
||||
}
|
||||
|
||||
AndroidTraffSourceV0_7::~AndroidTraffSourceV0_7()
|
||||
{
|
||||
jni::GetEnv()->DeleteGlobalRef(m_implObject);
|
||||
}
|
||||
|
||||
void AndroidTraffSourceV0_7::Close()
|
||||
{
|
||||
Unsubscribe();
|
||||
}
|
||||
|
||||
void AndroidTraffSourceV0_7::Subscribe(std::set<MwmSet::MwmId> & mwms)
|
||||
{
|
||||
jni::GetEnv()->CallVoidMethod(m_implObject, m_subscribeImpl, nullptr);
|
||||
}
|
||||
|
||||
void AndroidTraffSourceV0_7::Unsubscribe()
|
||||
{
|
||||
jni::GetEnv()->CallVoidMethod(m_implObject, m_unsubscribeImpl);
|
||||
}
|
||||
|
||||
void AndroidTraffSourceV0_8::Create(TraffSourceManager & manager, std::string const & packageId)
|
||||
{
|
||||
std::unique_ptr<AndroidTraffSourceV0_8> source = std::unique_ptr<AndroidTraffSourceV0_8>(new AndroidTraffSourceV0_8(manager, packageId));
|
||||
manager.RegisterSource(std::move(source));
|
||||
}
|
||||
|
||||
AndroidTraffSourceV0_8::AndroidTraffSourceV0_8(TraffSourceManager & manager, std::string const & packageId)
|
||||
: TraffSource(manager)
|
||||
{
|
||||
JNIEnv * env = jni::GetEnv();
|
||||
|
||||
static jclass const implClass = jni::GetGlobalClassRef(env, "app/organicmaps/sdk/traffxml/SourceImplV0_8");
|
||||
|
||||
static jmethodID const implConstructor = jni::GetConstructorID(env, implClass, "(Landroid/content/Context;JLjava/lang/String;)V");
|
||||
|
||||
jlong nativeManager = reinterpret_cast<jlong>(&manager);
|
||||
|
||||
jobject implObject = env->NewObject(
|
||||
implClass, implConstructor, android::Platform::Instance().GetContext(), nativeManager, jni::ToJavaString(env, packageId));
|
||||
|
||||
m_implObject = env->NewGlobalRef(implObject);
|
||||
|
||||
m_subscribeImpl = jni::GetMethodID(env, m_implObject, "subscribe", "(Ljava/lang/String;)V");
|
||||
m_changeSubscriptionImpl = jni::GetMethodID(env, m_implObject, "changeSubscription", "(Ljava/lang/String;)V");
|
||||
m_unsubscribeImpl = jni::GetMethodID(env, m_implObject, "unsubscribe", "()V");
|
||||
|
||||
// TODO packageId (if we need that at all here)
|
||||
}
|
||||
|
||||
AndroidTraffSourceV0_8::~AndroidTraffSourceV0_8()
|
||||
{
|
||||
jni::GetEnv()->DeleteGlobalRef(m_implObject);
|
||||
}
|
||||
|
||||
void AndroidTraffSourceV0_8::Close()
|
||||
{
|
||||
Unsubscribe();
|
||||
}
|
||||
|
||||
void AndroidTraffSourceV0_8::Subscribe(std::set<MwmSet::MwmId> & mwms)
|
||||
{
|
||||
JNIEnv * env = jni::GetEnv();
|
||||
std::string data = "<filter_list>\n"
|
||||
+ GetMwmFilters(mwms)
|
||||
+ "</filter_list>";
|
||||
|
||||
env->CallVoidMethod(m_implObject, m_subscribeImpl, jni::ToJavaString(env, data));
|
||||
}
|
||||
|
||||
void AndroidTraffSourceV0_8::ChangeSubscription(std::set<MwmSet::MwmId> & mwms)
|
||||
{
|
||||
JNIEnv * env = jni::GetEnv();
|
||||
std::string data = "<filter_list>\n"
|
||||
+ GetMwmFilters(mwms)
|
||||
+ "</filter_list>";
|
||||
|
||||
env->CallVoidMethod(m_implObject, m_changeSubscriptionImpl, jni::ToJavaString(env, data));
|
||||
}
|
||||
|
||||
void AndroidTraffSourceV0_8::Unsubscribe()
|
||||
{
|
||||
jni::GetEnv()->CallVoidMethod(m_implObject, m_unsubscribeImpl);
|
||||
}
|
||||
} // namespace traffxml
|
||||
@@ -0,0 +1,199 @@
|
||||
#pragma once
|
||||
|
||||
#include "traffxml/traff_source.hpp"
|
||||
|
||||
namespace traffxml
|
||||
{
|
||||
/**
|
||||
* @brief A TraFF source which relies on Android Binder for message delivery, using version 0.7 of the TraFF protocol.
|
||||
*
|
||||
* TraFF 0.7 does not support subscriptions. Messages are broadcast as the payload to a `FEED` intent.
|
||||
*/
|
||||
class AndroidTraffSourceV0_7 : public TraffSource
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a new `AndroidTraffSourceV0_7` instance and registers it with the traffic manager.
|
||||
*
|
||||
* @param manager The traffic manager to register the new instance with
|
||||
*/
|
||||
static void Create(TraffSourceManager & manager);
|
||||
|
||||
virtual ~AndroidTraffSourceV0_7() override;
|
||||
|
||||
/**
|
||||
* @brief Prepares the traffic source for unloading.
|
||||
*/
|
||||
// TODO do we need a close operation here?
|
||||
// TODO move this to the parent class and override it here?
|
||||
void Close();
|
||||
|
||||
/**
|
||||
* @brief Subscribes to a traffic service.
|
||||
*
|
||||
* TraFF 0.7 does not support subscriptions. This implementation registers a broadcast receiver.
|
||||
*
|
||||
* @param mwms The MWMs for which data is needed (not used by this implementation).
|
||||
*/
|
||||
virtual void Subscribe(std::set<MwmSet::MwmId> & mwms) override;
|
||||
|
||||
/**
|
||||
* @brief Changes an existing traffic subscription.
|
||||
*
|
||||
* This implementation does nothing, as TraFF 0.7 does not support subscriptions.
|
||||
*
|
||||
* @param mwms The new set of MWMs for which data is needed.
|
||||
*/
|
||||
virtual void ChangeSubscription(std::set<MwmSet::MwmId> & mwms) override {};
|
||||
|
||||
/**
|
||||
* @brief Unsubscribes from a traffic service we are subscribed to.
|
||||
*
|
||||
* TraFF 0.7 does not support subscriptions. This implementation unregisters the broadcast
|
||||
* receiver which was registered by `Subscribe()`.
|
||||
*/
|
||||
virtual void Unsubscribe() override;
|
||||
|
||||
/**
|
||||
* @brief Whether this source should be polled.
|
||||
*
|
||||
* Prior to calling `Poll()` on a source, the caller should always first call `IsPollNeeded()` and
|
||||
* poll the source only if the result is true.
|
||||
*
|
||||
* This implementation always returns false, as message delivery on Android uses `FEED` (push).
|
||||
*
|
||||
* @return true if the source should be polled, false if not.
|
||||
*/
|
||||
virtual bool IsPollNeeded() override { return false; };
|
||||
|
||||
/**
|
||||
* @brief Polls the traffic service for updates.
|
||||
*
|
||||
* This implementation does nothing, as message delivery on Android uses `FEED` (push).
|
||||
*/
|
||||
virtual void Poll() override {};
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Constructs a new `AndroidTraffSourceV0_7`.
|
||||
* @param manager The `TrafficSourceManager` instance to register the source with.
|
||||
*/
|
||||
AndroidTraffSourceV0_7(TraffSourceManager & manager);
|
||||
|
||||
private:
|
||||
// TODO “subscription” (i.e. broadcast receiver) state
|
||||
|
||||
/**
|
||||
* @brief The Java implementation class instance.
|
||||
*/
|
||||
jobject m_implObject;
|
||||
|
||||
/**
|
||||
* @brief The Java subscribe method.
|
||||
*/
|
||||
jmethodID m_subscribeImpl;
|
||||
|
||||
/**
|
||||
* @brief The Java unsubscribe method.
|
||||
*/
|
||||
jmethodID m_unsubscribeImpl;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A TraFF source which relies on Android Binder for message delivery, using version 0.8 of the TraFF protocol.
|
||||
*
|
||||
* TraFF 0.8 supports subscriptions. Messages are announced through a `FEED` intent, whereupon the
|
||||
* consumer can retrieve them from a content provider.
|
||||
*/
|
||||
class AndroidTraffSourceV0_8 : public TraffSource
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a new `AndroidTraffSourceV0_8` instance and registers it with the traffic manager.
|
||||
*
|
||||
* @param manager The traffic manager to register the new instance with
|
||||
* @param packageId The package ID of the app providing the TraFF source.
|
||||
*/
|
||||
static void Create(TraffSourceManager & manager, std::string const & packageId);
|
||||
|
||||
virtual ~AndroidTraffSourceV0_8() override;
|
||||
|
||||
/**
|
||||
* @brief Prepares the traffic source for unloading.
|
||||
*
|
||||
* If there is still an active subscription, it unsubscribes, but without processing the result
|
||||
* received from the service. Otherwise, teardown is a no-op.
|
||||
*/
|
||||
// TODO move this to the parent class and override it here?
|
||||
void Close();
|
||||
|
||||
/**
|
||||
* @brief Subscribes to a traffic service.
|
||||
*
|
||||
* @param mwms The MWMs for which data is needed.
|
||||
*/
|
||||
virtual void Subscribe(std::set<MwmSet::MwmId> & mwms) override;
|
||||
|
||||
/**
|
||||
* @brief Changes an existing traffic subscription.
|
||||
*
|
||||
* @param mwms The new set of MWMs for which data is needed.
|
||||
*/
|
||||
virtual void ChangeSubscription(std::set<MwmSet::MwmId> & mwms) override;
|
||||
|
||||
/**
|
||||
* @brief Unsubscribes from a traffic service we are subscribed to.
|
||||
*/
|
||||
virtual void Unsubscribe() override;
|
||||
|
||||
/**
|
||||
* @brief Whether this source should be polled.
|
||||
*
|
||||
* Prior to calling `Poll()` on a source, the caller should always first call `IsPollNeeded()` and
|
||||
* poll the source only if the result is true.
|
||||
*
|
||||
* This implementation always returns false, as message delivery on Android uses `FEED` (push).
|
||||
*
|
||||
* @return true if the source should be polled, false if not.
|
||||
*/
|
||||
virtual bool IsPollNeeded() override { return false; };
|
||||
|
||||
/**
|
||||
* @brief Polls the traffic service for updates.
|
||||
*
|
||||
* This implementation does nothing, as message delivery on Android uses `FEED` (push).
|
||||
*/
|
||||
virtual void Poll() override {};
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Constructs a new `AndroidTraffSourceV0_8`.
|
||||
* @param manager The `TrafficSourceManager` instance to register the source with.
|
||||
* @param packageId The package ID of the app providing the TraFF source.
|
||||
*/
|
||||
AndroidTraffSourceV0_8(TraffSourceManager & manager, std::string const & packageId);
|
||||
|
||||
private:
|
||||
// TODO subscription state
|
||||
|
||||
/**
|
||||
* @brief The Java implementation class instance.
|
||||
*/
|
||||
jobject m_implObject;
|
||||
|
||||
/**
|
||||
* @brief The Java subscribe method.
|
||||
*/
|
||||
jmethodID m_subscribeImpl;
|
||||
|
||||
/**
|
||||
* @brief The Java changeSubscription method.
|
||||
*/
|
||||
jmethodID m_changeSubscriptionImpl;
|
||||
|
||||
/**
|
||||
* @brief The Java unsubscribe method.
|
||||
*/
|
||||
jmethodID m_unsubscribeImpl;
|
||||
};
|
||||
} // namespace traffxml
|
||||
@@ -0,0 +1,34 @@
|
||||
// TODO which of the two do we need? (jni_helper includes jni)
|
||||
//#include <jni>
|
||||
#include "app/organicmaps/sdk/core/jni_helper.hpp"
|
||||
|
||||
#include "traffxml/traff_source.hpp"
|
||||
#include "traffxml/traff_model_xml.hpp"
|
||||
|
||||
#include <optional>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_sdk_traffxml_SourceImpl_onFeedReceivedImpl(JNIEnv * env, jclass thiz, jlong nativeManager, jstring feed)
|
||||
{
|
||||
std::string feedStd = jni::ToNativeString(env, feed);
|
||||
pugi::xml_document document;
|
||||
traffxml::TraffFeed parsedFeed;
|
||||
|
||||
if (!document.load_string(feedStd.c_str()))
|
||||
{
|
||||
LOG(LWARNING, ("Feed is not a well-formed XML document"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!traffxml::ParseTraff(document, std::nullopt, parsedFeed))
|
||||
{
|
||||
LOG(LWARNING, ("Feed is not a valid TraFF feed"));
|
||||
return;
|
||||
}
|
||||
|
||||
traffxml::TraffSourceManager & manager = *reinterpret_cast<traffxml::TraffSourceManager*>(nativeManager);
|
||||
manager.ReceiveFeed(parsedFeed);
|
||||
}
|
||||
}
|
||||
@@ -119,4 +119,74 @@ JNIEXPORT void JNICALL Java_app_organicmaps_sdk_util_Config_nativeSetTranslitera
|
||||
frm()->SaveTransliteration(value);
|
||||
frm()->AllowTransliteration(value);
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_app_organicmaps_sdk_util_Config_nativeGetTrafficHttpEnabled(JNIEnv * env, jclass thiz)
|
||||
{
|
||||
return frm()->LoadTrafficHttpEnabled();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_sdk_util_Config_nativeSetTrafficHttpEnabled(JNIEnv * env, jclass thiz,
|
||||
jboolean value)
|
||||
{
|
||||
frm()->SaveTrafficHttpEnabled(value);
|
||||
frm()->SetTrafficHttpEnabled(value);
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_app_organicmaps_sdk_util_Config_nativeGetTrafficHttpUrl(JNIEnv * env, jclass thiz)
|
||||
{
|
||||
std::string value = frm()->LoadTrafficHttpUrl();
|
||||
return jni::ToJavaString(env, value);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_sdk_util_Config_nativeSetTrafficHttpUrl(JNIEnv * env, jclass thiz,
|
||||
jstring value)
|
||||
{
|
||||
frm()->SaveTrafficHttpUrl(jni::ToNativeString(env, value));
|
||||
frm()->SetTrafficHttpUrl(jni::ToNativeString(env, value));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_sdk_util_Config_applyTrafficLegacyEnabled(JNIEnv * env, jclass thiz,
|
||||
jboolean value)
|
||||
{
|
||||
TrafficManager & tm = g_framework->GetTrafficManager();
|
||||
tm.RemoveTraffSourceIf([](traffxml::TraffSource* source) {
|
||||
if (traffxml::AndroidTraffSourceV0_7* traffSource = dynamic_cast<traffxml::AndroidTraffSourceV0_7*>(source))
|
||||
{
|
||||
traffSource->Close();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
});
|
||||
if (value)
|
||||
traffxml::AndroidTraffSourceV0_7::Create(tm);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_sdk_util_Config_applyTrafficApps(JNIEnv * env, jclass thiz, jobjectArray value)
|
||||
{
|
||||
jsize valueLen = env->GetArrayLength(value);
|
||||
TrafficManager & tm = g_framework->GetTrafficManager();
|
||||
tm.RemoveTraffSourceIf([](traffxml::TraffSource* source) {
|
||||
if (traffxml::AndroidTraffSourceV0_8* traffSource = dynamic_cast<traffxml::AndroidTraffSourceV0_8*>(source))
|
||||
{
|
||||
traffSource->Close();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
});
|
||||
for (jsize i = 0; i < valueLen; i++)
|
||||
{
|
||||
jstring jAppId = (jstring)env->GetObjectArrayElement(value, i);
|
||||
std::string appId = jni::ToNativeString(env, jAppId);
|
||||
traffxml::AndroidTraffSourceV0_8::Create(tm, appId);
|
||||
env->DeleteLocalRef(jAppId);
|
||||
}
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright © 2017–2020 traffxml.org.
|
||||
*
|
||||
* Relicensed to CoMaps by the original author.
|
||||
*/
|
||||
|
||||
package app.organicmaps.sdk.traffxml;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import app.organicmaps.sdk.traffxml.Version;
|
||||
import app.organicmaps.sdk.traffxml.AndroidTransport;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.IntentFilter.MalformedMimeTypeException;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class AndroidConsumer {
|
||||
/**
|
||||
* Creates an Intent filter which matches the Intents a TraFF consumer needs to receive.
|
||||
*
|
||||
* <p>Different filters are available for consumers implementing different versions of the TraFF
|
||||
* specification.
|
||||
*
|
||||
* @param version The version of the TraFF specification (one of the constants in {@link org.traffxml.traff.Version})
|
||||
*
|
||||
* @return An intent filter matching the necessary Intents
|
||||
*/
|
||||
public static IntentFilter createIntentFilter(int version) {
|
||||
IntentFilter res = new IntentFilter();
|
||||
switch (version) {
|
||||
case Version.V0_7:
|
||||
res.addAction(AndroidTransport.ACTION_TRAFF_PUSH);
|
||||
break;
|
||||
case Version.V0_8:
|
||||
res.addAction(AndroidTransport.ACTION_TRAFF_PUSH);
|
||||
res.addDataScheme(AndroidTransport.CONTENT_SCHEMA);
|
||||
try {
|
||||
res.addDataType(AndroidTransport.MIME_TYPE_TRAFF);
|
||||
} catch (MalformedMimeTypeException e) {
|
||||
// as long as the constant is a well-formed MIME type, this exception never gets thrown
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid version code: " + version);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a TraFF intent to a source.
|
||||
*
|
||||
* <p>This encapsulates most of the low-level Android handling.
|
||||
*
|
||||
* <p>If the recipient specified in {@code packageName} declares multiple receivers for the intent in its
|
||||
* manifest, a separate intent will be delivered to each of them. The intent will not be delivered to
|
||||
* receivers registered at runtime.
|
||||
*
|
||||
* <p>All intents are sent as explicit ordered broadcasts. This means two things:
|
||||
*
|
||||
* <p>Any app which declares a matching receiver in its manifest will be woken up to process the intent.
|
||||
* This works even with certain Android 7 builds which restrict intent delivery to apps which are not
|
||||
* currently running.
|
||||
*
|
||||
* <p>It is safe for the recipient to unconditionally set result data. If the recipient does not set
|
||||
* result data, the result will have a result code of
|
||||
* {@link org.traffxml.transport.android.AndroidTransport#RESULT_INTERNAL_ERROR}, no data and no extras.
|
||||
*
|
||||
* @param context The context
|
||||
* @param action The intent action.
|
||||
* @param data The intent data (for TraFF, this is the content provider URI), or null
|
||||
* @param extras The extras for the intent
|
||||
* @param packageName The package name for the intent recipient, or null to deliver the intent to all matching receivers
|
||||
* @param receiverPermission A permission which the recipient must hold, or null if not required
|
||||
* @param resultReceiver A BroadcastReceiver which will receive the result for the intent
|
||||
*/
|
||||
public static void sendTraffIntent(Context context, String action, Uri data, Bundle extras, String packageName,
|
||||
String receiverPermission, BroadcastReceiver resultReceiver) {
|
||||
Intent outIntent = new Intent(action);
|
||||
PackageManager pm = context.getPackageManager();
|
||||
List<ResolveInfo> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>This is a broadcast intent and must be sent as an explicit broadcast.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>This is a broadcast intent. It can be used in different forms:
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>This is a broadcast intent and must be sent as an explicit broadcast.
|
||||
*
|
||||
* <p>The filter list must be specified in the {@link #EXTRA_FILTER_LIST} extra.
|
||||
*
|
||||
* <p>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,
|
||||
*
|
||||
* <p>This is a broadcast intent and must be sent as an explicit broadcast.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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,
|
||||
*
|
||||
* <p>This is a broadcast intent and must be sent as an explicit broadcast.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>This is a String extra. It contains a {@code feed} XML element.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>This is a String extra.
|
||||
*/
|
||||
public static final String EXTRA_PACKAGE = "package";
|
||||
|
||||
/**
|
||||
* Extra which contains a subscription ID.
|
||||
*
|
||||
* <p>This is a String extra.
|
||||
*/
|
||||
public static final String EXTRA_SUBSCRIPTION_ID = "subscription_id";
|
||||
|
||||
/**
|
||||
* Extra which contains the timeout duration for a subscription.
|
||||
*
|
||||
* <p>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];
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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<ResolveInfo> receivers07 = pm.queryBroadcastReceivers(outIntent, 0);
|
||||
List<ResolveInfo> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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("<feed>\n");
|
||||
while (cursor.moveToNext())
|
||||
builder.append(cursor.getString(cursor.getColumnIndex(AndroidTransport.COLUMN_DATA))).append("\n");
|
||||
builder.append("</feed>");
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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() {}
|
||||
|
||||
@@ -393,6 +403,63 @@ public final class Config
|
||||
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()
|
||||
{
|
||||
@@ -536,4 +603,10 @@ public final class Config
|
||||
private static native void nativeSetLargeFontsSize(boolean 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);
|
||||
}
|
||||
|
||||
@@ -601,31 +601,29 @@
|
||||
<string name="type.railway.monorail">مونوریل</string>
|
||||
<string name="type.railway.rail">راه آهن</string>
|
||||
<string name="type.railway.rail.highspeed">راه آهن پرسرعت</string>
|
||||
<string name="type.railway.rail.tourism">راه آهن توریستی</string>
|
||||
<string name="type.railway.rail.main">راه آهن</string>
|
||||
<string name="type.railway.rail.tourism">راهآهن گردشگرانه</string>
|
||||
<string name="type.railway.rail.main">راهآهن</string>
|
||||
<!-- Includes ordinary railway=rail w/o more specific usage= and service= tags. -->
|
||||
<string name="type.railway.rail.branch">راه آهن ثانویه</string>
|
||||
<string name="type.railway.rail.branch">اَزگ راهآهن</string>
|
||||
<!-- Non-passenger utility tracks: industrial, military, test. -->
|
||||
<string name="type.railway.rail.utility">راه آهن شهری</string>
|
||||
<string name="type.railway.rail.spur">خار راه آهن</string>
|
||||
<!-- Short service tracks: siding, yard, crossover. -->
|
||||
<string name="type.railway.rail.service">ریل کمکی</string>
|
||||
<string name="type.railway.rail.bridge">پل راه آهن</string>
|
||||
<string name="type.railway.rail.highspeed.bridge">پل راه آهن</string>
|
||||
<string name="type.railway.rail.tourism.bridge">پل راه آهن</string>
|
||||
<string name="type.railway.rail.main.bridge">پل راه آهن</string>
|
||||
<string name="type.railway.rail.branch.bridge">پل راه آهن</string>
|
||||
<string name="type.railway.rail.utility.bridge">پل راه آهن</string>
|
||||
<string name="type.railway.rail.spur.bridge">پل راه آهن</string>
|
||||
<string name="type.railway.rail.service.bridge">پل راه آهن</string>
|
||||
<string name="type.railway.rail.tunnel">تونل راه آهن</string>
|
||||
<string name="type.railway.rail.highspeed.tunnel">تونل راه آهن</string>
|
||||
<string name="type.railway.rail.tourism.tunnel">تونل راه آهن</string>
|
||||
<string name="type.railway.rail.main.tunnel">تونل راه آهن</string>
|
||||
<string name="type.railway.rail.branch.tunnel">تونل راه آهن</string>
|
||||
<string name="type.railway.rail.utility.tunnel">تونل راه آهن</string>
|
||||
<string name="type.railway.rail.spur.tunnel">تونل راه آهن</string>
|
||||
<string name="type.railway.rail.service.tunnel">تونل راه آهن</string>
|
||||
<string name="type.railway.rail.utility">سامانه راهآهن برقی</string>
|
||||
<string name="type.railway.rail.spur">شاخک راهآهن</string>
|
||||
<string name="type.railway.rail.bridge">پل راهآهن</string>
|
||||
<string name="type.railway.rail.highspeed.bridge">پل راهآهن</string>
|
||||
<string name="type.railway.rail.tourism.bridge">پل راهآهن</string>
|
||||
<string name="type.railway.rail.main.bridge">پل راهآهن</string>
|
||||
<string name="type.railway.rail.branch.bridge">پل راهآهن</string>
|
||||
<string name="type.railway.rail.utility.bridge">پل راهآهن</string>
|
||||
<string name="type.railway.rail.spur.bridge">پل راهآهن</string>
|
||||
<string name="type.railway.rail.service.bridge">پل راهآهن</string>
|
||||
<string name="type.railway.rail.tunnel">آهون راهآهن</string>
|
||||
<string name="type.railway.rail.highspeed.tunnel">آهون راهآهن</string>
|
||||
<string name="type.railway.rail.tourism.tunnel">آهون راهآهن</string>
|
||||
<string name="type.railway.rail.main.tunnel">آهون راهآهن</string>
|
||||
<string name="type.railway.rail.branch.tunnel">آهون راهآهن</string>
|
||||
<string name="type.railway.rail.utility.tunnel">آهون راهآهن</string>
|
||||
<string name="type.railway.rail.spur.tunnel">آهون راهآهن</string>
|
||||
<string name="type.railway.rail.service.tunnel">آهون راهآهن</string>
|
||||
<string name="type.railway.station">ایستگاه قطار</string>
|
||||
<string name="type.railway.station.funicular">فونیکولور</string>
|
||||
<string name="type.railway.station.light_rail">ایستگاه قطار</string>
|
||||
@@ -1071,7 +1069,7 @@
|
||||
<string name="type.wheelchair.limited">دسترسی کرانمند با رواگصندلی</string>
|
||||
<string name="type.wheelchair.no">بدون دسترسی با صندلی چرخ دار</string>
|
||||
<string name="type.wheelchair.yes">دسترسی کامل با صندلی چرخ دار</string>
|
||||
<string name="type.piste_type.snow_park">پارک برفی</string>
|
||||
<string name="type.piste_type.snow_park">بوستان برفی</string>
|
||||
<string name="type.piste_type.hike">مسیر پیاده روی برفی</string>
|
||||
<string name="type.piste_type.connection">اتصال پیست</string>
|
||||
<string name="type.piste_type.skitour">مسیر اسکیتور</string>
|
||||
|
||||
212
android/sdk/src/main/res/values-kw/types_strings.xml
Normal file
212
android/sdk/src/main/res/values-kw/types_strings.xml
Normal file
@@ -0,0 +1,212 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="type.addr_interpolation">Trigva</string>
|
||||
<string name="type.addr_interpolation.even">Trigva</string>
|
||||
<string name="type.addr_interpolation.odd">Trigva</string>
|
||||
<string name="type.aerialway.chair_lift">Kador-yskynna</string>
|
||||
<string name="type.aeroway.aerodrome">Ayrborth</string>
|
||||
<string name="type.aeroway.aerodrome.international">Ayrborth Keswlasek</string>
|
||||
<string name="type.aeroway.apron">Ayrborth Apron</string>
|
||||
<string name="type.aeroway.gate">Ayrborth Porth</string>
|
||||
<string name="type.aeroway.helipad">Le Tirans Askel Dro</string>
|
||||
<string name="type.aeroway.runway">Hyns-tira</string>
|
||||
<string name="type.aeroway.terminal">Termynal</string>
|
||||
<string name="type.amenity.arts_centre">Kresen-art</string>
|
||||
<string name="type.amenity.atm">Toll y\'n Fos</string>
|
||||
<string name="type.amenity.bank">Arghanti</string>
|
||||
<string name="type.amenity.bar">Barr</string>
|
||||
<string name="type.amenity.bbq">Rastella Barbakoa</string>
|
||||
<string name="type.amenity.bench">Form</string>
|
||||
<string name="type.amenity.bicycle_parking">Parkyans Diwros</string>
|
||||
<string name="type.amenity.bicycle_parking.covered">Parkyans Kdhys Diwros</string>
|
||||
<string name="type.amenity.boat_rental">Arveth Skath</string>
|
||||
<string name="type.amenity.bicycle_rental">Arveth Diwros</string>
|
||||
<string name="type.amenity.bicycle_repair_station">Ewnansva Diwros</string>
|
||||
<string name="type.amenity.biergarten">Jardin Korev</string>
|
||||
<string name="type.amenity.brothel">Horji</string>
|
||||
<string name="type.amenity.love_hotel">Ostel Karnal</string>
|
||||
<string name="type.amenity.bureau_de_change">Keschanj Arghansedh</string>
|
||||
<string name="type.amenity.bus_station">Gorsav Kyttrin</string>
|
||||
<string name="type.amenity.cafe">Koffiji</string>
|
||||
<string name="type.amenity.car_rental">Arveth Karr</string>
|
||||
<string name="type.amenity.motorcycle_rental">Arveth Jynn Diwros</string>
|
||||
<string name="type.amenity.car_wash">Golghva Gerri</string>
|
||||
<string name="type.man_made.telescope">Pellweler</string>
|
||||
<string name="type.man_made.telescope.radio">Pellweler (Radyo)</string>
|
||||
<string name="type.man_made.observatory">Mirji</string>
|
||||
<string name="type.amenity.childcare">Skol-veythrin</string>
|
||||
<string name="type.amenity.cinema">Cinema</string>
|
||||
<string name="type.amenity.studio">Studhla Media</string>
|
||||
<string name="type.amenity.clinic">Medhegva</string>
|
||||
<string name="type.amenity.college">Kolji</string>
|
||||
<string name="type.amenity.community_centre">Kresen Gemeneth</string>
|
||||
<string name="type.amenity.conference_centre">Kresen Keskussulyans</string>
|
||||
<string name="type.amenity.courthouse">Stevel an Lys</string>
|
||||
<string name="type.amenity.dentist">Medhek Dens</string>
|
||||
<string name="type.amenity.doctors">Medhek</string>
|
||||
<string name="type.amenity.drinking_water">Dowr Evadow</string>
|
||||
<string name="type.drinking_water.yes">Dowr Evadow</string>
|
||||
<string name="type.amenity.exhibition_centre">Kresen Diskwedhyans</string>
|
||||
<string name="type.amenity.money_transfer">Treusworrans Mona</string>
|
||||
<string name="type.amenity.music_school">Skol-ilowek</string>
|
||||
<string name="type.amenity.flight_school">Skol Ayr</string>
|
||||
<string name="type.amenity.language_school">Skol Tavas</string>
|
||||
<string name="type.office.diplomatic">Kanatti</string>
|
||||
<string name="type.office.security">Buro Gwithyas Diogeledh</string>
|
||||
<string name="type.building.guardhouse">Borth Gwithyas Diogeledh</string>
|
||||
<string name="type.power.portal">Portal Tredan</string>
|
||||
<string name="type.amenity.fast_food">Boos Uskis</string>
|
||||
<string name="type.amenity.ferry_terminal">Kowbal</string>
|
||||
<string name="type.amenity.fire_station">Tanlu</string>
|
||||
<string name="type.amenity.fountain">Fenten</string>
|
||||
<string name="type.amenity.fuel">Petrolva</string>
|
||||
<string name="type.amenity.grave_yard">Bedhros</string>
|
||||
<string name="type.amenity.grave_yard.christian">Bedhros Kristyon</string>
|
||||
<string name="type.amenity.hospital">Klavji</string>
|
||||
<string name="type.amenity.ice_cream">Dehen Rew</string>
|
||||
<string name="type.amenity.internet_cafe">Koffiji Kesrosweyth</string>
|
||||
<string name="type.amenity.kindergarten">Floghva</string>
|
||||
<string name="type.amenity.library">Lyverva</string>
|
||||
<string name="type.amenity.loading_dock">Kay Dalgarga</string>
|
||||
<string name="type.amenity.luggage_locker">Amari Fardellow</string>
|
||||
<string name="type.amenity.marketplace">Tyller Marghas</string>
|
||||
<string name="type.amenity.motorcycle_parking">Parkyans Jynn Diwros</string>
|
||||
<string name="type.amenity.nightclub">Kist Nos</string>
|
||||
<string name="type.amenity.parking">Parkyans</string>
|
||||
<string name="type.amenity.parking.fee">Parkyans</string>
|
||||
<string name="type.amenity.parking.multi.storey">Parkyans Liesleur</string>
|
||||
<string name="type.amenity.parking.multi.storey.fee">Parkyans Liesleur</string>
|
||||
<string name="type.amenity.parking.no.access">Parkyans Privedh</string>
|
||||
<string name="type.amenity.parking.permissive">Parkyans Privedh</string>
|
||||
<string name="type.amenity.parking.private">Parkyans Privedh</string>
|
||||
<string name="type.amenity.parking.park_and_ride">Parkyans Parkya hag Ehwias</string>
|
||||
<string name="type.amenity.parking.underground">Parkyans yn-dann dhor</string>
|
||||
<string name="type.amenity.parking.underground.fee">Parkyans yn-dann dhor</string>
|
||||
<string name="type.amenity.parking.underground.private">Parkyans privedh yn-dann dhor</string>
|
||||
<string name="type.amenity.parking_entrance">Entrans Parkyans</string>
|
||||
<string name="type.amenity.parking_entrance.private">Entrans Privedh Parkyans</string>
|
||||
<string name="type.amenity.parking_entrance.permissive">Entrans Parkyans</string>
|
||||
<string name="type.amenity.pharmacy">Ferylva</string>
|
||||
<string name="type.amenity.place_of_worship.buddhist">Eglos Teg Bouddiek</string>
|
||||
<string name="type.amenity.place_of_worship.christian">Eglos</string>
|
||||
<string name="type.amenity.place_of_worship.jewish">Synaga</string>
|
||||
<string name="type.amenity.place_of_worship.muslim">Mosk</string>
|
||||
<string name="type.amenity.police">Kreslu</string>
|
||||
<string name="type.amenity.post_office">Chei Post</string>
|
||||
<string name="type.amenity.prison">Prison</string>
|
||||
<string name="type.amenity.pub">Barr</string>
|
||||
<string name="type.amenity.public_bookcase">Keschanj Lyver</string>
|
||||
<string name="type.amenity.recycling.centre">Kresen Eylgylghyans</string>
|
||||
<string name="type.amenity.recycling">Kofen Eylgylghyans</string>
|
||||
<string name="type.amenity.recycling.container">Kofen Eylgylghyans</string>
|
||||
<string name="type.recycling.batteries">Batriow</string>
|
||||
<string name="type.recycling.clothes">Dillas</string>
|
||||
<string name="type.recycling.glass_bottles">Botellow Gweder</string>
|
||||
<string name="type.recycling.paper">Paper</string>
|
||||
<string name="type.recycling.plastic">Plastek</string>
|
||||
<string name="type.recycling.plastic_bottles">Botellow Plastek</string>
|
||||
<string name="type.recycling.scrap_metal">Wast Alkan</string>
|
||||
<string name="type.recycling.small_appliances">Wast Elektronek</string>
|
||||
<string name="type.recycling.cardboard">Pasbord</string>
|
||||
<string name="type.recycling.cans">Kannys</string>
|
||||
<string name="type.recycling.shoes">Eskisyow</string>
|
||||
<string name="type.recycling.green_waste">Lygennow</string>
|
||||
<string name="type.amenity.restaurant">Bosti</string>
|
||||
<string name="type.amenity.school">Skol</string>
|
||||
<string name="type.amenity.shelter">Skovva</string>
|
||||
<string name="type.amenity.shelter.public_transport">Skovva</string>
|
||||
<string name="type.amenity.public_bath">Bath Poblek</string>
|
||||
<string name="type.amenity.shower">Kowas</string>
|
||||
<string name="type.amenity.telephone">Fon</string>
|
||||
<string name="type.amenity.theatre">Gwariji</string>
|
||||
<string name="type.amenity.toilets">Attesva</string>
|
||||
<string name="type.toilets.yes">Attesva</string>
|
||||
<string name="type.amenity.townhall">Odyans</string>
|
||||
<string name="type.amenity.university">Pennskol</string>
|
||||
<string name="type.amenity.vending_machine.public_transport_tickets">Jynn Tokyn</string>
|
||||
<string name="type.amenity.parcel_locker">Amari Fardel</string>
|
||||
<string name="type.amenity.vehicle_inspection">Hwithrans Karr</string>
|
||||
<string name="type.amenity.vending_machine.fuel">Pomp Petrol</string>
|
||||
<string name="type.amenity.veterinary">Medhek Bestes</string>
|
||||
<string name="type.amenity.animal_shelter">Gredi</string>
|
||||
<string name="type.amenity.waste_basket">Argh</string>
|
||||
<string name="type.amenity.waste_disposal">Argh Bras</string>
|
||||
<string name="type.barrier">Lett</string>
|
||||
<string name="type.barrier.yes">Lett</string>
|
||||
<string name="type.barrier.block">Stock</string>
|
||||
<string name="type.barrier.border_control">Amalborth</string>
|
||||
<string name="type.barrier.chain">Chayn</string>
|
||||
<string name="type.barrier.city_wall">Fos Cita</string>
|
||||
<string name="type.barrier.cycle_barrier">Lett Diwros</string>
|
||||
<string name="type.waterway.ditch">Kleys</string>
|
||||
<string name="type.barrier.entrance">Entrans</string>
|
||||
<string name="type.barrier.fence">Ke</string>
|
||||
<string name="type.barrier.guard_rail">Kledhren</string>
|
||||
<string name="type.barrier.gate">Porth</string>
|
||||
<string name="type.barrier.hedge">Ke</string>
|
||||
<string name="type.barrier.kissing_gate">Porth Amm</string>
|
||||
<string name="type.barrier.stile">Kammva</string>
|
||||
<string name="type.barrier.wicket_gate">Porth Darasik</string>
|
||||
<string name="type.barrier.toll_booth">Tollva</string>
|
||||
<string name="type.barrier.wall">Fos</string>
|
||||
<string name="type.boundary">Or</string>
|
||||
<string name="type.boundary.administrative">Or Menystrek</string>
|
||||
<string name="type.boundary.administrative.2">Or Kenedhlegi</string>
|
||||
<string name="type.boundary.administrative.3">Or Ranndirek</string>
|
||||
<string name="type.boundary.administrative.4">Or Ranndirek</string>
|
||||
<string name="type.boundary.national_park">Park Kenedhlek</string>
|
||||
<string name="type.boundary.aboriginal_lands">Tir Teythiek</string>
|
||||
<string name="type.building">Drehevyans</string>
|
||||
<string name="type.building.address">Trigva</string>
|
||||
<string name="type.building.has_parts">Drehevyans</string>
|
||||
<string name="type.building_part">Drehevyans</string>
|
||||
<string name="type.building.garage">Karrji</string>
|
||||
<string name="type.building.train_station">Drehevyans Gorsav</string>
|
||||
<string name="type.building.warehouse">Gwaraji</string>
|
||||
<string name="type.cemetery.grave">Bedh</string>
|
||||
<string name="type.craft">Kreft</string>
|
||||
<string name="type.craft.blacksmith">Ferror</string>
|
||||
<string name="type.craft.carpenter">Karpenter</string>
|
||||
<string name="type.craft.electrician">Tredaner</string>
|
||||
<string name="type.craft.electronics_repair">Ewnans Tredanegek</string>
|
||||
<string name="type.craft.gardener">Lowarther</string>
|
||||
<string name="type.craft.handicraft">Dornweyth</string>
|
||||
<string name="type.craft.metal_construction">Gweythor Alkan</string>
|
||||
<string name="type.craft.painter">Payntyer</string>
|
||||
<string name="type.craft.photographer">Skeusenner</string>
|
||||
<string name="type.shop.camera">Gwerthji Kamera</string>
|
||||
<string name="type.craft.plumber">Plommer</string>
|
||||
<string name="type.craft.shoemaker">Ewnans Eskis</string>
|
||||
<string name="type.craft.winery">Gwinyer</string>
|
||||
<string name="type.craft.tailor">Tregher</string>
|
||||
<string name="type.cuisine.african">afrikan</string>
|
||||
<string name="type.cuisine.american">amerikanek</string>
|
||||
<string name="type.cuisine.arab">arabek</string>
|
||||
<string name="type.cuisine.argentinian">arghantinek</string>
|
||||
<string name="type.cuisine.asian">asiek</string>
|
||||
<string name="type.cuisine.austrian">ostrian</string>
|
||||
<string name="type.cuisine.barbecue">Barbakoa</string>
|
||||
<string name="type.cuisine.beef_bowl">Bolla Bewin</string>
|
||||
<string name="type.cuisine.brazilian">brasiliek</string>
|
||||
<string name="type.cuisine.breakfast">Hansel</string>
|
||||
<string name="type.cuisine.bubble_tea">Te Hwethen</string>
|
||||
<string name="type.cuisine.burger">Borger</string>
|
||||
<string name="type.cuisine.cake">Tesen</string>
|
||||
<string name="type.cuisine.chicken">Kig Yar</string>
|
||||
<string name="type.cuisine.chinese">chinek</string>
|
||||
<string name="type.cuisine.coffee_shop">Koffi</string>
|
||||
<string name="type.cuisine.croatian">kroatek</string>
|
||||
<string name="type.cuisine.curry">Kurri</string>
|
||||
<string name="type.cuisine.donut">Knowen Doos</string>
|
||||
<string name="type.cuisine.ethiopian">ethiopek</string>
|
||||
<string name="type.cuisine.fine_dining">Dybri Brav</string>
|
||||
<string name="type.cuisine.fish">Pysk</string>
|
||||
<string name="type.cuisine.fish_and_chips">Asklotti</string>
|
||||
<string name="type.cuisine.french">frynkek</string>
|
||||
<string name="type.cuisine.georgian">jorjiek</string>
|
||||
<string name="type.cuisine.german">almaynek</string>
|
||||
<string name="type.cuisine.greek">grek</string>
|
||||
<string name="type.cuisine.grill">Rastell</string>
|
||||
<string name="type.cuisine.hungarian">hungarek</string>
|
||||
<string name="type.cuisine.ice_cream">Dehen Rew</string>
|
||||
</resources>
|
||||
@@ -484,5 +484,18 @@
|
||||
"Iraq_North":"اپاختر اراک",
|
||||
"Iran_East Description":"مشهد، زاهدان، یزد",
|
||||
"Iran_North Description":"تهران، کرج، تبریز",
|
||||
"Iran_South Description":"اسپهان، شیراز، اهواز"
|
||||
"Iran_South Description":"اسپهان، شیراز، اهواز",
|
||||
"Japan_Kinki Region_Osaka_Osaka":"اوساکا",
|
||||
"Japan_Kinki Region_Osaka_West":"اوساکا — خاور",
|
||||
"Lithuania_East":"لیتوانی — باختر",
|
||||
"Lithuania_West":"لیتوانی — خاور",
|
||||
"Mexico_Chihuahua":"چیواوا",
|
||||
"Mexico_Sonora":"سونورا",
|
||||
"Nepal_Kathmandu":"کاتماندو",
|
||||
"Nigeria_North":"نیجریه — اپاختر",
|
||||
"Nigeria_South":"نیجریه — اواخشتر",
|
||||
"Norway_Hordaland":"هوردالاند",
|
||||
"Norway_Nordland":"نوردلاند",
|
||||
"Peru_Lima":"لیما",
|
||||
"Portugal_Islands":"گزیرگهای پرتغال"
|
||||
}
|
||||
|
||||
17
data/test_data/traff/DE-A10-Werder-GrossKreutz.xml
Normal file
17
data/test_data/traff/DE-A10-Werder-GrossKreutz.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<!-- From DE-RealWorldLargeFeed. -->
|
||||
<!--
|
||||
The route starts at a large junction, goes down the wrong carriageway of the motorway,
|
||||
changes direction at the next junction, then back.
|
||||
-->
|
||||
<feed>
|
||||
<message expiration_time="2025-10-09T21:37:08+03:00" id="tmc:d.1.7:d.1.10209.n.1" receive_time="2025-10-09T21:17:08+03:00" update_time="2025-10-09T21:17:08+03:00">
|
||||
<location directionality="ONE_DIRECTION" fuzziness="LOW_RES" road_class="MOTORWAY" road_name="Westlicher Berliner Ring" road_ref="A10">
|
||||
<from junction_name="Werder" junction_ref="21">+52.334801 +12.814650</from>
|
||||
<to junction_name="Groß Kreutz" junction_ref="22">+52.393700 +12.835000</to>
|
||||
</location>
|
||||
<events>
|
||||
<event class="CONGESTION" type="CONGESTION_STATIONARY_TRAFFIC">
|
||||
</event>
|
||||
</events>
|
||||
</message>
|
||||
</feed>
|
||||
17
data/test_data/traff/DE-A115-PotsdamDrewitz-Nuthetal.xml
Normal file
17
data/test_data/traff/DE-A115-PotsdamDrewitz-Nuthetal.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<!-- From DE-RealWorldLargeFeed. -->
|
||||
<!--
|
||||
The route ends at a large junction, proceeds past the reference point, then
|
||||
goes back down the wrong carriageway of the motorway.
|
||||
-->
|
||||
<feed>
|
||||
<message expiration_time="2025-10-09T21:37:08+03:00" id="tmc:d.1.7:d.1.10292.p.1" receive_time="2025-10-09T21:17:08+03:00" update_time="2025-10-09T21:17:08+03:00">
|
||||
<location directionality="ONE_DIRECTION" fuzziness="LOW_RES" road_class="MOTORWAY" road_name="Autobahnzubringer Magdeburg" road_ref="A115">
|
||||
<from junction_name="Potsdam-Drewitz" junction_ref="5a">+52.352650 +13.140700</from>
|
||||
<to junction_name="Nuthetal" junction_ref="7">+52.300201 +13.083500</to>
|
||||
</location>
|
||||
<events>
|
||||
<event class="CONGESTION" type="CONGESTION_STATIONARY_TRAFFIC">
|
||||
</event>
|
||||
</events>
|
||||
</message>
|
||||
</feed>
|
||||
@@ -0,0 +1,16 @@
|
||||
<!--
|
||||
From DE-realworld5.
|
||||
Test case for turn penalty (eastern end).
|
||||
-->
|
||||
<feed>
|
||||
<message expiration_time="2025-10-09T21:37:08+03:00" id="tmc:d.1.15:d.1.29829.n.1" receive_time="2025-10-09T21:17:08+03:00" update_time="2025-10-09T21:17:08+03:00">
|
||||
<location directionality="ONE_DIRECTION" fuzziness="LOW_RES" road_name="Mittlerer Ring" road_ref="B2R">
|
||||
<from junction_name="Lerchenauer Straße">+48.176102 +11.558100</from>
|
||||
<to junction_name="Petueltunnel">+48.178001 +11.572800</to>
|
||||
</location>
|
||||
<events>
|
||||
<event class="CONGESTION" type="CONGESTION_QUEUE">
|
||||
</event>
|
||||
</events>
|
||||
</message>
|
||||
</feed>
|
||||
19
data/test_data/traff/DE-B2R-SendlingSued-Passauerstrasse.xml
Normal file
19
data/test_data/traff/DE-B2R-SendlingSued-Passauerstrasse.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<!-- From DE-realworld5. -->
|
||||
<!--
|
||||
A combination of reference points on the opposite carriageway, high offroad cost,
|
||||
allowing any road to be used and a low penalty results in a completely incorrect
|
||||
location (through a residential area rather than along the opposite carriageway).
|
||||
Test case for turn penalty (eastern end) if the above is resolved.
|
||||
-->
|
||||
<feed>
|
||||
<message expiration_time="2025-10-09T21:37:08+03:00" id="tmc:d.1.15:d.1.22689.p.1" receive_time="2025-10-09T21:17:08+03:00" update_time="2025-10-09T21:17:08+03:00">
|
||||
<location directionality="ONE_DIRECTION" fuzziness="LOW_RES" road_name="Mittlerer Ring" road_ref="B2R">
|
||||
<from junction_name="München-Sendling-Süd">+48.110901 +11.518500</from>
|
||||
<to junction_name="Passauerstraße">+48.110649 +11.534150</to>
|
||||
</location>
|
||||
<events>
|
||||
<event class="CONGESTION" type="CONGESTION_QUEUE">
|
||||
</event>
|
||||
</events>
|
||||
</message>
|
||||
</feed>
|
||||
22
data/test_data/traff/DE-GM4-UrbanDualCarriagewayEndpoint.xml
Normal file
22
data/test_data/traff/DE-GM4-UrbanDualCarriagewayEndpoint.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<!--
|
||||
Subset of realworld5.
|
||||
Test case for turn penalty (both ends).
|
||||
Junction names are the names of the crossing roads.
|
||||
No segments of the crossing roads should be among the matched segments.
|
||||
-->
|
||||
<feed>
|
||||
<message expiration_time="2025-10-09T21:37:08+03:00" id="tmc:d.1.15:d.1.46572.p.1,14" receive_time="2025-10-09T21:17:08+03:00" update_time="2025-10-09T21:17:08+03:00">
|
||||
<location directionality="ONE_DIRECTION" fuzziness="LOW_RES" road_name="Leopoldstraße" road_ref="GM4">
|
||||
<from junction_name="Potsdamer Straße">+48.167301 +11.586200</from>
|
||||
<to junction_name="Ungererstraße">+48.164200 +11.586500</to>
|
||||
</location>
|
||||
<events>
|
||||
<event class="INCIDENT" type="INCIDENT_ACCIDENT">
|
||||
</event>
|
||||
<event class="CONGESTION" type="CONGESTION_STATIONARY_TRAFFIC">
|
||||
</event>
|
||||
<event class="HAZARD" type="HAZARD_PASSABLE_WITH_CARE_BELOW_ELEVATION">
|
||||
</event>
|
||||
</events>
|
||||
</message>
|
||||
</feed>
|
||||
58
data/test_data/traff/DE-RoundaboutEndpoint.xml
Executable file
58
data/test_data/traff/DE-RoundaboutEndpoint.xml
Executable file
@@ -0,0 +1,58 @@
|
||||
<!--
|
||||
Test cases for reference points inside a roundabout.
|
||||
If the roundabout itself is included in the decoded location, the message would
|
||||
affect all roads which connect to it, resulting in incorrect routing, especially
|
||||
in the case of closure events. For this reason, we must truncate roundabouts if
|
||||
they occur at the start or end of the decoded location.
|
||||
-->
|
||||
<feed>
|
||||
<message id="tmc:d.1.12:d.1.13962.p.5,11" receive_time="2018-07-27T10:22:22+02:00" update_time="2018-07-27T08:43:15Z" expiration_time="2018-07-27T09:43:15Z" urgency="URGENT">
|
||||
<location fuzziness="LOW_RES" directionality="BOTH_DIRECTIONS" road_class="SECONDARY" road_ref="L87">
|
||||
<from junction_name="Rheinau-Freistett/B36">+48.661098 +7.936800</from>
|
||||
<to junction_name="Gambsheim (F)">+48.683701 +7.916600</to>
|
||||
</location>
|
||||
<events>
|
||||
<event class="CONSTRUCTION" type="CONSTRUCTION_CONSTRUCTION_WORK">
|
||||
</event>
|
||||
<event class="RESTRICTION" type="RESTRICTION_CLOSED">
|
||||
</event>
|
||||
</events>
|
||||
</message>
|
||||
<message id="tmc:d.1.12:d.1.26212.p.5,11" receive_time="2018-07-27T10:21:49+02:00" update_time="2018-07-27T08:42:41Z" expiration_time="2018-07-27T09:42:41Z" urgency="URGENT">
|
||||
<location fuzziness="LOW_RES" directionality="BOTH_DIRECTIONS" road_class="PRIMARY" road_ref="B428">
|
||||
<from junction_name="Hackenheim">+49.822948 +7.906900</from>
|
||||
<to junction_name="Frei-Laubersheim">+49.803650 +7.901450</to>
|
||||
</location>
|
||||
<events>
|
||||
<event class="CONSTRUCTION" type="CONSTRUCTION_LONGTERM_ROADWORKS">
|
||||
</event>
|
||||
<event class="RESTRICTION" type="RESTRICTION_CLOSED">
|
||||
</event>
|
||||
</events>
|
||||
</message>
|
||||
<message id="tmc:d.1.12:d.1.48638.n.9,11" receive_time="2018-07-27T10:11:06+02:00" update_time="2018-07-27T08:31:43Z" expiration_time="2018-07-27T09:31:43Z" urgency="URGENT">
|
||||
<location fuzziness="LOW_RES" directionality="BOTH_DIRECTIONS" road_class="PRIMARY" road_ref="B272">
|
||||
<from junction_name="Hochstadt">+49.239750 +8.222300</from>
|
||||
<to junction_name="Weingarten">+49.253448 +8.268100</to>
|
||||
</location>
|
||||
<events>
|
||||
<event class="CONSTRUCTION" type="CONSTRUCTION_ROADWORKS">
|
||||
</event>
|
||||
<event class="RESTRICTION" type="RESTRICTION_CLOSED_AHEAD">
|
||||
</event>
|
||||
</events>
|
||||
</message>
|
||||
<message id="tmc:d.1.12:d.1.56576.n.9,11" receive_time="2018-07-27T10:22:53+02:00" update_time="2018-07-27T08:43:45Z" expiration_time="2018-07-27T09:43:45Z" urgency="URGENT">
|
||||
<location fuzziness="LOW_RES" directionality="ONE_DIRECTION" road_class="PRIMARY" road_ref="B417">
|
||||
<from junction_name="Diez">+50.372398 +8.038500</from>
|
||||
<to junction_name="Diez">+50.370850 +8.004050</to>
|
||||
</location>
|
||||
<events>
|
||||
<event class="CONSTRUCTION" type="CONSTRUCTION_RESURFACING_WORK">
|
||||
</event>
|
||||
<event class="RESTRICTION" type="RESTRICTION_CLOSED">
|
||||
<supplementary_info class="VEHICLE" type="S_VEHICLE_THROUGH_TRAFFIC"/>
|
||||
</event>
|
||||
</events>
|
||||
</message>
|
||||
</feed>
|
||||
23
data/test_data/traff/LT-A5-Kaunas-Eastbound.xml
Normal file
23
data/test_data/traff/LT-A5-Kaunas-Eastbound.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<!--
|
||||
This is a 140 m location. Reference points are almost exactly on the opposite
|
||||
carriageway; since this is inside a junction, there is a wider gap between
|
||||
the two carriageways than there would be on a normal stretch of expressway.
|
||||
Without truncation, the decoded location starts approximately in the right
|
||||
spot but overshoots the end point, going to the nearest junction, then back
|
||||
in the opposite direction and to the end point on the opposite carriageway,
|
||||
ending within 5 m of the end point.
|
||||
-->
|
||||
<feed>
|
||||
<message id="lt.eismoinfo.restrictions:4249b6510b73750684ca94de5fe8cf32,eastbound" receive_time="2025-01-21T12:33:06Z" update_time="2025-01-21T12:33:06Z" expiration_time="2025-12-31T21:00:00Z" cancellation="false" forecast="false" urgency="NORMAL">
|
||||
<merge/>
|
||||
<location country="LT" origin="Suvalkai*" directionality="ONE_DIRECTION" destination="Kaunas" road_class="TRUNK" road_ref="A5">
|
||||
<from distance="0.14">54.939945 23.879789</from>
|
||||
<to distance="0.0" junction_name="Kaunas">54.940094 23.881950</to>
|
||||
</location>
|
||||
<events>
|
||||
<event class="RESTRICTION" type="RESTRICTION_MAX_WIDTH" q_dimension="3.5"/>
|
||||
<event class="CONSTRUCTION" type="CONSTRUCTION_ROADWORKS"/>
|
||||
<event class="RESTRICTION" type="RESTRICTION_SPEED_LIMIT" speed="50"/>
|
||||
</events>
|
||||
</message>
|
||||
</feed>
|
||||
@@ -108,8 +108,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"
|
||||
|
||||
@@ -207,8 +207,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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <vector>
|
||||
|
||||
namespace traffic
|
||||
{
|
||||
bool GenerateTrafficKeysFromDataFile(std::string const & mwmPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::vector<TrafficInfo::RoadSegmentId> keys;
|
||||
TrafficInfo::ExtractTrafficKeys(mwmPath, keys);
|
||||
|
||||
std::vector<uint8_t> 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
|
||||
@@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace traffic
|
||||
{
|
||||
bool GenerateTrafficKeysFromDataFile(std::string const & mwmPath);
|
||||
} // namespace traffic
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -27,6 +27,40 @@ NSString * const kUDFileLoggingEnabledKey = @"FileLoggingEnabledKey";
|
||||
|
||||
@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;
|
||||
|
||||
@@ -717,6 +717,13 @@
|
||||
"editor_place_doesnt_exist" = "Place does not exist";
|
||||
"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";
|
||||
@@ -816,6 +823,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";
|
||||
|
||||
@@ -737,6 +737,13 @@
|
||||
"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";
|
||||
@@ -837,6 +844,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";
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"download_maps" = "بارگیری نقشهها";
|
||||
|
||||
/* Settings/Downloader - info for country which started downloading */
|
||||
"downloading" = "درحال دانلود…";
|
||||
"downloading" = "بارگیری میشود…";
|
||||
|
||||
/* Choose measurement on first launch alert - choose metric system button */
|
||||
"kilometres" = "کیلومتر";
|
||||
|
||||
@@ -914,34 +914,31 @@
|
||||
"type.railway.preserved.tunnel" = "Preserved Rail Tunnel";
|
||||
"type.railway.rail" = "راه آهن";
|
||||
"type.railway.rail.highspeed" = "راه آهن پرسرعت";
|
||||
"type.railway.rail.tourism" = "راه آهن توریستی";
|
||||
"type.railway.rail.main" = "راه آهن";
|
||||
"type.railway.rail.tourism" = "راهآهن گردشگرانه";
|
||||
"type.railway.rail.main" = "راهآهن";
|
||||
|
||||
/* Includes ordinary railway=rail w/o more specific usage= and service= tags. */
|
||||
"type.railway.rail.branch" = "راه آهن ثانویه";
|
||||
"type.railway.rail.branch" = "اَزگ راهآهن";
|
||||
|
||||
/* Non-passenger utility tracks: industrial, military, test. */
|
||||
"type.railway.rail.utility" = "راه آهن شهری";
|
||||
"type.railway.rail.spur" = "خار راه آهن";
|
||||
|
||||
/* Short service tracks: siding, yard, crossover. */
|
||||
"type.railway.rail.service" = "ریل کمکی";
|
||||
"type.railway.rail.bridge" = "پل راه آهن";
|
||||
"type.railway.rail.highspeed.bridge" = "پل راه آهن";
|
||||
"type.railway.rail.tourism.bridge" = "پل راه آهن";
|
||||
"type.railway.rail.main.bridge" = "پل راه آهن";
|
||||
"type.railway.rail.branch.bridge" = "پل راه آهن";
|
||||
"type.railway.rail.utility.bridge" = "پل راه آهن";
|
||||
"type.railway.rail.spur.bridge" = "پل راه آهن";
|
||||
"type.railway.rail.service.bridge" = "پل راه آهن";
|
||||
"type.railway.rail.tunnel" = "تونل راه آهن";
|
||||
"type.railway.rail.highspeed.tunnel" = "تونل راه آهن";
|
||||
"type.railway.rail.tourism.tunnel" = "تونل راه آهن";
|
||||
"type.railway.rail.main.tunnel" = "تونل راه آهن";
|
||||
"type.railway.rail.branch.tunnel" = "تونل راه آهن";
|
||||
"type.railway.rail.utility.tunnel" = "تونل راه آهن";
|
||||
"type.railway.rail.spur.tunnel" = "تونل راه آهن";
|
||||
"type.railway.rail.service.tunnel" = "تونل راه آهن";
|
||||
"type.railway.rail.utility" = "سامانه راهآهن برقی";
|
||||
"type.railway.rail.spur" = "شاخک راهآهن";
|
||||
"type.railway.rail.bridge" = "پل راهآهن";
|
||||
"type.railway.rail.highspeed.bridge" = "پل راهآهن";
|
||||
"type.railway.rail.tourism.bridge" = "پل راهآهن";
|
||||
"type.railway.rail.main.bridge" = "پل راهآهن";
|
||||
"type.railway.rail.branch.bridge" = "پل راهآهن";
|
||||
"type.railway.rail.utility.bridge" = "پل راهآهن";
|
||||
"type.railway.rail.spur.bridge" = "پل راهآهن";
|
||||
"type.railway.rail.service.bridge" = "پل راهآهن";
|
||||
"type.railway.rail.tunnel" = "آهون راهآهن";
|
||||
"type.railway.rail.highspeed.tunnel" = "آهون راهآهن";
|
||||
"type.railway.rail.tourism.tunnel" = "آهون راهآهن";
|
||||
"type.railway.rail.main.tunnel" = "آهون راهآهن";
|
||||
"type.railway.rail.branch.tunnel" = "آهون راهآهن";
|
||||
"type.railway.rail.utility.tunnel" = "آهون راهآهن";
|
||||
"type.railway.rail.spur.tunnel" = "آهون راهآهن";
|
||||
"type.railway.rail.service.tunnel" = "آهون راهآهن";
|
||||
"type.railway.station" = "ایستگاه قطار";
|
||||
"type.railway.station.funicular" = "فونیکولور";
|
||||
"type.railway.station.light_rail" = "ایستگاه قطار";
|
||||
@@ -1438,7 +1435,7 @@
|
||||
"type.piste_type.nordic" = "Nordic Ski Trail";
|
||||
"type.piste_type.sled" = "Sledding Piste";
|
||||
"type.piste_type.sled.area" = "Sledding Piste";
|
||||
"type.piste_type.snow_park" = "پارک برفی";
|
||||
"type.piste_type.snow_park" = "بوستان برفی";
|
||||
"type.piste_type.hike" = "مسیر پیاده روی برفی";
|
||||
"type.piste_type.connection" = "اتصال پیست";
|
||||
"type.piste_type.skitour" = "مسیر اسکیتور";
|
||||
|
||||
@@ -439,7 +439,7 @@
|
||||
"dialog_incorrect_feature_position" = "Change location";
|
||||
"app_tip_01" = "With your donations and support, we can create the best maps in the World!";
|
||||
"downloader_status_failed" = "Failed";
|
||||
"dialog_routing_system_error" = "System error";
|
||||
"dialog_routing_system_error" = "Sistemska greška";
|
||||
"buildings" = "Buildings";
|
||||
"dialog_routing_download_and_build_cross_route" = "Would you like to download the map and create a more optimal route spanning more than one map?";
|
||||
"dialog_routing_disclaimer_beware" = "Please stay alert and safe on the roads!";
|
||||
@@ -479,7 +479,7 @@
|
||||
"placepage_remove_stop" = "Remove Stop";
|
||||
"core_placepage_unknown_place" = "Map Point";
|
||||
"elevation_profile_diff_level_hard" = "Hard";
|
||||
"dialog_routing_try_again" = "Please try again";
|
||||
"dialog_routing_try_again" = "Pokušajte ponovno";
|
||||
"no_available" = "No";
|
||||
"fuel_places" = "Gas Stations";
|
||||
"clear_search" = "Clear Search History";
|
||||
|
||||
@@ -1024,7 +1024,7 @@
|
||||
"icloud_disabled_title" = "iCloud jest wyłączony";
|
||||
|
||||
/* Message for the "iCloud is Disabled" alert. */
|
||||
"icloud_disabled_message" = "Aby korzystać z tej funkcji, włącz usługę iCloud w ustawieniach urządzenia.";
|
||||
"icloud_disabled_message" = "Aby korzystać z tej funkcji, włącz usługę iCloud dla CoMaps w ustawieniach swojego konta Apple.";
|
||||
|
||||
/* Title for the "Enable iCloud Syncronization" alert's "Enable" action button. */
|
||||
"enable" = "Włącz";
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
272F1F3B2E0EE0A300FA52EF /* NoExistingProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 272F1F3A2E0EE09500FA52EF /* NoExistingProfileView.swift */; };
|
||||
272F1F3D2E0EE0C800FA52EF /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 272F1F3C2E0EE0C400FA52EF /* ProfileView.swift */; };
|
||||
272F1F462E0EEF9400FA52EF /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 272F1F452E0EEF8B00FA52EF /* SafariView.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 */; };
|
||||
@@ -767,6 +768,7 @@
|
||||
272F1F3A2E0EE09500FA52EF /* NoExistingProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoExistingProfileView.swift; sourceTree = "<group>"; };
|
||||
272F1F3C2E0EE0C400FA52EF /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
|
||||
272F1F452E0EEF8B00FA52EF /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
2752B6CD2E3121D800887CC4 /* Language.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Language.swift; sourceTree = "<group>"; };
|
||||
2765D1CD2E13F9BC0005CA2B /* BridgeControllers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BridgeControllers.swift; sourceTree = "<group>"; };
|
||||
@@ -1792,6 +1794,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 */,
|
||||
@@ -2029,6 +2032,7 @@
|
||||
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
274720592E439FBA00C516DF /* libtraffxml.a */,
|
||||
FAF9DDA22A86DC54000D7037 /* libharfbuzz.a */,
|
||||
FA456C3B26BDC6AD00B83C20 /* Chart.framework */,
|
||||
FA853BF226BC5DE50026D455 /* libshaders.a */,
|
||||
|
||||
@@ -448,6 +448,28 @@ import AVFoundation
|
||||
}
|
||||
|
||||
|
||||
/// 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
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="24128" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="ipad9_7" orientation="landscape" layout="fullscreen" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24063"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -81,26 +81,35 @@
|
||||
<rect key="frame" x="16" y="58" width="308" height="64"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="g13-pK-Eig" userLabel="Outdoor Button" customClass="BottomMenuLayerButton" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="102.5" height="64"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="1024" height="64"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<connections>
|
||||
<action selector="onOutdoorButton:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="UQ2-jj-fPc"/>
|
||||
</connections>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="edA-Mo-3Vx" customClass="BottomMenuLayerButton" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="102.5" y="0.0" width="103" height="64"/>
|
||||
<rect key="frame" x="1024" y="0.0" width="1024" height="64"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<connections>
|
||||
<action selector="onIsoLinesButton:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="3LS-C2-2Mc"/>
|
||||
</connections>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4US-fZ-cyg" customClass="BottomMenuLayerButton" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="205.5" y="0.0" width="102.5" height="64"/>
|
||||
<rect key="frame" x="2048" y="0.0" width="1024" height="64"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<connections>
|
||||
<action selector="onSubwayButton:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="xxM-kP-gT1"/>
|
||||
</connections>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" id="95L-lU-yCQ" customClass="BottomMenuLayerButton" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="-358" y="-352" width="1024" height="768"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<viewLayoutGuide key="safeArea" id="ZQs-ua-gDZ"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<connections>
|
||||
<action selector="onTrafficButton:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="tie-i7-JaB"/>
|
||||
</connections>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="70" id="d0H-kE-IWx"/>
|
||||
@@ -144,6 +153,7 @@
|
||||
<outlet property="isoLinesButton" destination="edA-Mo-3Vx" id="qoC-8w-EqY"/>
|
||||
<outlet property="outdoorButton" destination="g13-pK-Eig" id="ib1-aw-Qv9"/>
|
||||
<outlet property="subwayButton" destination="4US-fZ-cyg" id="eQB-HR-Wgl"/>
|
||||
<outlet property="trafficButton" destination="95L-lU-yCQ" id="O9W-En-8Rc"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="137.6953125" y="201.953125"/>
|
||||
</tableViewCell>
|
||||
|
||||
@@ -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 {
|
||||
@@ -212,6 +220,24 @@ struct SettingsNavigationView: View {
|
||||
} header: {
|
||||
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())
|
||||
@@ -229,6 +255,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
|
||||
@@ -276,6 +304,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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,5 @@ add_subdirectory(shaders)
|
||||
add_subdirectory(storage)
|
||||
add_subdirectory(tracking)
|
||||
add_subdirectory(traffic)
|
||||
add_subdirectory(traffxml)
|
||||
add_subdirectory(transit)
|
||||
|
||||
@@ -38,9 +38,9 @@ namespace
|
||||
{
|
||||
// The first zoom level in kAverageSegmentsCount.
|
||||
int constexpr kFirstZoomInAverageSegments = 10;
|
||||
std::array<size_t, 10> constexpr kAverageSegmentsCount = {
|
||||
// 10 11 12 13 14 15 16 17 18 19
|
||||
10000, 5000, 10000, 5000, 2500, 5000, 2000, 1000, 500, 500};
|
||||
std::array<size_t, 11> 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;
|
||||
|
||||
|
||||
@@ -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 <typename Point>
|
||||
class ParametrizedSegment
|
||||
{
|
||||
@@ -36,7 +40,9 @@ public:
|
||||
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 @@ public:
|
||||
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);
|
||||
|
||||
|
||||
@@ -19,20 +19,31 @@ public:
|
||||
/// Registers a new map.
|
||||
std::pair<MwmId, RegResult> 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 @@ private:
|
||||
std::unique_ptr<FeatureSourceFactory> 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<FeatureSourceFactory>()) {}
|
||||
};
|
||||
|
||||
/// 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:
|
||||
|
||||
@@ -76,6 +76,18 @@ public:
|
||||
// (number of points in inner triangle-strips).
|
||||
using PointsBufferT = buffer_vector<m2::PointD, 32>;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
@@ -85,6 +97,13 @@ public:
|
||||
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);
|
||||
|
||||
@@ -701,24 +701,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);
|
||||
|
||||
@@ -157,8 +157,10 @@ public:
|
||||
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:
|
||||
@@ -243,19 +245,26 @@ public:
|
||||
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 */) {}
|
||||
};
|
||||
|
||||
@@ -304,7 +313,14 @@ public:
|
||||
/// @todo In fact, std::shared_ptr<MwmInfo> is a MwmId. Seems like better to make vector<MwmId> interface.
|
||||
void GetMwmsInfo(std::vector<std::shared_ptr<MwmInfo>> & 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();
|
||||
@@ -345,10 +361,18 @@ private:
|
||||
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.
|
||||
|
||||
@@ -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 <algorithm>
|
||||
|
||||
@@ -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";
|
||||
@@ -273,8 +285,11 @@ Framework::Framework(FrameworkParams const & params, bool loadMaps)
|
||||
[this]() -> StringsBundle const &
|
||||
{ return m_stringsBundle; }, [this]() -> power_management::PowerManager const & { return m_powerManager; }),
|
||||
static_cast<RoutingManager::Delegate &>(*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<descriptions::Loader>(m_featuresFetcher.GetDataSource()))
|
||||
@@ -348,10 +363,10 @@ Framework::Framework(FrameworkParams const & params, bool loadMaps)
|
||||
editor.SetDelegate(make_unique<search::EditorDelegate>(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());
|
||||
|
||||
@@ -383,6 +398,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();
|
||||
@@ -398,14 +426,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();
|
||||
|
||||
@@ -487,6 +515,8 @@ void Framework::LoadMapsSync()
|
||||
LOG(LDEBUG, ("Editor initialized"));
|
||||
|
||||
GetStorage().RestoreDownloadQueue();
|
||||
|
||||
InitializeTraffic();
|
||||
}
|
||||
|
||||
// Small copy-paste with LoadMapsSync, but I don't have a better solution.
|
||||
@@ -509,6 +539,8 @@ void Framework::LoadMapsAsync(std::function<void()> && callback)
|
||||
|
||||
GetStorage().RestoreDownloadQueue();
|
||||
|
||||
InitializeTraffic();
|
||||
|
||||
callback();
|
||||
});
|
||||
}).detach();
|
||||
@@ -2475,6 +2507,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;
|
||||
|
||||
@@ -103,11 +103,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) {}
|
||||
};
|
||||
@@ -233,7 +244,34 @@ public:
|
||||
/// \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<void()> && callback);
|
||||
|
||||
/// Registers all local map files in internal indexes.
|
||||
@@ -384,6 +422,16 @@ private:
|
||||
private:
|
||||
std::vector<m2::TriangleD> 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);
|
||||
@@ -489,11 +537,18 @@ private:
|
||||
std::unique_ptr<descriptions::Loader> 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);
|
||||
@@ -721,6 +776,14 @@ public:
|
||||
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);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 <utility>
|
||||
#include <vector>
|
||||
|
||||
class TrafficManager final
|
||||
class TrafficManager final : public traffxml::TraffSourceManager
|
||||
{
|
||||
public:
|
||||
using CountryInfoGetterFn = std::function<storage::CountryInfoGetter const &()>;
|
||||
using CountryParentNameGetterFn = std::function<std::string(std::string const &)>;
|
||||
using TrafficUpdateCallbackFn = std::function<void(bool)>;
|
||||
|
||||
/**
|
||||
* @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 @@ public:
|
||||
using TrafficStateChangedFn = std::function<void(TrafficState)>;
|
||||
using GetMwmsByRectFn = std::function<std::vector<MwmSet::MwmId>(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<std::string, traffxml::TraffMessage> GetMessageCache();
|
||||
|
||||
TrafficState GetState() const;
|
||||
void SetStateListener(TrafficStateChangedFn const & onStateChangedFn);
|
||||
|
||||
void SetDrapeEngine(ref_ptr<df::DrapeEngine> 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<bool(traffxml::TraffSource*)>& 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 @@ public:
|
||||
void SetSimplifiedColorScheme(bool simplified);
|
||||
bool HasSimplifiedColorScheme() const { return m_hasSimplifiedColorScheme; }
|
||||
|
||||
private:
|
||||
struct CacheEntry
|
||||
/**
|
||||
* @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<traffxml::TraffSource> 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<MwmSet::MwmId> & 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();
|
||||
|
||||
/**
|
||||
* @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<void()> f)
|
||||
{
|
||||
CacheEntry();
|
||||
explicit CacheEntry(std::chrono::time_point<std::chrono::steady_clock> const & requestTime);
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
f();
|
||||
}
|
||||
|
||||
bool m_isLoaded;
|
||||
size_t m_dataSize;
|
||||
private:
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> m_lastActiveTime;
|
||||
std::chrono::time_point<std::chrono::steady_clock> m_lastRequestTime;
|
||||
std::chrono::time_point<std::chrono::steady_clock> m_lastResponseTime;
|
||||
/**
|
||||
* @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);
|
||||
|
||||
int m_retriesCount;
|
||||
bool m_isWaitingForResponse;
|
||||
/**
|
||||
* @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();
|
||||
|
||||
traffic::TrafficInfo::Availability m_lastAvailability;
|
||||
};
|
||||
/**
|
||||
* @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<MwmSet::MwmId> & mwms);
|
||||
|
||||
void OnTrafficDataResponse(traffic::TrafficInfo && info);
|
||||
void OnTrafficRequestFailed(traffic::TrafficInfo && info);
|
||||
/**
|
||||
* @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 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 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<MwmSet::MwmId> & lastMwmsByRect,
|
||||
std::set<MwmSet::MwmId> & 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<MwmSet::MwmId> & 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 <class F>
|
||||
void ForEachMwm(F && f) const
|
||||
{
|
||||
std::vector<std::shared_ptr<MwmInfo>> allMwmInfo;
|
||||
m_dataSource.GetMwmsInfo(allMwmInfo);
|
||||
std::for_each(allMwmInfo.begin(), allMwmInfo.end(), std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void ForEachActiveMwm(F && f) const
|
||||
{
|
||||
@@ -144,44 +535,233 @@ private:
|
||||
std::for_each(activeMwms.begin(), activeMwms.end(), std::forward<F>(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<int64_t> 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<MyPosition, bool> m_currentPosition = {MyPosition(), false};
|
||||
std::pair<MyPosition, bool> m_currentPositionLazy = m_currentPosition;
|
||||
std::pair<ScreenBase, bool> m_currentModelView = {ScreenBase(), false};
|
||||
std::pair<ScreenBase, bool> 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<TrafficState> m_state;
|
||||
TrafficStateChangedFn m_onStateChangedFn;
|
||||
|
||||
bool m_hasSimplifiedColorScheme = true;
|
||||
|
||||
size_t m_maxCacheSizeBytes;
|
||||
size_t m_currentCacheSizeBytes = 0;
|
||||
|
||||
std::map<MwmSet::MwmId, CacheEntry> m_mwmCache;
|
||||
/**
|
||||
* @brief The TraFF sources from which we get traffic information.
|
||||
*
|
||||
* Threads must lock `m_trafficSourceMutex` prior to accessing this member.
|
||||
*/
|
||||
std::vector<std::unique_ptr<traffxml::TraffSource>> 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<MwmSet::MwmId> m_lastDrapeMwmsByRect;
|
||||
std::set<MwmSet::MwmId> m_activeDrapeMwms;
|
||||
std::vector<MwmSet::MwmId> m_lastRoutingMwmsByRect;
|
||||
std::vector<MwmSet::MwmId> m_lastPositionMwmsByRect;
|
||||
std::set<MwmSet::MwmId> m_activePositionMwms;
|
||||
std::set<MwmSet::MwmId> 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<MwmSet::MwmId, std::string> m_trafficETags;
|
||||
/**
|
||||
* @brief Whether active MWMs have changed since the last request.
|
||||
*/
|
||||
bool m_activeMwmsChanged = false;
|
||||
|
||||
std::atomic<bool> m_isPaused;
|
||||
|
||||
std::vector<MwmSet::MwmId> 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<std::chrono::steady_clock> m_lastResponseTime;
|
||||
|
||||
/**
|
||||
* @brief When the last update notification to the Drape engine was posted.
|
||||
*/
|
||||
std::chrono::time_point<std::chrono::steady_clock> m_lastDrapeUpdate;
|
||||
|
||||
/**
|
||||
* @brief When the last update notification to the traffic observer was posted.
|
||||
*/
|
||||
std::chrono::time_point<std::chrono::steady_clock> m_lastObserverUpdate;
|
||||
|
||||
/**
|
||||
* @brief When the cache file was last updated.
|
||||
*/
|
||||
std::chrono::time_point<std::chrono::steady_clock> 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<traffxml::TraffFeed> 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<bool> 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<std::string, traffxml::TraffMessage> m_messageCache;
|
||||
|
||||
/**
|
||||
* @brief The storage instance.
|
||||
*
|
||||
* Used to persist the TraFF message cache between sessions.
|
||||
*/
|
||||
std::unique_ptr<traffxml::LocalStorage> m_storage;
|
||||
|
||||
/**
|
||||
* @brief The TraFF decoder instance.
|
||||
*
|
||||
* Used to decode TraFF locations into road segments on the map.
|
||||
*/
|
||||
std::unique_ptr<traffxml::DefaultTraffDecoder> 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<MwmSet::MwmId, traffic::TrafficInfo::Coloring> m_allMwmColoring;
|
||||
|
||||
/**
|
||||
* @brief Callback function which gets called on traffic updates.
|
||||
*
|
||||
* Intended for testing.
|
||||
*/
|
||||
std::optional<TrafficUpdateCallbackFn> m_trafficUpdateCallbackFn;
|
||||
};
|
||||
|
||||
extern std::string DebugPrint(TrafficManager::TrafficState state);
|
||||
|
||||
@@ -36,20 +36,61 @@ public:
|
||||
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 @@ private:
|
||||
bool m_hasPosition = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A mark in the shape of a dot.
|
||||
*/
|
||||
class DebugMarkPoint : public UserMark
|
||||
{
|
||||
public:
|
||||
@@ -141,6 +185,9 @@ public:
|
||||
drape_ptr<SymbolNameZoomInfo> GetSymbolNames() const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A mark in the shape of a dot, of caller-defined color and radius.
|
||||
*/
|
||||
class ColoredMarkPoint : public UserMark
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -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 @@ public:
|
||||
|
||||
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.
|
||||
|
||||
@@ -17,6 +17,8 @@ AbsentRegionsFinder::AbsentRegionsFinder(CountryFileGetterFn const & countryFile
|
||||
|
||||
void AbsentRegionsFinder::GenerateAbsentRegions(Checkpoints const & checkpoints, RouterDelegate const & delegate)
|
||||
{
|
||||
m_regions.clear();
|
||||
|
||||
if (m_routerThread)
|
||||
{
|
||||
m_routerThread->Cancel();
|
||||
@@ -48,18 +50,21 @@ void AbsentRegionsFinder::GetAbsentRegions(std::set<std::string> & regions)
|
||||
|
||||
void AbsentRegionsFinder::GetAllRegions(std::set<std::string> & countries)
|
||||
{
|
||||
countries.clear();
|
||||
// Note: if called from `RoutingSession` callback, m_state will still have its pre-update value.
|
||||
if (m_routerThread)
|
||||
{
|
||||
m_routerThread->Join();
|
||||
|
||||
if (!m_routerThread)
|
||||
return;
|
||||
for (auto const & mwmName : m_routerThread->GetRoutineAs<RegionsRouter>()->GetMwmNames())
|
||||
{
|
||||
if (!mwmName.empty())
|
||||
m_regions.emplace(mwmName);
|
||||
}
|
||||
|
||||
m_routerThread->Join();
|
||||
m_routerThread.reset();
|
||||
}
|
||||
|
||||
for (auto const & mwmName : m_routerThread->GetRoutineAs<RegionsRouter>()->GetMwmNames())
|
||||
if (!mwmName.empty())
|
||||
countries.emplace(mwmName);
|
||||
|
||||
m_routerThread.reset();
|
||||
countries = m_regions;
|
||||
}
|
||||
|
||||
bool AbsentRegionsFinder::AreCheckpointsInSameMwm(Checkpoints const & checkpoints) const
|
||||
|
||||
@@ -14,19 +14,45 @@ namespace routing
|
||||
{
|
||||
using LocalFileCheckerFn = std::function<bool(std::string const &)>;
|
||||
|
||||
// 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> 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<std::string> & 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<std::string> & absentCountries);
|
||||
|
||||
private:
|
||||
@@ -39,5 +65,19 @@ private:
|
||||
DataSource & m_dataSource;
|
||||
|
||||
std::unique_ptr<threads::Thread> m_routerThread;
|
||||
|
||||
/**
|
||||
* @brief Mutex for access to `m_regions`.
|
||||
*
|
||||
* Threads which access `m_regions` 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<std::string> m_regions;
|
||||
};
|
||||
} // namespace routing
|
||||
|
||||
@@ -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<std::string> & countries)
|
||||
{
|
||||
if (!m_absentRegionsFinder)
|
||||
return;
|
||||
m_absentRegionsFinder->GetAllRegions(countries);
|
||||
}
|
||||
|
||||
void AsyncRouter::RouterDelegateProxy::OnProgress(float progress)
|
||||
{
|
||||
ProgressCallback onProgress = nullptr;
|
||||
|
||||
@@ -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 @@ public:
|
||||
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<std::string> & countries);
|
||||
|
||||
private:
|
||||
/// Worker thread function
|
||||
void ThreadFunc();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -33,9 +33,18 @@ public:
|
||||
|
||||
// @TODO(bykoianko) Method Generate() should fill
|
||||
// vector<RouteSegment> 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<geometry::PointWithAltitude> const & path,
|
||||
base::Cancellable const & cancellable, std::vector<RouteSegment> & routeSegments);
|
||||
void Clear();
|
||||
|
||||
@@ -22,41 +22,170 @@ 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> 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.
|
||||
*
|
||||
* @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() { 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<EdgeEstimator> Create(VehicleType vehicleType, double maxWeighSpeedKMpH,
|
||||
SpeedKMpH const & offroadSpeedKMpH,
|
||||
std::shared_ptr<TrafficStash> trafficStash, DataSource * dataSourcePtr,
|
||||
std::shared_ptr<NumMwmIds> numMwmIds);
|
||||
|
||||
/**
|
||||
* @brief Creates an `EdgeEstimator` based on a vehicle model.
|
||||
*
|
||||
* This is a convenience wrapper around `Create(VehicleType, double, SpeedKMpH const &,
|
||||
* std::shared_ptr<TrafficStash>, DataSource *, std::shared_ptr<NumMwmIds>)`, 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<EdgeEstimator> Create(VehicleType vehicleType, VehicleModelInterface const & vehicleModel,
|
||||
std::shared_ptr<TrafficStash> trafficStash, DataSource * dataSourcePtr,
|
||||
std::shared_ptr<NumMwmIds> numMwmIds);
|
||||
@@ -74,13 +203,70 @@ private:
|
||||
// std::shared_ptr<NumMwmIds> m_numMwmIds;
|
||||
// std::unordered_map<NumMwmId, double> 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);
|
||||
|
||||
/**
|
||||
* @brief Calculates the climb penalty for cars.
|
||||
*
|
||||
* 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 GetCarClimbPenalty(EdgeEstimator::Purpose purpose, double tangent, geometry::Altitude altitudeM);
|
||||
|
||||
} // namespace routing
|
||||
|
||||
@@ -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<m2::PointD> 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<Segment> const & segments, m2::PointD const & point, WorldGraph & graph)
|
||||
FakeEnding MakeFakeEnding(vector<Segment> const & segments, m2::PointD const & point,
|
||||
WorldGraph & graph, bool snapToEnds)
|
||||
{
|
||||
FakeEnding ending;
|
||||
double averageAltitude = 0.0;
|
||||
@@ -57,7 +58,7 @@ FakeEnding MakeFakeEnding(vector<Segment> 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<Segment> 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());
|
||||
|
||||
@@ -40,9 +40,11 @@ struct FakeEnding final
|
||||
std::vector<Projection> m_projections;
|
||||
};
|
||||
|
||||
FakeEnding MakeFakeEnding(std::vector<Segment> const & segments, m2::PointD const & point, WorldGraph & graph);
|
||||
FakeEnding MakeFakeEnding(Segment const & segment, m2::PointD const & point, IndexGraph & graph);
|
||||
FakeEnding MakeFakeEnding(std::vector<Segment> 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
|
||||
|
||||
@@ -143,9 +143,10 @@ void FeaturesRoadGraphBase::ForEachFeatureClosestToCross(m2::PointD const & cros
|
||||
}
|
||||
|
||||
void FeaturesRoadGraphBase::FindClosestEdges(m2::RectD const & rect, uint32_t count,
|
||||
vector<pair<Edge, geometry::PointWithAltitude>> & vicinities) const
|
||||
vector<pair<Edge, geometry::PointWithAltitude>> & vicinities,
|
||||
bool snapToEnds) const
|
||||
{
|
||||
NearestEdgeFinder finder(rect.Center(), nullptr /* IsEdgeProjGood */);
|
||||
NearestEdgeFinder finder(rect.Center(), nullptr /* IsEdgeProjGood */, snapToEnds);
|
||||
|
||||
m_dataSource.ForEachStreet([&](FeatureType & ft)
|
||||
{
|
||||
|
||||
@@ -84,8 +84,21 @@ public:
|
||||
/// @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<std::pair<Edge, geometry::PointWithAltitude>> & vicinities) const override;
|
||||
std::vector<std::pair<Edge, geometry::PointWithAltitude>> & vicinities,
|
||||
bool snapToEnds = false) const override;
|
||||
|
||||
std::vector<IRoadGraph::FullRoadInfo> FindRoads(m2::RectD const & rect,
|
||||
IsGoodFeatureFn const & isGoodFeature) const override;
|
||||
void GetFeatureTypes(FeatureID const & featureId, feature::TypesHolder & types) const override;
|
||||
|
||||
@@ -217,6 +217,9 @@ template <typename AccessPositionType>
|
||||
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);
|
||||
|
||||
@@ -123,10 +123,10 @@ unique_ptr<DirectionsEngine> CreateDirectionsEngine(VehicleType vehicleType, sha
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
shared_ptr<TrafficStash> CreateTrafficStash(VehicleType, shared_ptr<NumMwmIds>, traffic::TrafficCache const &)
|
||||
shared_ptr<TrafficStash> CreateTrafficStash(VehicleType vehicleType, shared_ptr<NumMwmIds> numMwmIds,
|
||||
traffic::TrafficCache const & trafficCache)
|
||||
{
|
||||
return nullptr;
|
||||
// return (vehicleType == VehicleType::Car ? make_shared<TrafficStash>(trafficCache, numMwmIds) : nullptr);
|
||||
return (vehicleType == VehicleType::Car ? make_shared<TrafficStash>(trafficCache, numMwmIds) : nullptr);
|
||||
}
|
||||
|
||||
void PushPassedSubroutes(Checkpoints const & checkpoints, vector<Route::SubrouteAttrs> & subroutes)
|
||||
@@ -262,6 +262,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> numMwmIds, unique_ptr<m4::Tree<NumMwmId>> numMwmTree,
|
||||
std::shared_ptr<EdgeEstimator> 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<WorldGraph> IndexRouter::MakeSingleMwmWorldGraph()
|
||||
{
|
||||
auto worldGraph = MakeWorldGraph();
|
||||
@@ -283,7 +314,7 @@ bool IndexRouter::FindClosestProjectionToRoad(m2::PointD const & point, m2::Poin
|
||||
std::vector<EdgeProjectionT> candidates;
|
||||
|
||||
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 +547,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);
|
||||
@@ -1060,10 +1091,15 @@ RouterResultCode IndexRouter::AdjustRoute(Checkpoints const & checkpoints, m2::P
|
||||
return RouterResultCode::NoError;
|
||||
}
|
||||
|
||||
RoutingOptions IndexRouter::GetRoutingOptions()
|
||||
{
|
||||
return RoutingOptions::LoadCarOptionsFromSettings();
|
||||
}
|
||||
|
||||
unique_ptr<WorldGraph> 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 +1146,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 +1231,11 @@ bool IndexRouter::PointsOnEdgesSnapping::IsFencedOff(m2::PointD const & point, E
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
void IndexRouter::PointsOnEdgesSnapping::RoadsToNearestEdges(m2::PointD const & point, vector<RoadInfoT> const & roads,
|
||||
IsEdgeProjGood const & isGood,
|
||||
vector<EdgeProjectionT> & edgeProj)
|
||||
{
|
||||
NearestEdgeFinder finder(point, isGood);
|
||||
NearestEdgeFinder finder(point, isGood, (m_router.GetMode() == Mode::Decoding));
|
||||
for (auto const & road : roads)
|
||||
finder.AddInformationSource(road);
|
||||
|
||||
@@ -1324,7 +1359,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.
|
||||
|
||||
@@ -44,6 +44,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:
|
||||
@@ -68,6 +86,24 @@ public:
|
||||
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<NumMwmIds> numMwmIds, std::unique_ptr<m4::Tree<NumMwmId>> numMwmTree,
|
||||
@@ -90,6 +126,63 @@ public:
|
||||
|
||||
VehicleType GetVehicleType() const { return m_vehicleType; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Creates a new `IndexRouter` instance.
|
||||
*
|
||||
* This constructor is intended for use by the TraFF decoder, not for normal routing. It differs
|
||||
* from the general-purpose constructor in two ways.
|
||||
*
|
||||
* It takes an explicit `EdgeEstimator` argument, instance, which gives the caller fine-grained
|
||||
* control over the cost calculations used for routing by supplying an `EdgeEstimator` of their
|
||||
* choice.
|
||||
*
|
||||
* It also lacks the `TrafficCache` argument and never creates a traffic stash. This creates a
|
||||
* router instance which ignores the traffic situation, regardless of 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
|
||||
* @param numMwmTree
|
||||
* @param estimator An edge estimator
|
||||
* @param dataSource The MWM data source
|
||||
*/
|
||||
IndexRouter(VehicleType vehicleType, bool loadAltitudes,
|
||||
CountryParentNameGetterFn const & countryParentNameGetterFn,
|
||||
TCountryFileFn const & countryFileFn, CountryRectFn const & countryRectFn,
|
||||
std::shared_ptr<NumMwmIds> numMwmIds, std::unique_ptr<m4::Tree<NumMwmId>> numMwmTree,
|
||||
std::shared_ptr<EdgeEstimator> estimator, DataSource & dataSource);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns the mode in which the router is operating.
|
||||
*
|
||||
* The `IndexRouter` always returns `Mode::Navigation`; subclasses may override this method and
|
||||
* return different values.
|
||||
*
|
||||
* In navigation mode, the router may exit with `RouterResultCode::NeedMoreMaps` if it determines
|
||||
* that a better route can be calculated with additional maps. When snapping endpoints to edges,
|
||||
* it will consider only edges which are not “fenced off” by other edges, i.e. which can be
|
||||
* reached from the endpoint without crossing other edges. This decreases the number of fake
|
||||
* endings and thus speeds up routing, without any undesirable side effects for that use case.
|
||||
*
|
||||
* In decoding mode, the router will never exit with `RouterResultCode::NeedMoreMaps`: it will try
|
||||
* to find a route with the existing maps, or exit without finding a route. When snapping
|
||||
* endpoints to edges, it considers all edges within the given radius, fenced off or not.
|
||||
*/
|
||||
virtual Mode GetMode() { return Mode::Navigation; }
|
||||
|
||||
/**
|
||||
* @brief Returns current routing options.
|
||||
*
|
||||
* In this class, the routing options are the one set in the GUI. Subclasses may override this
|
||||
* method to provide different routing options.
|
||||
*/
|
||||
virtual RoutingOptions GetRoutingOptions();
|
||||
|
||||
private:
|
||||
RouterResultCode CalculateSubrouteJointsMode(IndexGraphStarter & starter, RouterDelegate const & delegate,
|
||||
std::shared_ptr<AStarProgress> const & progress,
|
||||
@@ -154,8 +247,8 @@ private:
|
||||
static bool IsFencedOff(m2::PointD const & point, EdgeProjectionT const & edgeProjection,
|
||||
std::vector<RoadInfoT> const & fences);
|
||||
|
||||
static void RoadsToNearestEdges(m2::PointD const & point, std::vector<RoadInfoT> const & roads,
|
||||
IsEdgeProjGood const & isGood, std::vector<EdgeProjectionT> & edgeProj);
|
||||
void RoadsToNearestEdges(m2::PointD const & point, std::vector<RoadInfoT> const & roads,
|
||||
IsEdgeProjGood const & isGood, std::vector<EdgeProjectionT> & edgeProj);
|
||||
|
||||
Segment GetSegmentByEdge(Edge const & edge) const;
|
||||
|
||||
|
||||
@@ -9,9 +9,11 @@ namespace routing
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
NearestEdgeFinder::NearestEdgeFinder(m2::PointD const & point, IsEdgeProjGood const & isEdgeProjGood)
|
||||
NearestEdgeFinder::NearestEdgeFinder(m2::PointD const & point, IsEdgeProjGood const & isEdgeProjGood,
|
||||
bool snapToEnds)
|
||||
: m_point(point)
|
||||
, m_isEdgeProjGood(isEdgeProjGood)
|
||||
, m_snapToEnds(snapToEnds)
|
||||
{}
|
||||
|
||||
void NearestEdgeFinder::AddInformationSource(IRoadGraph::FullRoadInfo const & roadInfo)
|
||||
@@ -28,7 +30,7 @@ void NearestEdgeFinder::AddInformationSource(IRoadGraph::FullRoadInfo const & ro
|
||||
{
|
||||
m2::ParametrizedSegment<m2::PointD> segment(junctions[i - 1].GetPoint(), junctions[i].GetPoint());
|
||||
|
||||
m2::PointD const closestPoint = segment.ClosestPointTo(m_point);
|
||||
m2::PointD const closestPoint = segment.ClosestPointTo(m_point, m_snapToEnds);
|
||||
double const squaredDist = m_point.SquaredLength(closestPoint);
|
||||
|
||||
if (squaredDist < res.m_squaredDist)
|
||||
@@ -48,7 +50,7 @@ void NearestEdgeFinder::AddInformationSource(IRoadGraph::FullRoadInfo const & ro
|
||||
geometry::Altitude const startAlt = segStart.GetAltitude();
|
||||
geometry::Altitude const endAlt = segEnd.GetAltitude();
|
||||
m2::ParametrizedSegment<m2::PointD> segment(junctions[idx - 1].GetPoint(), junctions[idx].GetPoint());
|
||||
m2::PointD const closestPoint = segment.ClosestPointTo(m_point);
|
||||
m2::PointD const closestPoint = segment.ClosestPointTo(m_point, m_snapToEnds);
|
||||
|
||||
double const segLenM = mercator::DistanceOnEarth(segStart.GetPoint(), segEnd.GetPoint());
|
||||
geometry::Altitude projPointAlt = geometry::kDefaultAltitudeMeters;
|
||||
|
||||
@@ -27,7 +27,12 @@ using IsEdgeProjGood = std::function<bool(std::pair<Edge, geometry::PointWithAlt
|
||||
class NearestEdgeFinder
|
||||
{
|
||||
public:
|
||||
NearestEdgeFinder(m2::PointD const & point, IsEdgeProjGood const & isEdgeProjGood);
|
||||
/**
|
||||
* @param snapToEnds If false, projections of `point` can be anywhere on a segment;
|
||||
* if true, `point` is projected onto the nearest segment endpoint.
|
||||
*/
|
||||
NearestEdgeFinder(m2::PointD const & point, IsEdgeProjGood const & isEdgeProjGood,
|
||||
bool snapToEnds = false);
|
||||
|
||||
inline bool HasCandidates() const { return !m_candidates.empty(); }
|
||||
|
||||
@@ -56,6 +61,7 @@ private:
|
||||
std::vector<EdgeProjectionT> & res) const;
|
||||
|
||||
m2::PointD const m_point;
|
||||
bool m_snapToEnds;
|
||||
std::vector<Candidate> m_candidates;
|
||||
IsEdgeProjGood m_isEdgeProjGood;
|
||||
};
|
||||
|
||||
@@ -263,7 +263,7 @@ public:
|
||||
/// then returns empty array.
|
||||
using EdgeProjectionT = std::pair<Edge, JunctionPointT>;
|
||||
virtual void FindClosestEdges(m2::RectD const & /*rect*/, uint32_t /*count*/,
|
||||
std::vector<EdgeProjectionT> & /*vicinities*/) const
|
||||
std::vector<EdgeProjectionT> & /*vicinities*/, bool snapToEnds) const
|
||||
{}
|
||||
|
||||
/// \returns Vector of pairs FeatureID and corresponding RoadInfo for road features
|
||||
|
||||
@@ -9,10 +9,11 @@
|
||||
|
||||
namespace routing
|
||||
{
|
||||
// RoadPoint is a unique identifier for any road point in mwm file.
|
||||
//
|
||||
// Contains feature id and point id.
|
||||
// Point id is the ordinal number of the point in the road.
|
||||
/**
|
||||
* @brief A unique identifier for any point on a road in an mwm file.
|
||||
*
|
||||
* It contains a feature id and point id. The point id is the ordinal number of the point in the road.
|
||||
*/
|
||||
class RoadPoint final
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -39,8 +39,21 @@ namespace routing
|
||||
using SubrouteUid = uint64_t;
|
||||
SubrouteUid constexpr kInvalidSubrouteId = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
/// \brief The route is composed of one or several subroutes. Every subroute is composed of segments.
|
||||
/// For every Segment is kept some attributes in the structure SegmentInfo.
|
||||
/**
|
||||
* @brief A segment of the route.
|
||||
*
|
||||
* The route is composed of one or several subroutes. Every subroute is composed of segments.
|
||||
*
|
||||
* For every Segment, some attributes are kept in the `SegmentInfo` structure.
|
||||
*
|
||||
* @todo the statement regarding `SegmentInfo` seems to be outdated, is `SegmentInfo` a prececessor
|
||||
* of `RouteSegment`?
|
||||
*
|
||||
* Segment data which is actually related to a point, such as junction, distance and time, refer to
|
||||
* the end which is closer to the end of the route. For the first segment, distance and time are the
|
||||
* length and travel time of the segment itself. For the last segment, distance and time are the
|
||||
* length and travel time of the entore route, and the junction is the finish point.
|
||||
*/
|
||||
class RouteSegment final
|
||||
{
|
||||
public:
|
||||
@@ -69,17 +82,50 @@ public:
|
||||
uint8_t m_maxSpeedKmPH = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Holds structured information about a road.
|
||||
*
|
||||
* `m_ref` and `m_name` refer to the road itself.
|
||||
*
|
||||
* This structure is only populated for the first segment of a feature (segment index is either 0,
|
||||
* or one less than the segment count of the feature, or the segment is the first segment of the
|
||||
* route which is not a fake segment). For subsequent segments of the same feature, it is empty.
|
||||
*/
|
||||
struct RoadNameInfo
|
||||
{
|
||||
// This is for street/road. |m_ref| |m_name|.
|
||||
std::string m_name; // E.g "Johnson Ave.".
|
||||
std::string m_destination_ref; // Number of next road, e.g. "CA 85", Sometimes "CA 85 South". Usually match |m_ref|
|
||||
// of next main road.
|
||||
// This is for 1st segment of link after junction. Exit |junction_ref| to |m_destination_ref| for |m_destination|.
|
||||
std::string m_junction_ref; // Number of junction e.g. "398B".
|
||||
std::string m_destination; // E.g. "Cupertino".
|
||||
std::string m_ref; // Number of street/road e.g. "CA 85".
|
||||
/**
|
||||
* @brief The name of the road, e.g. “Johnson Ave”.
|
||||
*/
|
||||
std::string m_name;
|
||||
/**
|
||||
* @brief The number of the next road.
|
||||
*
|
||||
* This usually matches `m_ref` of the naxt main road, e.g. “CA 85”, sometimes “CA 85 South”.
|
||||
*/
|
||||
std::string m_destination_ref;
|
||||
/**
|
||||
* @brief The junction number, e.g. “398B”.
|
||||
*
|
||||
* This is used for the first link segment after a junction (exit `junction_ref` to
|
||||
* `m_destination_ref` for `m_destination`).
|
||||
*/
|
||||
std::string m_junction_ref;
|
||||
/**
|
||||
* @brief The destination of the road, e.g. “Cupertino”.
|
||||
*/
|
||||
std::string m_destination;
|
||||
/**
|
||||
* @brief The number of the road, e.g. “CA85”.
|
||||
*/
|
||||
std::string m_ref;
|
||||
/**
|
||||
* @brief Whether the road is of a link type.
|
||||
*/
|
||||
bool m_isLink = false;
|
||||
/**
|
||||
* @brief Whether the road is part of a roundabout.
|
||||
*/
|
||||
bool m_onRoundabout = false;
|
||||
|
||||
RoadNameInfo() = default;
|
||||
RoadNameInfo(std::string name) : m_name(std::move(name)) {}
|
||||
@@ -158,8 +204,30 @@ public:
|
||||
turns::TurnItem const & GetTurn() const { return m_turn; }
|
||||
void ClearTurnLanes() { m_turn.m_lanes.clear(); }
|
||||
|
||||
/**
|
||||
* @brief Returns distance from the beginning of the route in meters.
|
||||
*
|
||||
* Distance is measured up to the end of the current segment, i.e. including the segment. For the
|
||||
* first segment, this is identical to the length of the segment; for the last segment, it is
|
||||
* identical to the length of the entire route.
|
||||
*
|
||||
* Note that the first and last real (non-fake) segment on the route may not report the actual
|
||||
* length between their endpoints, but only the part which is also part of the route.
|
||||
*/
|
||||
double GetDistFromBeginningMeters() const { return m_distFromBeginningMeters; }
|
||||
double GetDistFromBeginningMerc() const { return m_distFromBeginningMerc; }
|
||||
|
||||
/**
|
||||
* @brief Returns travel time from the beginning of the route.
|
||||
*
|
||||
* Travel time is the ETA from the beginning of the route to the end of the current segment, i.e.
|
||||
* including the segment. For the first segment, this is identical to the travel time along the
|
||||
* segment; for the last segment, it is identical to the travel time along the entire route.
|
||||
*
|
||||
* Note that the first and last real (non-fake) segment on the route may report time based not
|
||||
* upon the actual length between their endpoints, but upon the part which is also part of the
|
||||
* route.
|
||||
*/
|
||||
double GetTimeFromBeginningSec() const { return m_timeFromBeginningS; }
|
||||
|
||||
bool HasTransitInfo() const { return m_transitInfo.HasTransitInfo(); }
|
||||
|
||||
@@ -45,16 +45,49 @@ enum class RouterResultCode
|
||||
|
||||
enum class SessionState
|
||||
{
|
||||
NoValidRoute, // No valid route: no route after application launching or the route was removed.
|
||||
RouteBuilding, // We requested a route and wait when it will be built. User may be following
|
||||
// the previous route.
|
||||
RouteNotStarted, // Route is built but the user isn't on it.
|
||||
OnRoute, // User follows the route.
|
||||
RouteNeedRebuild, // User left the route.
|
||||
RouteFinished, // Destination point is reached but the session isn't closed.
|
||||
RouteNoFollowing, // Route is built but following mode has been disabled.
|
||||
RouteRebuilding, // We requested a route rebuild and wait when it will be rebuilt.
|
||||
// User may following the previous route.
|
||||
/**
|
||||
* No valid route: no route after application launching, or the route was removed.
|
||||
*
|
||||
* This is the initial state at launch; in order to get out of it, a destination must be set AND
|
||||
* the current location must be obtained.
|
||||
*/
|
||||
NoValidRoute,
|
||||
|
||||
/**
|
||||
* We have requested a route and are waiting for it to be built. User may be following the previous route.
|
||||
*/
|
||||
RouteBuilding,
|
||||
|
||||
/**
|
||||
* Route is built but the user isn't on it.
|
||||
*/
|
||||
RouteNotStarted,
|
||||
|
||||
/**
|
||||
* User is following the route.
|
||||
*/
|
||||
OnRoute,
|
||||
|
||||
/**
|
||||
* User has left the route.
|
||||
*/
|
||||
RouteNeedRebuild,
|
||||
|
||||
/**
|
||||
* Destination point has been reached but the session isn’t closed yet.
|
||||
*/
|
||||
RouteFinished,
|
||||
|
||||
/**
|
||||
* Route has been built but following mode has been disabled.
|
||||
*/
|
||||
RouteNoFollowing,
|
||||
|
||||
/**
|
||||
* We have requested a route rebuild and are waiting for it to be rebuilt. User may be following
|
||||
* the previous route.
|
||||
*/
|
||||
RouteRebuilding,
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -61,6 +61,26 @@ bool IsRoad(Types const & types)
|
||||
|
||||
void FillSegmentInfo(std::vector<double> const & times, std::vector<RouteSegment> & routeSegments);
|
||||
|
||||
/**
|
||||
* @brief Constructs or reconstructs a route.
|
||||
*
|
||||
* This function populates `route` with segments and geometry. Segments are calculated from `graph`
|
||||
* (the route graph) and `path` (points on the route); each pair of consecutive points becomes a
|
||||
* segment. The actual calculation is delegated to `engine` and can be influenced by passing a
|
||||
* different directions engine. Segment information is then enriched with the length of each segment
|
||||
* (calculated directly) and the estimated travel time specified in `times`. Geometry is calculated
|
||||
* from `path` by extracting latitude and longitude from each item.
|
||||
*
|
||||
* The number of items in `times` must be equal to the number of segments, or the number of items in
|
||||
* `points` minus 1. The items of `times` are travel times from start, therefore no value can be
|
||||
* less than the previous one.
|
||||
*
|
||||
* @param engine The directions engine
|
||||
* @param graph The route graph
|
||||
* @param path The route path, an ordered list of points on the route
|
||||
* @param times Travel times (from start) for each segment
|
||||
* @param route The route
|
||||
*/
|
||||
void ReconstructRoute(DirectionsEngine & engine, IndexRoadGraph const & graph, base::Cancellable const & cancellable,
|
||||
std::vector<geometry::PointWithAltitude> const & path, std::vector<double> const & times,
|
||||
Route & route);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user