Merge branch 'master' into continue_settings_refactor

# Conflicts:
#	OsmAnd/res/xml/navigation_settings_new.xml
#	OsmAnd/src/net/osmand/aidl/ConnectedApp.java
#	OsmAnd/src/net/osmand/plus/ContextMenuAdapter.java
#	OsmAnd/src/net/osmand/plus/OsmandPlugin.java
#	OsmAnd/src/net/osmand/plus/activities/MapActivity.java
#	OsmAnd/src/net/osmand/plus/activities/SettingsBaseActivity.java
#	OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java
#	OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java
#	OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java
#	OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java
#	OsmAnd/src/net/osmand/plus/helpers/LockHelper.java
#	OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java
#	OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java
#	OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenuFragment.java
#	OsmAnd/src/net/osmand/plus/mapmarkers/CoordinateInputDialogFragment.java
#	OsmAnd/src/net/osmand/plus/myplaces/TrackActivityFragmentAdapter.java
#	OsmAnd/src/net/osmand/plus/parkingpoint/ParkingPositionPlugin.java
#	OsmAnd/src/net/osmand/plus/rastermaps/OsmandRasterMapsPlugin.java
#	OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java
#	OsmAnd/src/net/osmand/plus/routepreparationmenu/RoutingOptionsHelper.java
#	OsmAnd/src/net/osmand/plus/routing/RouteProvider.java
#	OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java
#	OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureMenuItemsFragment.java
#	OsmAnd/src/net/osmand/plus/settings/fragments/RouteParametersFragment.java
#	OsmAnd/src/net/osmand/plus/settings/fragments/VehicleParametersFragment.java
#	OsmAnd/src/net/osmand/plus/settings/preferences/ListPreferenceEx.java
#	OsmAnd/src/net/osmand/plus/views/GPXLayer.java
#	OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java
#	OsmAnd/src/net/osmand/plus/views/layers/TransportStopsLayer.java
#	OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java
#	OsmAnd/src/net/osmand/plus/views/mapwidgets/RouteInfoWidgetsFactory.java
This commit is contained in:
Dima-1 2020-10-07 17:54:32 +03:00
commit ace5e92f93
1670 changed files with 68007 additions and 89609 deletions

View file

@ -0,0 +1,16 @@
---
name: "❓ Support request"
about: Questions and requests for support
---
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
Please do not file questions or support requests on the GitHub issues tracker.
In case you aren't sure whether to open an issue or not, please send your request to support@osmand.net.
You can get your questions answered using the [Google group](https://groups.google.com/forum/#!forum/osmand) or chat in real-time via our Telegram instances: [English](https://t.me/OsmAndMaps), [German](https://t.me/deosmand), [French](https://t.me/frosmand), [Ukrainian](https://t.me/uaosmand), [Italian](https://t.me/itosmand), [Russian](https://t.me/ruosmand).
Thank you!
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑

12
.github/ISSUE_TEMPLATE/2-faq-report.md vendored Normal file
View file

@ -0,0 +1,12 @@
---
name: "📚 Outdated FAQ"
about: Report an issue in FAQ
---
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
Please do not file FAQ issues on the GitHub issues tracker.
Instead use the [Google group](https://groups.google.com/forum/#!forum/osmand) to fix wrong or outdated FAQ.
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑

18
.github/ISSUE_TEMPLATE/3-bug-report.md vendored Normal file
View file

@ -0,0 +1,18 @@
---
name: "\U0001F41E Bug report"
about: Report a bug in OsmAnd
---
### Description
### How to reproduce?
### Your Environment
OsmAnd Version:
Android/iOS version:
Device model:
**Maps used (online or offline):**
If you have an issue related to offline maps, tell us the exact name of the map file where the issue occurs and its edition date.

View file

@ -0,0 +1,72 @@
---
name: "\U0001F6A9 Routing report"
about: Report a routing issue in OsmAnd
---
<!--🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅
Oh hi there! 😄
To expedite issue processing please search open and closed issues before submitting a new one.
Existing issues often contain information about workarounds, resolution, or progress updates.
GitHub is our main development tool for our developers. There are hundreds of requests a month and there are relatively few developers.
So by opening an issue, please know that your issue will be sent out to all developers and acknowledge that it could be closed without explanation or with just a brief message.
Comments on the closed issues are also sent to all developers, so you will definitely will be heard.
However, there is no guarantee that a developer will pick up the issue to work on it.
Please be sure to read our [FAQ](https://osmand.net/help-online) before creating an issue here.
The best way to get help about an OsmAnd issue is to create a valid and detailed issue.
Please give us the following information so that we can try to **reproduce** your issue:
🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅-->
# 🐞 routing report
### Routing engine
<!-- Which routing provider was used? (please tick the proper box [x]) -->
- [ ] OsmAnd's in-app offline routing
- [ ] Any online routing provider (YOURS, OpenRouteService, OSRM, etc.)
### Routing Profile
<!-- What routing profile is chosen in the OsmAnd app? (car, bike, pedestrian, fastest or shortest, etc.) -->
### Start and end points
<!-- Please specify (as exactly as possible) the start and endpoint of your route by telling us the city and street name so that we can find it via the in-app address search easily.
Also, a permalink from [openstreetmap.org](https://www.openstreetmap.org/) can be helpful. -->
### Actual and expected routes
<!-- Tell us your expected routing and how OsmAnd routes, or add screenshots here. -->
### Is this a regression?
<!-- Did this behavior use to work in the previous version? -->
<!-- ✍️--> Yes, the previous version in which this bug was not present was: ....
## 🌍 Your Environment
**OsmAnd Version:**
<pre><code>
<!-- paste version below -->
<!-- ✍️-->
</code></pre>
**Device and Android/iOS version:**
**Maps used (online or offline):**
<!-- Please tick the correct box [x] (or both) -->
- [ ] Offline maps offered within the OsmAnd app for download.
<!-- If you have an issue related to offline maps, tell us the exact name of the map file where the issue occurs and its edition date. -->
- [ ] Online (tile / raster) maps <!-- Please name it -->
**Anything else relevant?**

View file

@ -0,0 +1,34 @@
---
name: "\U0001F680 Feature request"
about: Suggest a feature for OsmAnd
---
<!--🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅
Oh hi there! 😄
To expedite issue processing please search open and closed issues before submitting a new one.
Existing issues often contain information about workarounds, resolution, or progress updates.
GitHub is our main development tool for our developers. There are hundreds of requests a month and there are relatively few developers.
So by opening an issue, please know that your issue will be sent out to all developers and acknowledge that it could be closed without explanation or with just a brief message.
Comments on the closed issues are also sent to all developers, so you will definitely will be heard.
However, there is no guarantee that a developer will pick up the issue to work on it.
Please be sure to read our [FAQ](https://osmand.net/help-online) before creating an issue here.
🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅-->
# 🚀 feature request
### Description
<!-- ✍️ A clear and concise description of the feature... -->
### Describe the solution you'd like
<!-- ✍️--> If you have a solution in mind, please describe it.
### Describe alternatives you've considered
<!-- ✍️--> Have you considered any alternative solutions or workarounds?

View file

@ -0,0 +1,10 @@
name: "Validate Gradle Wrapper"
on: [push, pull_request]
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1

4
.gitignore vendored
View file

@ -19,6 +19,10 @@ OsmAndCore_*.aar
.project
out/
# Huawei
agconnect-services.json
OsmAndHms.jks
# Android Studio
/.idea
*.iml

View file

@ -1,7 +1,7 @@
### Credits to all major contributors/developers:
Major contributors /developers listed here https://github.com/osmandapp/osmandapp.github.io/blob/master/website/help/about.html#L8
### Credits to all major contributors / developers:
Major contributors / developers listed here https://github.com/osmandapp/osmandapp.github.io/blob/master/website/help/about.html#L8
### Other Pull requests
Copyright © All authors of translations and pull requests could be found in commits history:
Copyright © All authors of translations and pull requests can be found in the commit history:
- Translations are under special “contributor” name weblate
- Pull requests have two committers, first is original contributor and second is project maintainer
- Pull requests have two committers: first is original contributor, and second is project maintainer

View file

@ -1,26 +0,0 @@
Before entering an issue, please read the information below.
```GitHub is a main development tool for developers. There are hundreds requests a month and there are only few developers. So by opening an issue, please know that your issue will be sent out to all developers and acknowledge that it could be closed without explanation or with a brief message. Comments on the closed issues are also sent to all developers, so you will definitely will be heard. However, there is no guarantee that a developer will pick up the issue to work on it.```
In case you are not sure to open issue or not, please send your request to support support@osmand.net. For **general questions** about OsmAnd, please use the [Google group](https://groups.google.com/forum/#!forum/osmand).
Please be sure to have read our [FAQ](http://osmand.net/help-online) before creating an issue here. (Also, tell us in Google Groups when the FAQ is wrong or outdated.)
The best way to get help about an OsmAnd issue is to create a valid and detailed issue content.
Please give us the following information so that we can try to **reproduce** your issue:
What version of OsmAnd are you using, on what device and Android/iOS version?
Do you use the offline maps offered within the OsmAnd app for download, or online (tile / raster) maps?
If you have an issue related to offline maps, tell us the exact name of the map file where the issue occurs, and its edition date.
__*In case of wrong routing:*__
Tell us whether you have used OsmAnd's in-app offline routing, or any online routing provider like YOURS, OpenRouteService or OSRM.
What routing profile is chosen in OsmAnd app? (car, bike, pedestrian, fastest or shortest)
Please specify as exactly as possible the start and end point of your route by telling us city name and street name so that we can find it via in-app address search easily. Also, a permalink from openstreetmap.org can be helpful.
Tell us your expected routing, and how OsmAnd routes, or add screenshots here.

70
LICENSE
View file

@ -1,11 +1,11 @@
OsmAnd OSM Automated Navigation Directions navigation software based on OpenStreetMap.
Copyright © 20102018 OsmAnd BV (Amstelveen, Netherlands - KvK 62066714, BTW 854627704B01).
Copyright © 20102020 OsmAnd BV (Amstelveen, Netherlands - KvK 62066714, BTW 854627704B01).
The code in this repository is covered by *GPLv3* (for code) and the artwork is covered by [ CC-BY-NC-ND 4.0 ](https://creativecommons.org/licenses/by-nc-nd/4.0/) (for artwork), except some 3rd party libs and resources.
ATTENTION: please be aware that some artwork has proprietary license.
The code in this repository is covered by *GPLv3* (for code) and the artwork is covered by [CC-BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/) (for artwork), except for some third-party libraries and resources.
ATTENTION: Please be aware that some artwork uses a proprietary license.
Main AUTHORS (more detailed list in AUTHORS):
Main AUTHORS (more detailed list in AUTHORS.md):
Victor Shcherb all parts of the project, originator
Alexey Pelykh rendering and core parts
Hardy Mueller main parts of the project, mostly rendering, UI interaction design
@ -30,33 +30,33 @@
* Source code in main repositories is covered by GPLv3 (license exceptions below):
- https://github.com/osmandapp/Osmand/tree/master/OsmAnd
- https://github.com/osmandapp/Osmand/tree/master/OsmAnd-java
- https://github.com/osmandapp/Osmand-core/tree/legacy_core
- https://github.com/osmandapp/Osmand-core/
- https://github.com/osmandapp/OsmAnd-core/tree/legacy_core
- https://github.com/osmandapp/OsmAnd-core
- https://github.com/osmandapp/OsmAnd-misc
- https://github.com/osmandapp/OsmAnd-tools
- https://github.com/osmandapp/OsmAnd-resources
* UI Design and UX work, such as layout, icons is covered by CC-BY-NC-ND
* UI Design and UX work, such as layout and icons, are covered by [CC-BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/)
- https://github.com/osmandapp/Osmand/tree/master/OsmAnd/res and others
Restriction to UI/UX CC-BY-NC-ND:
* Publishing applications using the OsmAnd UI/UX code to Google Play, Amazon Market or Apple Store must be done with written permission*
* Publishing applications using the OsmAnd UI/UX code to Google Play, Amazon Market, or Apple Store must be done with written permission.*
* Map icons are taken from sjjb and some are drawn and distributed under Public Domain (except icons8)
- https://github.com/osmandapp/OsmAnd-resources/tree/master/icons/svg
- some icons are from http://osm-icons.org/wiki/Icons (CC-0 by Markus59, CC BY-SA 2.0)
* Integration with Qt library in (https://github.com/osmandapp/Osmand-core/) - dynamic linking
- LGPL (http://www.qt.io/qt-licensing-terms/)
* Integration with Qt library in (https://github.com/osmandapp/OsmAnd-core) - dynamic linking
- LGPL (https://www.qt.io/terms-conditions/)
* Map icons and their derived primitives are covered by proprietary license:
* Map icons and their derived primitives are covered by a proprietary license:
- © icons8.com (https://github.com/osmandapp/OsmAnd-resources/tree/master/icons/svg/icons8)
* Generated voice files from TTS files are covered by proprietary license:
* Generated voice files from TTS files are covered by a proprietary license:
- Voice files (https://github.com/osmandapp/OsmAnd-resources/tree/master/voice)
* Fonts (https://github.com/osmandapp/OsmAnd-resources/tree/master/rendering_styles/fonts)
- Google Fonts (Apache License 2)
- Google Fonts (Apache License 2.0)
* Map icons (Mapnik):
- Open-SVG-Map-Icons - Public Domain
@ -65,49 +65,27 @@
- SherlockBar - Apache License - https://github.com/osmandapp/Osmand/tree/master/SherlockBar (https://github.com/JakeWharton/ActionBarSherlock/blob/master/LICENSE.txt)
* Patched libraries:
- Protobuf - New BSD License (patches - https://github.com/osmandapp/Osmand/tree/master/OsmAnd-java/protobuf-src, https://github.com/osmandapp/OsmAnd-core/tree/legacy_core/externals/protobuf)
- Expat - (https://github.com/osmandapp/OsmAnd-core/tree/legacy_core/externals/expat)
- Freetype - (https://github.com/osmandapp/OsmAnd-core/tree/legacy_core/externals/freetype)
- Giflib - (https://github.com/osmandapp/OsmAnd-core/tree/legacy_core/externals/giflib)
- Protobuf - New BSD License (patches - https://github.com/osmandapp/Osmand/tree/master/OsmAnd-java/src/main/java/com/google/protobuf, https://github.com/osmandapp/OsmAnd-core/tree/legacy_core/externals/protobuf)
- Jpeg - (https://github.com/osmandapp/OsmAnd-core/tree/legacy_core/externals/jpeg)
- Libpng - (https://github.com/osmandapp/OsmAnd-core/tree/legacy_core/externals/libpng)
- Skia - (https://github.com/osmandapp/OsmAnd-core/tree/legacy_core/externals/skia)
- ZLib - (https://github.com/osmandapp/OsmAnd-core/tree/legacy_core/externals/zlib)
- android-openmap-framework - (https://github.com/osmandapp/Osmand/tree/master/OsmAnd-java/src/main/java/com/jwetherell/openmap/common)
- mapbox-vector-tile - (https://github.com/osmandapp/Osmand/tree/master/OsmAnd-java/src/main/java/com/wdtinc/mapbox_vector_tile)
* 3rd party libraries present in the libs folder (https://github.com/osmandapp/Osmand/tree/master/OsmAnd-java/libs):
- bzip2-20090327.jar Bzip2 - Apache License
- commons-logging-1.1.1.jar - Apache License
- gnu-trove-osmand.jar GNU trove - LGPL
- icu4j-49_1.jar - ICU license (http://source.icu-project.org/repos/icu/icu/trunk/license.html)
- json-20090211.jar - BSD alike (http://www.json.org/license.html)
- junidecode-0.1.jar - BSD-4-Clause-UC (http://sourceforge.net/projects/junidecode/)
- kxml2-2.3.0.jar - BSD license (http://www.kxml.org/)
- tuprolog.jar - LGPL (http://apice.unibo.it/xwiki/bin/view/Tuprolog/)
- OpenMap framework - Apache License (https://code.google.com/p/android-openmap-framework/)
- icu4j-49_1_patched.jar - ICU license (https://home.unicode.org/basic-info/projects/#/icu/icu/trunk/license.html)
* Pull-requests and translations:
- All pull requests are accepted under MIT License (most honorable contributors are mentioned in AUTHORS list)
* Libraries not used in final product (https://github.com/osmandapp/OsmAnd-tools/tree/master/OsmAndMapCreator/libi)
- Ant libraries - (tools) https://github.com/osmandapp/Osmand/tree/master/OsmAnd/ant-lib
- jleveldb-v01.jar
- jsch-20120101.jar
- junit-4.10.jar
- mysql-connector-java-5.1.18-bin.jar
- sqlite-jdbc-3.7.6.3-20110609.081603-3.jar
- h2-latest.jar
- All pull requests are accepted under MIT License (most honorable contributors are mentioned in AUTHORS list).
* Special tools for new rendering (GPLv3)
- https://github.com/osmandapp/OsmAnd-tools/tree/master/obf-inspector
- https://github.com/osmandapp/OsmAnd-tools/tree/master/obf-verifier
- https://github.com/osmandapp/OsmAnd-tools/tree/master/route-tester
- https://github.com/osmandapp/OsmAnd-tools
* OSM data 2014
- Extracts - https://github.com/osmandapp/OsmAnd-misc/blob/master/osm-planet/osm-data/
* OSM data
- Extracts - https://github.com/osmandapp/OsmAnd-misc/tree/master/osm-planet/osm-data
* Data files
- Country boundaries - https://github.com/osmandapp/OsmAnd-misc/tree/master/osm-planet/geo-polygons © by Geofabrik - data free to use
- Country boundaries - https://github.com/osmandapp/OsmAnd-misc/tree/master/osm-planet/gislab-polygons © by http://be.gis-lab.info - data free to use
- Country boundaries - https://github.com/osmandapp/OsmAnd-misc/tree/master/osm-planet/polygons © by Geofabrik - data free to use
* Subway icons
- Moscow: Art Lebedev Studio (http://www.artlebedev.ru/everything/metro/logo/) [Public domain], undefined
@ -119,7 +97,7 @@
- Kazan: «Kazan-metro-Logo» (Россиянин) - own work. Under Public domain Commons license from the site - https://commons.wikimedia.org/wiki/File:Kazan-metro-Logo.svg#/media/File:Kazan-metro-Logo.svg
- Tbilisi: «Metro Tbilisi logo» (Carnby) - own work. Under CC BY-SA 3.0 license from site - https://commons.wikimedia.org/wiki/File:Metro_Tbilisi_logo.svg#/media/File:Metro_Tbilisi_logo.svg
- Minsk: «Minsk metro logo» (Skip405) - own work. Under Public domain Commons license from the site - https://commons.wikimedia.org/wiki/File:Minsk_metro_logo.svg#/media/File:Minsk_metro_logo.svg
- Nizhny Novgorod: «NNMetro» (AlexTref871) - own work. This vector image includes elements borrowed from another image:  Coat of arms of Nizhny Novgorod Region.svg.. Under Public domain Commons licensefrom the site - https://commons.wikimedia.org/wiki/File:NNMetro.svg#/media/File:NNMetro.svg
- Nizhny Novgorod: «NNMetro» (AlexTref871) - own work. This vector image includes elements borrowed from another image: Coat of arms of Nizhny Novgorod Region.svg.. Under Public domain Commons licensefrom the site - https://commons.wikimedia.org/wiki/File:NNMetro.svg#/media/File:NNMetro.svg
- Novosibirsk: «Logo-Nsk-Metro» (AlexTref871) - own work. Under Public domain Commons license from the site - https://commons.wikimedia.org/wiki/File:Logo-Nsk-Metro.svg#/media/File:Logo-Nsk-Metro.svg
- Yekaterinburg: «Ekt-metro-logo-01» (AlexTref871) - own work. Under CC BY-SA 4.0 license from site - https://commons.wikimedia.org/wiki/File:Ekt-metro-logo-01.svg#/media/File:Ekt-metro-logo-01.svg
- New York: «MTA New York City Subway logo» (Metropolitan Transportation Authority) - http://mta.info/. Under Public domain Commons license from the site - https://commons.wikimedia.org/wiki/File:MTA_New_York_City_Subway_logo.svg#/media/File:MTA_New_York_City_Subway_logo.svg
@ -152,7 +130,7 @@
- Los Angeles: "Lametro" by Los Angeles County Metropolitan Transportation Authority. Original uploader was Cheeselouise at en.wikipedia - Metro materials at http://www.metro.net/riding_metro/pocket_guide_cambodian_printers.pdf. Licensed under Public Domain via Commons - https://commons.wikimedia.org/wiki/File:Lametro.svg#/media/File:Lametro.svg
* Depth maps
- The GEBCO_2014 Grid, version 20150318, www.gebco.net
- The GEBCO_2020 Grid, www.gebco.net
- U.S. Bureau of Ocean Energy Management (24 may 2017,12m per pixel)
- South_Padre_Island_DEM_4133: Grothe, P.G., L.A. Taylor, B.W. Eakins, K.S. Carignan, D.Z. Friday, and M. Love, 2012. Digital Elevation Models of South Padre Island, Texas: Procedures, Data Sources and Analysis, NOAA National Geophysical Data Center technical report, Boulder, CO, 15 pp.
- Corpus_Christi_DEM_1035: Taylor, L.A., B.W. Eakins, K.S. Carignan, R.R. Warnken, T. Sazonova, and D.C. Schoolcraft, 2008. Digital Elevation Model of Corpus Christi, Texas: Procedures, Data Sources and Analysis, NOAA Technical Memorandum NESDIS NGDC-11, National Geophysical Data Center, Boulder, CO, 19 pp.

View file

@ -51,4 +51,9 @@ interface IOsmAndAidlCallback {
* Callback for {@link IOsmAndAidlInterface} registerForVoiceRouterMessages() method.
*/
void onVoiceRouterNotify(in OnVoiceNavigationParams params);
/**
* Callback for {@link IOsmAndAidlInterface} registerForKeyEvents() method.
*/
void onKeyEvent(in KeyEvent params);
}

View file

@ -20,6 +20,8 @@ import net.osmand.aidlapi.mapmarker.UpdateMapMarkerParams;
import net.osmand.aidlapi.calculateroute.CalculateRouteParams;
import net.osmand.aidlapi.profile.ExportProfileParams;
import net.osmand.aidlapi.gpx.ImportGpxParams;
import net.osmand.aidlapi.gpx.ShowGpxParams;
import net.osmand.aidlapi.gpx.StartGpxRecordingParams;
@ -74,6 +76,7 @@ import net.osmand.aidlapi.customization.OsmandSettingsParams;
import net.osmand.aidlapi.customization.OsmandSettingsInfoParams;
import net.osmand.aidlapi.customization.CustomizationInfoParams;
import net.osmand.aidlapi.customization.ProfileSettingsParams;
import net.osmand.aidlapi.customization.MapMarginsParams;
import net.osmand.aidlapi.gpx.AGpxFile;
import net.osmand.aidlapi.gpx.AGpxFileDetails;
@ -96,6 +99,14 @@ import net.osmand.aidlapi.mapmarker.RemoveMapMarkersParams;
import net.osmand.aidlapi.quickaction.QuickActionParams;
import net.osmand.aidlapi.quickaction.QuickActionInfoParams;
import net.osmand.aidlapi.lock.SetLockStateParams;
import net.osmand.aidlapi.events.AKeyEventsParams;
import net.osmand.aidlapi.info.AppInfoParams;
import net.osmand.aidlapi.profile.ExportProfileParams;
// NOTE: Add new methods at the end of file!!!
interface IOsmAndAidlInterface {
@ -841,4 +852,25 @@ interface IOsmAndAidlInterface {
boolean executeQuickAction(in QuickActionParams params);
boolean getQuickActionsInfo(out List<QuickActionInfoParams> quickActions);
/**
* Toggle Lock/Unlock screen.
*/
boolean setLockState(in SetLockStateParams params);
/**
* Method to register for key events.
*
* @params subscribeToUpdates (boolean) - boolean flag to subscribe or unsubscribe from key events
* @params callbackId (long) - id of callback, needed to unsubscribe key events
* @params callback (IOsmAndAidlCallback) - callback to notify user on key event
* @params keyEventList (List<Integer>) - list of requested key events
*/
long registerForKeyEvents(in AKeyEventsParams params, IOsmAndAidlCallback callback);
AppInfoParams getAppInfo();
boolean setMapMargins(in MapMarginsParams params);
boolean exportProfile(in ExportProfileParams params);
}

View file

@ -4,6 +4,8 @@ public interface OsmAndCustomizationConstants {
// Navigation Drawer:
String DRAWER_ITEM_ID_SCHEME = "drawer.action.";
String DRAWER_SWITCH_PROFILE_ID = DRAWER_ITEM_ID_SCHEME + "switch_profile";
String DRAWER_CONFIGURE_PROFILE_ID = DRAWER_ITEM_ID_SCHEME + "configure_profile";
String DRAWER_DASHBOARD_ID = DRAWER_ITEM_ID_SCHEME + "dashboard";
String DRAWER_MAP_MARKERS_ID = DRAWER_ITEM_ID_SCHEME + "map_markers";
String DRAWER_MY_PLACES_ID = DRAWER_ITEM_ID_SCHEME + "my_places";

View file

@ -9,12 +9,21 @@ import net.osmand.aidlapi.AidlParams;
public class CopyFileParams extends AidlParams {
public static final String DESTINATION_DIR_KEY = "destinationDir";
public static final String FILE_NAME_KEY = "fileName";
public static final String FILE_PART_DATA_KEY = "filePartData";
public static final String START_TIME_KEY = "startTime";
public static final String DONE_KEY = "done";
private String destinationDir;
private String fileName;
private byte[] filePartData;
private long startTime;
private boolean done;
public CopyFileParams(@NonNull String fileName, @NonNull byte[] filePartData, long startTime, boolean done) {
public CopyFileParams(@NonNull String destinationDir, @NonNull String fileName, @NonNull byte[] filePartData,
long startTime, boolean done) {
this.destinationDir = destinationDir;
this.fileName = fileName;
this.filePartData = filePartData;
this.startTime = startTime;
@ -37,6 +46,10 @@ public class CopyFileParams extends AidlParams {
}
};
public String getDestinationDir() {
return destinationDir;
}
public String getFileName() {
return fileName;
}
@ -55,23 +68,26 @@ public class CopyFileParams extends AidlParams {
@Override
public void writeToBundle(Bundle bundle) {
bundle.putString("fileName", fileName);
bundle.putByteArray("filePartData", filePartData);
bundle.putLong("startTime", startTime);
bundle.putBoolean("done", done);
bundle.putString(DESTINATION_DIR_KEY, destinationDir);
bundle.putString(FILE_NAME_KEY, fileName);
bundle.putByteArray(FILE_PART_DATA_KEY, filePartData);
bundle.putLong(START_TIME_KEY, startTime);
bundle.putBoolean(DONE_KEY, done);
}
@Override
protected void readFromBundle(Bundle bundle) {
fileName = bundle.getString("fileName");
filePartData = bundle.getByteArray("filePartData");
startTime = bundle.getLong("startTime");
done = bundle.getBoolean("done");
destinationDir = bundle.getString(DESTINATION_DIR_KEY);
fileName = bundle.getString(FILE_NAME_KEY);
filePartData = bundle.getByteArray(FILE_PART_DATA_KEY);
startTime = bundle.getLong(START_TIME_KEY);
done = bundle.getBoolean(DONE_KEY);
}
@Override
public String toString() {
return "CopyFileParams {" +
" destinationDir=" + destinationDir +
" fileName=" + fileName +
", filePartData size=" + filePartData.length +
", startTime=" + startTime +

View file

@ -0,0 +1,3 @@
package net.osmand.aidlapi.customization;
parcelable MapMarginsParams;

View file

@ -0,0 +1,77 @@
package net.osmand.aidlapi.customization;
import android.os.Bundle;
import android.os.Parcel;
import net.osmand.aidlapi.AidlParams;
public class MapMarginsParams extends AidlParams {
private String appModeKey;
private int leftMargin;
private int topMargin;
private int rightMargin;
private int bottomMargin;
public MapMarginsParams(String appModeKey, int leftMargin, int topMargin, int rightMargin, int bottomMargin) {
this.appModeKey = appModeKey;
this.leftMargin = leftMargin;
this.topMargin = topMargin;
this.rightMargin = rightMargin;
this.bottomMargin = bottomMargin;
}
public MapMarginsParams(Parcel in) {
readFromParcel(in);
}
public static final Creator<MapMarginsParams> CREATOR = new Creator<MapMarginsParams>() {
@Override
public MapMarginsParams createFromParcel(Parcel in) {
return new MapMarginsParams(in);
}
@Override
public MapMarginsParams[] newArray(int size) {
return new MapMarginsParams[size];
}
};
public String getAppModeKey() {
return appModeKey;
}
public int getLeftMargin() {
return leftMargin;
}
public int getTopMargin() {
return topMargin;
}
public int getRightMargin() {
return rightMargin;
}
public int getBottomMargin() {
return bottomMargin;
}
@Override
public void writeToBundle(Bundle bundle) {
bundle.putString("appModeKey", appModeKey);
bundle.putInt("leftMargin", leftMargin);
bundle.putInt("topMargin", topMargin);
bundle.putInt("rightMargin", rightMargin);
bundle.putInt("bottomMargin", bottomMargin);
}
@Override
protected void readFromBundle(Bundle bundle) {
appModeKey = bundle.getString("appModeKey");
leftMargin = bundle.getInt("leftMargin");
topMargin = bundle.getInt("topMargin");
rightMargin = bundle.getInt("rightMargin");
bottomMargin = bundle.getInt("bottomMargin");
}
}

View file

@ -5,15 +5,31 @@ import android.os.Bundle;
import android.os.Parcel;
import net.osmand.aidlapi.AidlParams;
import net.osmand.aidlapi.profile.AExportSettingsType;
import java.util.ArrayList;
import static net.osmand.aidlapi.profile.ExportProfileParams.SETTINGS_TYPE_KEY;
public class ProfileSettingsParams extends AidlParams {
public static final String VERSION_KEY = "version";
public static final String REPLACE_KEY = "replace";
public static final String LATEST_CHANGES_KEY = "latestChanges";
public static final String PROFILE_SETTINGS_URI_KEY = "profileSettingsUri";
private Uri profileSettingsUri;
private String latestChanges;
private int version;
private ArrayList<String> settingsTypeKeyList = new ArrayList<>();
boolean replace;
public ProfileSettingsParams(Uri profileSettingsUri, String latestChanges, int version) {
public ProfileSettingsParams(Uri profileSettingsUri, ArrayList<AExportSettingsType> settingsTypeList, boolean replace,
String latestChanges, int version) {
this.profileSettingsUri = profileSettingsUri;
for (AExportSettingsType settingsType : settingsTypeList) {
settingsTypeKeyList.add(settingsType.name());
}
this.replace = replace;
this.latestChanges = latestChanges;
this.version = version;
}
@ -46,17 +62,29 @@ public class ProfileSettingsParams extends AidlParams {
return profileSettingsUri;
}
public ArrayList<String> getSettingsTypeKeys() {
return settingsTypeKeyList;
}
public boolean isReplace() {
return replace;
}
@Override
public void writeToBundle(Bundle bundle) {
bundle.putInt("version", version);
bundle.putString("latestChanges", latestChanges);
bundle.putParcelable("profileSettingsUri", profileSettingsUri);
bundle.putInt(VERSION_KEY, version);
bundle.putString(LATEST_CHANGES_KEY, latestChanges);
bundle.putParcelable(PROFILE_SETTINGS_URI_KEY, profileSettingsUri);
bundle.putStringArrayList(SETTINGS_TYPE_KEY, settingsTypeKeyList);
bundle.putBoolean(REPLACE_KEY, replace);
}
@Override
protected void readFromBundle(Bundle bundle) {
version = bundle.getInt("version");
latestChanges = bundle.getString("latestChanges");
profileSettingsUri = bundle.getParcelable("profileSettingsUri");
version = bundle.getInt(VERSION_KEY);
latestChanges = bundle.getString(LATEST_CHANGES_KEY);
profileSettingsUri = bundle.getParcelable(PROFILE_SETTINGS_URI_KEY);
settingsTypeKeyList = bundle.getStringArrayList(SETTINGS_TYPE_KEY);
replace = bundle.getBoolean(REPLACE_KEY);
}
}

View file

@ -0,0 +1,2 @@
package net.osmand.aidlapi.events;
parcelable AKeyEventsParams;

View file

@ -0,0 +1,73 @@
package net.osmand.aidlapi.events;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import net.osmand.aidlapi.AidlParams;
import java.util.ArrayList;
public class AKeyEventsParams extends AidlParams {
private long callbackId = -1L;
private boolean subscribeToUpdates = true;
private ArrayList<Integer> keyEventList;
public AKeyEventsParams() {
}
protected AKeyEventsParams(Parcel in) {
readFromParcel(in);
}
public static final Parcelable.Creator<AKeyEventsParams> CREATOR = new Parcelable.Creator<AKeyEventsParams>() {
@Override
public AKeyEventsParams createFromParcel(Parcel in) {
return new AKeyEventsParams(in);
}
@Override
public AKeyEventsParams[] newArray(int size) {
return new AKeyEventsParams[size];
}
};
public long getCallbackId() {
return callbackId;
}
public void setCallbackId(long callbackId) {
this.callbackId = callbackId;
}
public void setSubscribeToUpdates(boolean subscribeToUpdates) {
this.subscribeToUpdates = subscribeToUpdates;
}
public boolean isSubscribeToUpdates() {
return subscribeToUpdates;
}
public void setKeyEventList(ArrayList<Integer> keyEventList) {
this.keyEventList = keyEventList;
}
public ArrayList<Integer> getKeyEventList() {
return keyEventList;
}
@Override
protected void readFromBundle(Bundle bundle) {
callbackId = bundle.getLong("callbackId");
subscribeToUpdates = bundle.getBoolean("subscribeToUpdates");
keyEventList = bundle.getIntegerArrayList("keyEventList");
}
@Override
public void writeToBundle(Bundle bundle) {
bundle.putLong("callbackId", callbackId);
bundle.putBoolean("subscribeToUpdates", subscribeToUpdates);
bundle.putIntegerArrayList("keyEventList", keyEventList);
}
}

View file

@ -11,16 +11,18 @@ public class AFavorite extends AidlParams {
private double lon;
private String name;
private String description;
private String address;
private String category;
private String color;
private boolean visible;
public AFavorite(double lat, double lon, String name, String description,
public AFavorite(double lat, double lon, String name, String description, String address,
String category, String color, boolean visible) {
this.lat = lat;
this.lon = lon;
this.name = name;
this.description = description;
this.address = address;
this.category = category;
this.color = color;
this.visible = visible;
@ -58,6 +60,8 @@ public class AFavorite extends AidlParams {
return description;
}
public String getAddress() { return address; }
public String getCategory() {
return category;
}
@ -76,6 +80,7 @@ public class AFavorite extends AidlParams {
bundle.putDouble("lon", lon);
bundle.putString("name", name);
bundle.putString("description", description);
bundle.putString("address", address);
bundle.putString("category", category);
bundle.putString("color", color);
bundle.putBoolean("visible", visible);
@ -87,6 +92,7 @@ public class AFavorite extends AidlParams {
lon = bundle.getDouble("lon");
name = bundle.getString("name");
description = bundle.getString("description");
address = bundle.getString("address");
category = bundle.getString("category");
color = bundle.getString("color");
visible = bundle.getBoolean("visible");

View file

@ -0,0 +1,3 @@
package net.osmand.aidlapi.info;
parcelable AppInfoParams;

View file

@ -0,0 +1,95 @@
package net.osmand.aidlapi.info;
import android.os.Bundle;
import android.os.Parcel;
import net.osmand.aidlapi.AidlParams;
import net.osmand.aidlapi.map.ALatLon;
public class AppInfoParams extends AidlParams {
private ALatLon lastKnownLocation;
private ALatLon mapLocation;
private Bundle turnInfo;
private int leftTime;
private int leftDistance;
private long arrivalTime;
private boolean mapVisible;
public AppInfoParams(ALatLon lastKnownLocation, ALatLon mapLocation, Bundle turnInfo, int leftTime, int leftDistance, long arrivalTime, boolean mapVisible) {
this.lastKnownLocation = lastKnownLocation;
this.mapLocation = mapLocation;
this.leftTime = leftTime;
this.leftDistance = leftDistance;
this.arrivalTime = arrivalTime;
this.turnInfo = turnInfo;
this.mapVisible = mapVisible;
}
public AppInfoParams(Parcel in) {
readFromParcel(in);
}
public static final Creator<AppInfoParams> CREATOR = new Creator<AppInfoParams>() {
@Override
public AppInfoParams createFromParcel(Parcel in) {
return new AppInfoParams(in);
}
@Override
public AppInfoParams[] newArray(int size) {
return new AppInfoParams[size];
}
};
public ALatLon getLastKnownLocation() {
return lastKnownLocation;
}
public ALatLon getMapLocation() {
return mapLocation;
}
public int getLeftTime() {
return leftTime;
}
public long getArrivalTime() {
return arrivalTime;
}
public int getLeftDistance() {
return leftDistance;
}
public boolean isMapVisible() {
return mapVisible;
}
public Bundle getTurnInfo() {
return turnInfo;
}
@Override
public void writeToBundle(Bundle bundle) {
bundle.putParcelable("lastKnownLocation", lastKnownLocation);
bundle.putParcelable("mapLocation", mapLocation);
bundle.putInt("leftTime", leftTime);
bundle.putLong("arrivalTime", arrivalTime);
bundle.putInt("leftDistance", leftDistance);
bundle.putBundle("turnInfo", turnInfo);
bundle.putBoolean("mapVisible", mapVisible);
}
@Override
protected void readFromBundle(Bundle bundle) {
lastKnownLocation = bundle.getParcelable("lastKnownLocation");
mapLocation = bundle.getParcelable("mapLocation");
leftTime = bundle.getInt("leftTime");
arrivalTime = bundle.getLong("arrivalTime");
leftDistance = bundle.getInt("leftDistance");
turnInfo = bundle.getBundle("turnInfo");
mapVisible = bundle.getBoolean("mapVisible");
}
}

View file

@ -0,0 +1,3 @@
package net.osmand.aidlapi.lock;
parcelable SetLockStateParams;

View file

@ -0,0 +1,45 @@
package net.osmand.aidlapi.lock;
import android.os.Bundle;
import android.os.Parcel;
import net.osmand.aidlapi.AidlParams;
public class SetLockStateParams extends AidlParams {
private boolean lock;
public SetLockStateParams(boolean lock) {
this.lock = lock;
}
public SetLockStateParams(Parcel in) {
readFromParcel(in);
}
public static final Creator<SetLockStateParams> CREATOR = new Creator<SetLockStateParams>() {
@Override
public SetLockStateParams createFromParcel(Parcel in) {
return new SetLockStateParams(in);
}
@Override
public SetLockStateParams[] newArray(int size) {
return new SetLockStateParams[size];
}
};
public boolean getLockState() {
return lock;
}
@Override
public void writeToBundle(Bundle bundle) {
bundle.putBoolean("lock", this.lock);
}
@Override
protected void readFromBundle(Bundle bundle) {
lock = bundle.getBoolean("lock");
}
}

View file

@ -10,12 +10,14 @@ public class SetMapLocationParams extends AidlParams {
private double latitude;
private double longitude;
private int zoom;
private float rotation;
private boolean animated;
public SetMapLocationParams(double latitude, double longitude, int zoom, boolean animated) {
public SetMapLocationParams(double latitude, double longitude, int zoom, float rotation, boolean animated) {
this.latitude = latitude;
this.longitude = longitude;
this.zoom = zoom;
this.rotation = rotation;
this.animated = animated;
}
@ -47,6 +49,10 @@ public class SetMapLocationParams extends AidlParams {
return zoom;
}
public float getRotation() {
return rotation;
}
public boolean isAnimated() {
return animated;
}
@ -56,6 +62,7 @@ public class SetMapLocationParams extends AidlParams {
bundle.putDouble("latitude", latitude);
bundle.putDouble("longitude", longitude);
bundle.putInt("zoom", zoom);
bundle.putFloat("rotation", rotation);
bundle.putBoolean("animated", animated);
}
@ -64,6 +71,7 @@ public class SetMapLocationParams extends AidlParams {
latitude = bundle.getDouble("latitude");
longitude = bundle.getDouble("longitude");
zoom = bundle.getInt("zoom");
rotation = bundle.getFloat("rotation");
animated = bundle.getBoolean("animated");
}
}

View file

@ -0,0 +1,3 @@
package net.osmand.aidlapi.profile;
parcelable AExportSettingsType;

View file

@ -0,0 +1,11 @@
package net.osmand.aidlapi.profile;
public enum AExportSettingsType {
PROFILE,
QUICK_ACTIONS,
POI_TYPES,
MAP_SOURCES,
CUSTOM_RENDER_STYLE,
CUSTOM_ROUTING,
AVOID_ROADS;
}

View file

@ -0,0 +1,3 @@
package net.osmand.aidlapi.profile;
parcelable ExportProfileParams;

View file

@ -0,0 +1,61 @@
package net.osmand.aidlapi.profile;
import android.os.Bundle;
import android.os.Parcel;
import net.osmand.aidlapi.AidlParams;
import java.util.ArrayList;
import java.util.List;
public class ExportProfileParams extends AidlParams {
public static final String PROFILE_KEY = "profile";
public static final String SETTINGS_TYPE_KEY = "settings_type";
private String profile;
private ArrayList<String> settingsTypeKeyList = new ArrayList<>();
public ExportProfileParams(String profile, ArrayList<AExportSettingsType> settingsTypeList) {
this.profile = profile;
for (AExportSettingsType settingsType : settingsTypeList) {
settingsTypeKeyList.add(settingsType.name());
}
}
public ExportProfileParams(Parcel in) {
readFromParcel(in);
}
public static final Creator<ExportProfileParams> CREATOR = new Creator<ExportProfileParams>() {
@Override
public ExportProfileParams createFromParcel(Parcel in) {
return new ExportProfileParams(in);
}
@Override
public ExportProfileParams[] newArray(int size) {
return new ExportProfileParams[size];
}
};
public String getProfile() {
return profile;
}
public List<String> getSettingsTypeKeys() {
return settingsTypeKeyList;
}
@Override
public void writeToBundle(Bundle bundle) {
bundle.putString(PROFILE_KEY, profile);
bundle.putStringArrayList(SETTINGS_TYPE_KEY, settingsTypeKeyList);
}
@Override
protected void readFromBundle(Bundle bundle) {
profile = bundle.getString(PROFILE_KEY);
settingsTypeKeyList = bundle.getStringArrayList(SETTINGS_TYPE_KEY);
}
}

View file

@ -43,6 +43,7 @@ task collectTestResources(type: Copy) {
into "src/test/resources/"
from("../../resources/test-resources") {
include "*"
include "/search/*"
}
from("../../resources/poi") {
include "poi_types.xml"

View file

@ -17,19 +17,34 @@ public class CollatorStringMatcher implements StringMatcher {
private final String part;
public static enum StringMatcherMode {
// tests only first word as base starts with part
CHECK_ONLY_STARTS_WITH,
// tests all words (split by space) and one of word should start with a given part
CHECK_STARTS_FROM_SPACE,
// tests all words except first (split by space) and one of word should start with a given part
CHECK_STARTS_FROM_SPACE_NOT_BEGINNING,
// tests all words (split by space) and one of word should be equal to part
CHECK_EQUALS_FROM_SPACE,
// simple collator contains in any part of the base
CHECK_CONTAINS,
CHECK_ONLY_STARTS_WITH_TRIM,
// simple collator equals
CHECK_EQUALS,
}
public CollatorStringMatcher(String part, StringMatcherMode mode) {
this.collator = OsmAndCollator.primaryCollator();
this.part = part.toLowerCase(Locale.getDefault());
part = simplifyStringAndAlignChars(part);
if (part.length() > 0 && part.charAt(part.length() - 1) == '.') {
part = part.substring(0, part.length() - 1);
if (mode == StringMatcherMode.CHECK_EQUALS_FROM_SPACE) {
mode = StringMatcherMode.CHECK_STARTS_FROM_SPACE;
} else if (mode == StringMatcherMode.CHECK_EQUALS) {
mode = StringMatcherMode.CHECK_ONLY_STARTS_WITH;
}
}
this.part = part;
this.mode = mode;
}
public Collator getCollator() {
@ -42,22 +57,20 @@ public class CollatorStringMatcher implements StringMatcher {
}
public static boolean cmatches(Collator collator, String base, String part, StringMatcherMode mode){
public static boolean cmatches(Collator collator, String fullName, String part, StringMatcherMode mode){
switch (mode) {
case CHECK_CONTAINS:
return ccontains(collator, base, part);
return ccontains(collator, fullName, part);
case CHECK_EQUALS_FROM_SPACE:
return cstartsWith(collator, base, part, true, true, true, false);
return cstartsWith(collator, fullName, part, true, true, true);
case CHECK_STARTS_FROM_SPACE:
return cstartsWith(collator, base, part, true, true, false, false);
return cstartsWith(collator, fullName, part, true, true, false);
case CHECK_STARTS_FROM_SPACE_NOT_BEGINNING:
return cstartsWith(collator, base, part, false, true, false, false);
return cstartsWith(collator, fullName, part, false, true, false);
case CHECK_ONLY_STARTS_WITH:
return cstartsWith(collator, base, part, true, false, false, false);
case CHECK_ONLY_STARTS_WITH_TRIM:
return cstartsWith(collator, base, part, true, false, false, true);
return cstartsWith(collator, fullName, part, true, false, false);
case CHECK_EQUALS:
return cstartsWith(collator, base, part, false, false, true, false);
return cstartsWith(collator, fullName, part, false, false, true);
}
return false;
}
@ -116,25 +129,19 @@ public class CollatorStringMatcher implements StringMatcher {
* Special check try to find as well in the middle of name
*
* @param collator
* @param searchInParam
* @param fullText
* @param theStart
* @param trim - trim theStart to searchInParam length if searchInParam non empty
* @return true if searchIn starts with token
*/
public static boolean cstartsWith(Collator collator, String searchInParam, String theStart,
boolean checkBeginning, boolean checkSpaces, boolean equals, boolean trim) {
String searchIn = searchInParam.toLowerCase(Locale.getDefault());
if (trim && searchIn.length() > 0) {
searchIn += " ";
}
public static boolean cstartsWith(Collator collator, String fullTextP, String theStart,
boolean checkBeginning, boolean checkSpaces, boolean equals) {
String searchIn = simplifyStringAndAlignChars(fullTextP);
int searchInLength = searchIn.length();
if (trim && searchInLength > 0 && theStart.length() > searchInLength) {
theStart = theStart.substring(0, searchInLength);
}
int startLength = theStart.length();
if (startLength == 0) {
return true;
}
// this is not correct because of Auhofstrasse != Auhofstraße
if (startLength > searchInLength) {
return false;
}
@ -156,7 +163,8 @@ public class CollatorStringMatcher implements StringMatcher {
if (isSpace(searchIn.charAt(i - 1)) && !isSpace(searchIn.charAt(i))) {
if (collator.equals(searchIn.substring(i, i + startLength), theStart)) {
if(equals) {
if(i + startLength == searchInLength || isSpace(searchIn.charAt(i + startLength))) {
if(i + startLength == searchInLength ||
isSpace(searchIn.charAt(i + startLength))) {
return true;
}
} else {
@ -172,7 +180,17 @@ public class CollatorStringMatcher implements StringMatcher {
return false;
}
private static String simplifyStringAndAlignChars(String fullText) {
int i;
fullText = fullText.toLowerCase(Locale.getDefault());
while( (i = fullText.indexOf('ß') ) != -1 ) {
fullText = fullText.substring(0, i) + "ss" + fullText.substring(i+1);
}
return fullText;
}
private static boolean isSpace(char c){
return !Character.isLetter(c) && !Character.isDigit(c);
}
}

View file

@ -1,6 +1,10 @@
package net.osmand;
import net.osmand.binary.StringBundle;
import net.osmand.binary.StringBundleWriter;
import net.osmand.binary.StringBundleXmlWriter;
import net.osmand.data.QuadRect;
import net.osmand.util.Algorithms;
@ -12,7 +16,6 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@ -42,10 +45,14 @@ import java.util.Stack;
import java.util.TimeZone;
public class GPXUtilities {
public final static Log log = PlatformUtil.getLog(GPXUtilities.class);
private static final String ICON_NAME_EXTENSION = "icon";
private static final String DEFAULT_ICON_NAME = "special_star";
private static final String BACKGROUND_TYPE_EXTENSION = "background";
private static final String PROFILE_TYPE_EXTENSION = "profile";
private static final String TRKPT_INDEX_EXTENSION = "trkpt_idx";
private final static String GPX_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; //$NON-NLS-1$
private final static String GPX_TIME_FORMAT_MILLIS = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; //$NON-NLS-1$
@ -115,6 +122,20 @@ public class GPXUtilities {
return extensions;
}
public Map<String, String> getExtensionsToWrite() {
if (extensions == null) {
extensions = new LinkedHashMap<>();
}
return extensions;
}
public void copyExtensions(GPXExtensions e) {
Map<String, String> extensionsToRead = e.getExtensionsToRead();
if (!extensionsToRead.isEmpty()) {
getExtensionsToWrite().putAll(extensionsToRead);
}
}
public GPXExtensionsWriter getExtensionsWriter() {
return extensionsWriter;
}
@ -148,14 +169,7 @@ public class GPXUtilities {
getExtensionsToWrite().remove("color");
}
public Map<String, String> getExtensionsToWrite() {
if (extensions == null) {
extensions = new LinkedHashMap<>();
}
return extensions;
}
private int parseColor(String colorString, int defColor) {
protected int parseColor(String colorString, int defColor) {
if (!Algorithms.isEmpty(colorString)) {
if (colorString.charAt(0) == '#') {
long color = Long.parseLong(colorString.substring(1), 16);
@ -302,6 +316,30 @@ public class GPXUtilities {
getExtensionsToWrite().put(BACKGROUND_TYPE_EXTENSION, backType);
}
public String getProfileType() {
return getExtensionsToRead().get(PROFILE_TYPE_EXTENSION);
}
public void setProfileType(String profileType) {
getExtensionsToWrite().put(PROFILE_TYPE_EXTENSION, profileType);
}
public void removeProfileType() {
getExtensionsToWrite().remove(PROFILE_TYPE_EXTENSION);
}
public int getTrkPtIndex() {
try {
return Integer.parseInt(getExtensionsToRead().get(TRKPT_INDEX_EXTENSION));
} catch (NumberFormatException e) {
return -1;
}
}
public void setTrkPtIndex(int index) {
getExtensionsToWrite().put(TRKPT_INDEX_EXTENSION, String.valueOf(index));
}
@Override
public int hashCode() {
final int prime = 31;
@ -412,6 +450,65 @@ public class GPXUtilities {
public double maxlon;
}
public static class RouteSegment {
public String id;
public String length;
public String segmentTime;
public String speed;
public String turnType;
public String turnAngle;
public String types;
public String pointTypes;
public String names;
public static RouteSegment fromStringBundle(StringBundle bundle) {
RouteSegment s = new RouteSegment();
s.id = bundle.getString("id", null);
s.length = bundle.getString("length", null);
s.segmentTime = bundle.getString("segmentTime", null);
s.speed = bundle.getString("speed", null);
s.turnType = bundle.getString("turnType", null);
s.turnAngle = bundle.getString("turnAngle", null);
s.types = bundle.getString("types", null);
s.pointTypes = bundle.getString("pointTypes", null);
s.names = bundle.getString("names", null);
return s;
}
public StringBundle toStringBundle() {
StringBundle bundle = new StringBundle();
bundle.putString("id", id);
bundle.putString("length", length);
bundle.putString("segmentTime", segmentTime);
bundle.putString("speed", speed);
bundle.putString("turnType", turnType);
bundle.putString("turnAngle", turnAngle);
bundle.putString("types", types);
bundle.putString("pointTypes", pointTypes);
bundle.putString("names", names);
return bundle;
}
}
public static class RouteType {
public String tag;
public String value;
public static RouteType fromStringBundle(StringBundle bundle) {
RouteType t = new RouteType();
t.tag = bundle.getString("t", null);
t.value = bundle.getString("v", null);
return t;
}
public StringBundle toStringBundle() {
StringBundle bundle = new StringBundle();
bundle.putString("t", tag);
bundle.putString("v", value);
return bundle;
}
}
public static class GPXTrackAnalysis {
public float totalDistance = 0;
public float totalDistanceWithoutGaps = 0;
@ -932,7 +1029,6 @@ public class GPXUtilities {
sp = new SplitSegment(segment, k - 1, cf);
currentMetricEnd += metricLimit;
prev = sp.get(0);
}
total += currentSegment;
}
@ -957,6 +1053,24 @@ public class GPXUtilities {
return ls;
}
public static QuadRect calculateBounds(List<WptPt> pts) {
QuadRect trackBounds = new QuadRect(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY,
Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
updateBounds(trackBounds, pts, 0);
return trackBounds;
}
public static void updateBounds(QuadRect trackBounds, List<WptPt> pts, int startIndex) {
for (int i = startIndex; i < pts.size(); i++) {
WptPt pt = pts.get(i);
trackBounds.right = Math.max(trackBounds.right, pt.lon);
trackBounds.left = Math.min(trackBounds.left, pt.lon);
trackBounds.top = Math.max(trackBounds.top, pt.lat);
trackBounds.bottom = Math.min(trackBounds.bottom, pt.lat);
}
}
public static class GPXFile extends GPXExtensions {
public String author;
public Metadata metadata;
@ -964,6 +1078,9 @@ public class GPXUtilities {
private List<WptPt> points = new ArrayList<>();
public List<Route> routes = new ArrayList<>();
public List<RouteSegment> routeSegments = new ArrayList<>();
public List<RouteType> routeTypes = new ArrayList<>();
public Exception error = null;
public String path = "";
public boolean showCurrentTrack;
@ -990,6 +1107,10 @@ public class GPXUtilities {
}
}
public boolean hasRoute() {
return !routeSegments.isEmpty() && !routeTypes.isEmpty();
}
public List<WptPt> getPoints() {
return Collections.unmodifiableList(points);
}
@ -1197,6 +1318,17 @@ public class GPXUtilities {
return pt;
}
public TrkSegment getNonEmptyTrkSegment() {
for (GPXUtilities.Track t : tracks) {
for (TrkSegment s : t.segments) {
if (s.points.size() > 0) {
return s;
}
}
}
return null;
}
public void addTrkSegment(List<WptPt> points) {
removeGeneralTrackIfExists();
@ -1493,6 +1625,97 @@ public class GPXUtilities {
}
return new QuadRect(left, top, right, bottom);
}
public int getGradientScaleColor(String gradientScaleType, int defColor) {
String clrValue = null;
if (extensions != null) {
clrValue = extensions.get(gradientScaleType);
}
return parseColor(clrValue, defColor);
}
public void setGradientScaleColor(String gradientScaleType, int gradientScaleColor) {
getExtensionsToWrite().put(gradientScaleType, Algorithms.colorToString(gradientScaleColor));
}
public String getGradientScaleType() {
if (extensions != null) {
return extensions.get("gradient_scale_type");
}
return null;
}
public void setGradientScaleType(String gradientScaleType) {
getExtensionsToWrite().put("gradient_scale_type", gradientScaleType);
}
public void removeGradientScaleType() {
getExtensionsToWrite().remove("gradient_scale_type");
}
public String getSplitType() {
if (extensions != null) {
return extensions.get("split_type");
}
return null;
}
public void setSplitType(String gpxSplitType) {
getExtensionsToWrite().put("split_type", gpxSplitType);
}
public double getSplitInterval() {
if (extensions != null) {
String splitIntervalStr = extensions.get("split_interval");
if (!Algorithms.isEmpty(splitIntervalStr)) {
try {
return Double.parseDouble(splitIntervalStr);
} catch (NumberFormatException e) {
log.error("Error reading split_interval", e);
}
}
}
return 0;
}
public void setSplitInterval(double splitInterval) {
getExtensionsToWrite().put("split_interval", String.valueOf(splitInterval));
}
public String getWidth(String defWidth) {
String widthValue = null;
if (extensions != null) {
widthValue = extensions.get("width");
}
return widthValue != null ? widthValue : defWidth;
}
public void setWidth(String width) {
getExtensionsToWrite().put("width", width);
}
public boolean isShowArrows() {
String showArrows = null;
if (extensions != null) {
showArrows = extensions.get("show_arrows");
}
return Boolean.parseBoolean(showArrows);
}
public void setShowArrows(boolean showArrows) {
getExtensionsToWrite().put("show_arrows", String.valueOf(showArrows));
}
public boolean isShowStartFinish() {
if (extensions != null && extensions.containsKey("show_start_finish")) {
return Boolean.parseBoolean(extensions.get("show_start_finish"));
}
return true;
}
public void setShowStartFinish(boolean showStartFinish) {
getExtensionsToWrite().put("show_start_finish", String.valueOf(showStartFinish));
}
}
public static String asString(GPXFile file) {
@ -1611,6 +1834,7 @@ public class GPXUtilities {
serializer.endTag(null, "wpt"); //$NON-NLS-1$
}
assignRouteExtensionWriter(file);
writeExtensions(serializer, file);
serializer.endTag(null, "gpx"); //$NON-NLS-1$
@ -1623,6 +1847,29 @@ public class GPXUtilities {
return null;
}
private static void assignRouteExtensionWriter(final GPXFile gpxFile) {
if (gpxFile.hasRoute() && gpxFile.getExtensionsWriter() == null) {
gpxFile.setExtensionsWriter(new GPXExtensionsWriter() {
@Override
public void writeExtensions(XmlSerializer serializer) {
StringBundle bundle = new StringBundle();
List<StringBundle> segmentsBundle = new ArrayList<>();
for (RouteSegment segment : gpxFile.routeSegments) {
segmentsBundle.add(segment.toStringBundle());
}
bundle.putBundleList("route", "segment", segmentsBundle);
List<StringBundle> typesBundle = new ArrayList<>();
for (RouteType routeType : gpxFile.routeTypes) {
typesBundle.add(routeType.toStringBundle());
}
bundle.putBundleList("types", "type", typesBundle);
StringBundleWriter bundleWriter = new StringBundleXmlWriter(bundle, serializer);
bundleWriter.writeBundle();
}
});
}
}
private static String getFilename(String path) {
if(path != null) {
int i = path.lastIndexOf('/');
@ -1840,23 +2087,7 @@ public class GPXUtilities {
}
public static GPXFile loadGPXFile(InputStream f) {
return loadGPXFile(f, null, null);
}
public static GPXFile loadGPXFile(InputStream f, GPXFile gpxFile, GPXExtensionsReader extensionsReader) {
boolean readExtensionsOnly = false;
if (gpxFile == null) {
gpxFile = new GPXFile(null);
} else {
if (f == null) {
try {
f = new FileInputStream(new File(gpxFile.path));
} catch (FileNotFoundException e) {
return gpxFile;
}
}
readExtensionsOnly = extensionsReader != null;
}
GPXFile gpxFile = new GPXFile(null);
SimpleDateFormat format = new SimpleDateFormat(GPX_TIME_FORMAT, Locale.US);
format.setTimeZone(TimeZone.getTimeZone("UTC"));
SimpleDateFormat formatMillis = new SimpleDateFormat(GPX_TIME_FORMAT_MILLIS, Locale.US);
@ -1870,6 +2101,10 @@ public class GPXUtilities {
Stack<GPXExtensions> parserState = new Stack<>();
boolean extensionReadMode = false;
boolean routePointExtension = false;
List<RouteSegment> routeSegments = gpxFile.routeSegments;
List<RouteType> routeTypes = gpxFile.routeTypes;
boolean routeExtension = false;
boolean typesExtension = false;
parserState.push(gpxFile);
int tok;
while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) {
@ -1878,37 +2113,50 @@ public class GPXUtilities {
String tag = parser.getName();
if (extensionReadMode && parse != null && !routePointExtension) {
String tagName = tag.toLowerCase();
boolean extensionsRead = false;
if (extensionsReader != null) {
extensionsRead = extensionsReader.readExtensions(gpxFile, parser);
if (routeExtension) {
if (tagName.equals("segment")) {
RouteSegment segment = parseRouteSegmentAttributes(parser);
routeSegments.add(segment);
}
} else if (typesExtension) {
if (tagName.equals("type")) {
RouteType type = parseRouteTypeAttributes(parser);
routeTypes.add(type);
}
}
if (!readExtensionsOnly && !extensionsRead) {
switch (tagName) {
case "routepointextension":
routePointExtension = true;
if (parse instanceof WptPt) {
parse.getExtensionsToWrite().put("offset", routeTrackSegment.points.size() + "");
}
break;
switch (tagName) {
case "routepointextension":
routePointExtension = true;
if (parse instanceof WptPt) {
parse.getExtensionsToWrite().put("offset", routeTrackSegment.points.size() + "");
}
break;
default:
Map<String, String> values = readTextMap(parser, tag);
if (values.size() > 0) {
for (Entry<String, String> entry : values.entrySet()) {
String t = entry.getKey().toLowerCase();
String value = entry.getValue();
parse.getExtensionsToWrite().put(t, value);
if (tag.equals("speed") && parse instanceof WptPt) {
try {
((WptPt) parse).speed = Float.parseFloat(value);
} catch (NumberFormatException e) {
log.debug(e.getMessage(), e);
}
case "route":
routeExtension = true;
break;
case "types":
typesExtension = true;
break;
default:
Map<String, String> values = readTextMap(parser, tag);
if (values.size() > 0) {
for (Entry<String, String> entry : values.entrySet()) {
String t = entry.getKey().toLowerCase();
String value = entry.getValue();
parse.getExtensionsToWrite().put(t, value);
if (tag.equals("speed") && parse instanceof WptPt) {
try {
((WptPt) parse).speed = Float.parseFloat(value);
} catch (NumberFormatException e) {
log.debug(e.getMessage(), e);
}
}
}
break;
}
}
break;
}
} else if (parse != null && tag.equals("extensions")) {
extensionReadMode = true;
@ -1918,7 +2166,7 @@ public class GPXUtilities {
routeTrackSegment.points.add(wptPt);
parserState.push(wptPt);
}
} else if (!readExtensionsOnly) {
} else {
if (parse instanceof GPXFile) {
if (tag.equals("gpx")) {
((GPXFile) parse).author = parser.getAttributeValue("", "creator");
@ -2114,7 +2362,12 @@ public class GPXUtilities {
if (parse != null && tag.equals("extensions")) {
extensionReadMode = false;
}
if (readExtensionsOnly) {
if (extensionReadMode && tag.equals("route")) {
routeExtension = false;
continue;
}
if (extensionReadMode && tag.equals("types")) {
typesExtension = false;
continue;
}
@ -2194,6 +2447,27 @@ public class GPXUtilities {
return wpt;
}
private static RouteSegment parseRouteSegmentAttributes(XmlPullParser parser) {
RouteSegment segment = new RouteSegment();
segment.id = parser.getAttributeValue("", "id");
segment.length = parser.getAttributeValue("", "length");
segment.segmentTime = parser.getAttributeValue("", "segmentTime");
segment.speed = parser.getAttributeValue("", "speed");
segment.turnType = parser.getAttributeValue("", "turnType");
segment.turnAngle = parser.getAttributeValue("", "turnAngle");
segment.types = parser.getAttributeValue("", "types");
segment.pointTypes = parser.getAttributeValue("", "pointTypes");
segment.names = parser.getAttributeValue("", "names");
return segment;
}
private static RouteType parseRouteTypeAttributes(XmlPullParser parser) {
RouteType type = new RouteType();
type.tag = parser.getAttributeValue("", "t");
type.value = parser.getAttributeValue("", "v");
return type;
}
private static Bounds parseBoundsAttributes(XmlPullParser parser) {
Bounds bounds = new Bounds();
try {

View file

@ -28,9 +28,6 @@ public class IndexConstants {
public static final String EXTRA_EXT = ".extra";
public static final String EXTRA_ZIP_EXT = ".extra.zip";
public static final String TOUR_INDEX_EXT = ".tour"; //$NON-NLS-1$
public static final String TOUR_INDEX_EXT_ZIP = ".tour.zip"; //$NON-NLS-1$
public static final String GEN_LOG_EXT = ".gen.log"; //$NON-NLS-1$
public static final String VOICE_INDEX_EXT_ZIP = ".voice.zip"; //$NON-NLS-1$
@ -48,6 +45,9 @@ public class IndexConstants {
public static final String GPX_FILE_EXT = ".gpx"; //$NON-NLS-1$
public static final String WPT_CHART_FILE_EXT = ".wpt.chart";
public static final String SQLITE_CHART_FILE_EXT = ".3d.chart";
public final static String POI_TABLE = "poi"; //$NON-NLS-1$
public static final String INDEX_DOWNLOAD_DOMAIN = "download.osmand.net";

View file

@ -0,0 +1,159 @@
package net.osmand;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.data.LatLon;
import net.osmand.util.Algorithms;
import java.util.ArrayList;
import java.util.List;
public class LocationsHolder {
private static final int LOCATION_TYPE_UNKNOWN = -1;
private static final int LOCATION_TYPE_LATLON = 0;
private static final int LOCATION_TYPE_LOCATION = 1;
private static final int LOCATION_TYPE_WPTPT = 2;
private List<LatLon> latLonList;
private List<Location> locationList;
private List<WptPt> wptPtList;
private int locationType;
private int size;
@SuppressWarnings("unchecked")
public LocationsHolder(List<?> locations) {
this.locationType = resolveLocationType(locations);
switch (locationType) {
case LOCATION_TYPE_LATLON:
latLonList = new ArrayList<>((List<LatLon>) locations);
size = locations.size();
break;
case LOCATION_TYPE_LOCATION:
locationList = new ArrayList<>((List<Location>) locations);
size = locations.size();
break;
case LOCATION_TYPE_WPTPT:
wptPtList = new ArrayList<>((List<WptPt>) locations);
size = locations.size();
break;
}
}
private int resolveLocationType(List<?> locations) {
if (!Algorithms.isEmpty(locations)) {
Object locationObj = locations.get(0);
if (locationObj instanceof LatLon) {
return LOCATION_TYPE_LATLON;
} else if (locationObj instanceof WptPt) {
return LOCATION_TYPE_WPTPT;
} else if (locationObj instanceof Location) {
return LOCATION_TYPE_LOCATION;
} else {
throw new IllegalArgumentException("Unsupported location type: " + locationObj.getClass().getSimpleName());
}
}
return LOCATION_TYPE_UNKNOWN;
}
public double getLatitude(int index) {
switch (locationType) {
case LOCATION_TYPE_LATLON:
return latLonList.get(index).getLatitude();
case LOCATION_TYPE_LOCATION:
return locationList.get(index).getLatitude();
case LOCATION_TYPE_WPTPT:
return wptPtList.get(index).getLatitude();
default:
return 0;
}
}
public double getLongitude(int index) {
switch (locationType) {
case LOCATION_TYPE_LATLON:
return latLonList.get(index).getLongitude();
case LOCATION_TYPE_LOCATION:
return locationList.get(index).getLongitude();
case LOCATION_TYPE_WPTPT:
return wptPtList.get(index).getLongitude();
default:
return 0;
}
}
public int getSize() {
return size;
}
@SuppressWarnings("unchecked")
private <T> List<T> getList(int locationType) {
List<T> res = new ArrayList<>();
if (size > 0) {
for (int i = 0; i < size; i++) {
switch (locationType) {
case LOCATION_TYPE_LATLON:
res.add((T) getLatLon(i));
break;
case LOCATION_TYPE_LOCATION:
res.add((T) getLocation(i));
break;
case LOCATION_TYPE_WPTPT:
res.add((T) getWptPt(i));
break;
}
}
}
return res;
}
public List<LatLon> getLatLonList() {
if (this.locationType == LOCATION_TYPE_LATLON) {
return latLonList;
} else {
return getList(LOCATION_TYPE_LATLON);
}
}
public List<WptPt> getWptPtList() {
if (this.locationType == LOCATION_TYPE_WPTPT) {
return wptPtList;
} else {
return getList(LOCATION_TYPE_WPTPT);
}
}
public List<Location> getLocationsList() {
if (this.locationType == LOCATION_TYPE_LOCATION) {
return locationList;
} else {
return getList(LOCATION_TYPE_LOCATION);
}
}
public LatLon getLatLon(int index) {
if (this.locationType == LOCATION_TYPE_LATLON) {
return latLonList.get(index);
} else {
return new LatLon(getLatitude(index), getLongitude(index));
}
}
public WptPt getWptPt(int index) {
if (this.locationType == LOCATION_TYPE_WPTPT) {
return wptPtList.get(index);
} else {
WptPt wptPt = new WptPt();
wptPt.lat = getLatitude(index);
wptPt.lon = getLongitude(index);
return wptPt;
}
}
public Location getLocation(int index) {
if (this.locationType == LOCATION_TYPE_LOCATION) {
return locationList.get(index);
} else {
return new Location("", getLatitude(index), getLongitude(index));
}
}
}

View file

@ -26,10 +26,9 @@ import net.osmand.data.QuadRect;
import net.osmand.render.RenderingRuleSearchRequest;
import net.osmand.render.RenderingRulesStorage;
import net.osmand.router.NativeTransportRoutingResult;
import net.osmand.router.PrecalculatedRouteDirection;
import net.osmand.router.RouteCalculationProgress;
import net.osmand.router.RouteSegmentResult;
import net.osmand.router.RoutingConfiguration;
import net.osmand.router.RoutingContext;
import net.osmand.router.TransportRoutingConfiguration;
import net.osmand.util.Algorithms;
@ -135,12 +134,10 @@ public class NativeLibrary {
return nativeTransportRouting(new int[] { sx31, sy31, ex31, ey31 }, cfg, progress);
}
public RouteSegmentResult[] runNativeRouting(int sx31, int sy31, int ex31, int ey31, RoutingConfiguration config,
RouteRegion[] regions, RouteCalculationProgress progress, PrecalculatedRouteDirection precalculatedRouteDirection,
boolean basemap, boolean publicTransport, boolean startTransportStop, boolean targetTransportStop) {
public RouteSegmentResult[] runNativeRouting(RoutingContext c, RouteRegion[] regions, boolean basemap) {
// config.router.printRules(System.out);
return nativeRouting(new int[] { sx31, sy31, ex31, ey31 }, config, config.initialDirection == null ? -360 : config.initialDirection.floatValue(),
regions, progress, precalculatedRouteDirection, basemap, publicTransport, startTransportStop, targetTransportStop);
return nativeRouting(c, c.config.initialDirection == null ? -360 : c.config.initialDirection.floatValue(),
regions, basemap);
}
@ -156,16 +153,15 @@ public class NativeLibrary {
protected static native NativeRouteSearchResult loadRoutingData(RouteRegion reg, String regName, int regfp, RouteSubregion subreg,
boolean loadObjects);
public static native void deleteNativeRoutingContext(long handle);
protected static native void deleteRenderingContextHandle(long handle);
protected static native void deleteRouteSearchResult(long searchResultHandle);
protected static native RouteDataObject[] getRouteDataObjects(RouteRegion reg, long rs, int x31, int y31);
protected static native RouteSegmentResult[] nativeRouting(int[] coordinates, RoutingConfiguration r,
float initDirection, RouteRegion[] regions, RouteCalculationProgress progress,
PrecalculatedRouteDirection precalculatedRouteDirection, boolean basemap,
boolean publicTransport, boolean startTransportStop, boolean targetTransportStop);
protected static native RouteSegmentResult[] nativeRouting(RoutingContext c, float initDirection, RouteRegion[] regions, boolean basemap);
protected static native NativeTransportRoutingResult[] nativeTransportRouting(int[] coordinates, TransportRoutingConfiguration cfg,
RouteCalculationProgress progress);
@ -354,6 +350,7 @@ public class NativeLibrary {
FileInputStream fis = new FileInputStream(f);
Algorithms.streamCopy(fis, ous);
fis.close();
System.out.println("FONT " + name);
initFontType(ous.toByteArray(), name.substring(0, name.length() - 4), name.toLowerCase().contains("bold"),
name.toLowerCase().contains("italic"));
} catch (IOException e) {

View file

@ -38,4 +38,5 @@ public class OsmAndCollator {
}
};
}
}

View file

@ -1,6 +1,7 @@
package net.osmand;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import org.apache.commons.logging.Log;
@ -19,61 +20,67 @@ public class Reshaper {
return "";
}
}
public static String reshape(String s) {
// if(true) {
// return s;
// }
try {
ArabicShaping as = new ArabicShaping(ArabicShaping.LETTERS_SHAPE | ArabicShaping.LENGTH_GROW_SHRINK);
ArabicShaping as = new ArabicShaping(ArabicShaping.LETTERS_SHAPE |
ArabicShaping.LENGTH_GROW_SHRINK);
//printSplit("B", s);
try {
s = as.shape(s);
} catch (ArabicShapingException e) {
LOG.error(e.getMessage(), e);
}
//printSplit("A", s);
Bidi line = new Bidi(s.length(), s.length());
line.setPara(s, Bidi.LEVEL_DEFAULT_LTR, null);
line.setPara(s, Bidi.LEVEL_DEFAULT_LTR, null);
// line.setPara(s, Bidi.LEVEL_DEFAULT_LTR, null);
// s = line.writeReordered(Bidi.DO_MIRRORING);
// s = reordered;
byte direction = line.getDirection();
if (direction != Bidi.MIXED) {
// unidirectional
if(line.isLeftToRight()) {
return s;
} else {
if (line.isLeftToRight()) {
return s;
} else {
char[] chs = new char[s.length()];
for(int i = 0; i< chs.length ; i++) {
chs[i] = s.charAt(chs.length - i - 1);
// chs[i] = s.charAt(chs.length - i - 1);
chs[i] = mirror(s.charAt(chs.length - i - 1));
}
return new String(chs);
}
} else {
// // mixed-directional
// mixed-directional
// System.out.println(s);
// printSplit("Split", s);
int count = line.countRuns();
// if (styleRunCount <= 1) {
// int style = styleRuns[0].style;
// // iterate over directional runs
// for (i = 0; i < count; ++i) {
// run = line.getVisualRun(i);
// renderRun(text, run.getStart(), run.getLimit(),
// run.getDirection(), style);
// }
// }
StringBuilder res = new StringBuilder();
// iterate over both directional and style runs
for (int i = 0; i < count; ++i) {
StringBuilder runs = new StringBuilder();
BidiRun run = line.getVisualRun(i);
int st = run.getStart();
int e = run.getLimit();
int j = run.getDirection() == Bidi.LTR ? st : e - 1;
int l = run.getDirection() == Bidi.LTR ? e : st - 1;
boolean plus = run.getDirection() == Bidi.LTR;
while (j != l) {
res.append(s.charAt(j));
if (plus) {
j++;
} else {
j--;
boolean ltr = run.getDirection() == Bidi.LTR;
int start = run.getStart();
int limit = run.getLimit();
int begin = ltr ? start : limit - 1;
int end = ltr ? limit : start - 1;
int ind = begin;
while (ind != end) {
char ch = s.charAt(ind);
if (!ltr) {
ch = mirror(ch);
}
res.append(ch);
runs.append(ch);
if (ltr) {
ind++;
} else {
ind--;
}
}
printSplit(run.getDirection() + " " + run.getEmbeddingLevel(), runs.toString());
}
return res.toString();
}
@ -83,24 +90,77 @@ public class Reshaper {
}
}
public static void main(String[] args) {
// char[] c = new char[] {'א', 'ד','ם', ' ', '1', '2'} ;
// String reshape = "אדם";
char[] c = new char[] {'א', 'ד','ם'} ;
String reshape = reshape(new String(c));
for(int i=0; i < reshape.length(); i++) {
System.out.println(reshape.charAt(i));
private static char mirror(char ch) {
switch (ch) {
case '(': ch = ')'; break;
case ')': ch = '('; break;
case '[': ch = ']'; break;
case ']': ch = '['; break;
}
return ch;
}
public static void main(String[] args) {
test2();
test3();
test4();
test5();
}
public static void test3() {
String s = "מרכז מסחרי/השלום (40050)";
String reshape = reshape(s);
String expected = "(40050) םולשה/ירחסמ זכרמ";
check(s, reshape, expected);
}
public static void test5() {
String s = "מרכז מסחרי/השלום (מרז)";
String reshape = reshape(s);
String expected = "(זרמ) םולשה/ירחסמ זכרמ";
check(s, reshape, expected);
}
public static void check(String source, String reshape, String expected) {
printSplit("Source ", source);
printSplit("Expected", expected);
printSplit("Reshaped", reshape);
System.out.println(reshape);
if (!reshape.equals(expected)) {
throw new IllegalArgumentException(String.format("Bug: expected '%s', reshaped '%s'", expected, reshape));
}
}
static void printSplit(String p, String source) {
printSplit(p, source, true);
printSplit(p, source, false);
}
static void printSplit(String p, String source, boolean f) {
System.out.print(p);
System.out.print(": \u2066");
for (int i = 0; i < source.length(); i++) {
if (f) {
System.out.print(source.charAt(i));
System.out.print(" \u200e");
} else {
System.out.print(String.format("%04x ", (int) source.charAt(i)));
}
}
// System.out.println(Arrays.toString(source.toCharArray()));
System.out.println();
System.out.flush();
}
private static void test2() {
public static void test2() {
String s = "گچ پژ نمکی باللغة العربي";
String reshape = reshape(s);
if (!reshape.equals("ﻲﺑﺮﻌﻟﺍ ﺔﻐﻠﻟﺎﺑ ﯽﮑﻤﻧ ﮋﭘ ﭻﮔ")) {
throw new IllegalArgumentException("BUG!!!");
}
String expected1 = "ﻲﺑﺮﻌﻟﺍ ﺔﻐﻠﻟﺎﺑ ﯽﮑﻤﻧ ﮋﭘ ﭻﮔ";
String expected2 = "ﻲﺑﺮﻌﻟﺍ ﺔﻐﻠﻟﺎﺑ یکﻤﻧ ژپ چگ";
check(s, reshape, expected1);
}
public static void test4() {
String s = "Abc (123)";
check(s, reshape(s), s);
}
}
}

View file

@ -221,7 +221,7 @@ public class BinaryMapAddressReaderAdapter {
int fp = codedIS.getTotalBytesRead();
int length = codedIS.readRawVarint32();
int oldLimit = codedIS.pushLimit(length);
City c = readCityHeader(new DefaultCityMatcher(matcher), fp, additionalTagsTable);
City c = readCityHeader(resultMatcher, new DefaultCityMatcher(matcher), fp, additionalTagsTable);
if (c != null) {
if (resultMatcher == null || resultMatcher.publish(c)) {
cities.add(c);
@ -256,6 +256,7 @@ public class BinaryMapAddressReaderAdapter {
int oldLimit = codedIS.pushLimit(length);
readStreet(s, null, false, x >> 7, y >> 7, city.isPostcode() ? city.getName() : null,
attributeTagsTable);
publishRawData(resultMatcher, s);
if (resultMatcher == null || resultMatcher.publish(s)) {
city.registerStreet(s);
}
@ -303,7 +304,7 @@ public class BinaryMapAddressReaderAdapter {
}
}
protected City readCityHeader(CityMatcher matcher, int filePointer, List<String> additionalTagsTable) throws IOException {
protected City readCityHeader(SearchRequest<? super City> resultMatcher, CityMatcher matcher, int filePointer, List<String> additionalTagsTable) throws IOException {
int x = 0;
int y = 0;
City c = null;
@ -313,6 +314,7 @@ public class BinaryMapAddressReaderAdapter {
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
publishRawData(resultMatcher, c);
return (matcher == null || matcher.matches(c)) ? c : null;
case OsmandOdb.CityIndex.CITY_TYPE_FIELD_NUMBER:
int type = codedIS.readUInt32();
@ -445,6 +447,7 @@ public class BinaryMapAddressReaderAdapter {
if (loadBuildingsAndIntersected) {
int oldLimit = codedIS.pushLimit(length);
Building b = readBuilding(offset, x, y, additionalTagsTable);
publishRawData(buildingsMatcher, b);
if (postcodeFilter == null || postcodeFilter.equalsIgnoreCase(b.getPostcode())) {
if (buildingsMatcher == null || buildingsMatcher.publish(b)) {
s.addBuilding(b);
@ -688,7 +691,7 @@ public class BinaryMapAddressReaderAdapter {
codedIS.seek(contOffset);
int len = codedIS.readRawVarint32();
int old = codedIS.pushLimit(len);
obj = readCityHeader(null, contOffset, reg.attributeTagsTable);
obj = readCityHeader(req, null, contOffset, reg.attributeTagsTable);
codedIS.popLimit(old);
}
if (obj != null) {
@ -701,6 +704,7 @@ public class BinaryMapAddressReaderAdapter {
readStreet(s, null, false, MapUtils.get31TileNumberX(l.getLongitude()) >> 7,
MapUtils.get31TileNumberY(l.getLatitude()) >> 7, obj.isPostcode() ? obj.getName() : null,
reg.attributeTagsTable);
publishRawData(req, s);
boolean matches = stringMatcher.matches(s.getName());
if (!matches) {
for (String n : s.getAllNames()) {
@ -727,7 +731,8 @@ public class BinaryMapAddressReaderAdapter {
codedIS.seek(offset);
int len = codedIS.readRawVarint32();
int old = codedIS.pushLimit(len);
City obj = readCityHeader(cityPostcodeMatcher, list.get(j), reg.attributeTagsTable);
City obj = readCityHeader(req, cityPostcodeMatcher, list.get(j), reg.attributeTagsTable);
publishRawData(req, obj);
if (obj != null && !published.contains(offset)) {
req.publish(obj);
published.add(offset);
@ -805,4 +810,9 @@ public class BinaryMapAddressReaderAdapter {
}
}
private <T> void publishRawData(SearchRequest<T> resultMatcher, T obj) {
if (resultMatcher != null && obj != null) {
resultMatcher.collectRawData(obj);
}
}
}

View file

@ -496,7 +496,7 @@ public class BinaryMapIndexReader {
}
}
Iterator<Entry<TransportIndex, TIntArrayList>> it = groupPoints.entrySet().iterator();
if (it.hasNext()) {
while (it.hasNext()) {
Entry<TransportIndex, TIntArrayList> e = it.next();
TransportIndex ind = e.getKey();
TIntArrayList pointers = e.getValue();
@ -696,6 +696,7 @@ public class BinaryMapIndexReader {
private void readMapIndex(MapIndex index, boolean onlyInitEncodingRules) throws IOException {
int defaultId = 1;
int oldLimit;
int encodingRulesSize = 0;
while (true) {
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
@ -712,10 +713,14 @@ public class BinaryMapIndexReader {
break;
case OsmandOdb.OsmAndMapIndex.RULES_FIELD_NUMBER :
if (onlyInitEncodingRules) {
if(encodingRulesSize == 0) {
encodingRulesSize = codedIS.getTotalBytesRead();
}
int len = codedIS.readInt32();
oldLimit = codedIS.pushLimit(len);
readMapEncodingRule(index, defaultId++);
codedIS.popLimit(oldLimit);
index.encodingRulesSizeBytes = (codedIS.getTotalBytesRead() - encodingRulesSize);
} else {
skipUnknownField(t);
}
@ -1457,8 +1462,14 @@ public class BinaryMapIndexReader {
public static <T> SearchRequest<T> buildAddressByNameRequest(ResultMatcher<T> resultMatcher, String nameRequest,
StringMatcherMode matcherMode) {
return buildAddressByNameRequest(resultMatcher, null, nameRequest, matcherMode);
}
public static <T> SearchRequest<T> buildAddressByNameRequest(ResultMatcher<T> resultMatcher, ResultMatcher<T> rawDataCollector, String nameRequest,
StringMatcherMode matcherMode) {
SearchRequest<T> request = new SearchRequest<T>();
request.resultMatcher = resultMatcher;
request.rawDataCollector = rawDataCollector;
request.nameQuery = nameRequest.trim();
request.matcherMode = matcherMode;
return request;
@ -1542,6 +1553,10 @@ public class BinaryMapIndexReader {
public static SearchRequest<Amenity> buildSearchPoiRequest(int x, int y, String nameFilter, int sleft, int sright, int stop, int sbottom, ResultMatcher<Amenity> resultMatcher) {
return buildSearchPoiRequest(x, y, nameFilter, sleft, sright, stop, sbottom, resultMatcher, null);
}
public static SearchRequest<Amenity> buildSearchPoiRequest(int x, int y, String nameFilter, int sleft, int sright, int stop, int sbottom, ResultMatcher<Amenity> resultMatcher, ResultMatcher<Amenity> rawDataCollector) {
SearchRequest<Amenity> request = new SearchRequest<Amenity>();
request.x = x;
request.y = y;
@ -1550,6 +1565,7 @@ public class BinaryMapIndexReader {
request.top = stop;
request.bottom = sbottom;
request.resultMatcher = resultMatcher;
request.rawDataCollector = rawDataCollector;
request.nameQuery = nameFilter.trim();
return request;
}
@ -1634,6 +1650,7 @@ public class BinaryMapIndexReader {
private boolean ocean = false;
private ResultMatcher<T> resultMatcher;
private ResultMatcher<T> rawDataCollector;
// 31 zoom tiles
// common variables
@ -1711,6 +1728,12 @@ public class BinaryMapIndexReader {
return false;
}
public void collectRawData(T obj) {
if (rawDataCollector != null) {
rawDataCollector.publish(obj);
}
}
protected void publishOceanTile(boolean ocean) {
if (ocean) {
this.ocean = true;
@ -1811,10 +1834,13 @@ public class BinaryMapIndexReader {
public int onewayReverseAttribute = -1;
public TIntHashSet positiveLayers = new TIntHashSet(2);
public TIntHashSet negativeLayers = new TIntHashSet(2);
public int encodingRulesSizeBytes;
// to speed up comparision
private MapIndex referenceMapIndex;
public Integer getRule(String t, String v) {
Map<String, Integer> m = encodingRules.get(t);
if (m != null) {
@ -2637,18 +2663,20 @@ public class BinaryMapIndexReader {
}
}
public TLongObjectHashMap<IncompleteTransportRoute> getIncompleteTransportRoutes() throws InvalidProtocolBufferException, IOException {
if (incompleteTransportRoutes == null) {
incompleteTransportRoutes = new TLongObjectHashMap<>();
for (TransportIndex ti : transportIndexes) {
if (ti.incompleteRoutesLength > 0) {
transportAdapter.readIncompleteRoutesList(incompleteTransportRoutes, ti.incompleteRoutesLength,
ti.incompleteRoutesOffset);
codedIS.seek(ti.incompleteRoutesOffset);
int oldLimit = codedIS.pushLimit(ti.incompleteRoutesLength);
transportAdapter.readIncompleteRoutesList(incompleteTransportRoutes, ti.filePointer);
codedIS.popLimit(oldLimit);
}
}
}
return incompleteTransportRoutes;
}
}

View file

@ -26,7 +26,6 @@ import net.osmand.data.Amenity.AmenityRoutePoint;
import net.osmand.data.LatLon;
import net.osmand.osm.MapPoiTypes;
import net.osmand.osm.PoiCategory;
import net.osmand.osm.PoiType;
import net.osmand.util.MapUtils;
import org.apache.commons.logging.Log;
@ -591,6 +590,7 @@ public class BinaryMapPoiReaderAdapter {
}
}
if (matches) {
req.collectRawData(am);
req.publish(am);
}
}
@ -637,6 +637,7 @@ public class BinaryMapPoiReaderAdapter {
int yp = (int) MapUtils.getTileNumberY(zSkip, am.getLocation().getLatitude());
long valSkip = (((long) xp) << zSkip) | yp;
if (!toSkip.contains(valSkip)) {
req.collectRawData(am);
boolean publish = req.publish(am);
if (publish) {
read = true;
@ -647,6 +648,7 @@ public class BinaryMapPoiReaderAdapter {
return read;
}
} else {
req.collectRawData(am);
if (req.publish(am)) {
read = true;
}
@ -770,7 +772,8 @@ public class BinaryMapPoiReaderAdapter {
}
}
subtype = poiTypes.replaceDeprecatedSubtype(type, subtype);
if (req.poiTypeFilter == null || req.poiTypeFilter.accept(type, subtype)) {
boolean isForbidden = poiTypes.isTypeForbidden(subtype);
if (!isForbidden && (req.poiTypeFilter == null || req.poiTypeFilter.accept(type, subtype))) {
if (amenityType == null) {
amenityType = type;
am.setSubType(subtype);

View file

@ -1,9 +1,25 @@
package net.osmand.binary;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.WireFormat;
import gnu.trove.iterator.TLongObjectIterator;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.list.array.TLongArrayList;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
@ -18,23 +34,6 @@ import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
import net.osmand.util.OpeningHoursParser;
import org.apache.commons.logging.Log;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import gnu.trove.iterator.TLongObjectIterator;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.list.array.TLongArrayList;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.map.hash.TLongObjectHashMap;
public class BinaryMapRouteReaderAdapter {
protected static final Log LOG = PlatformUtil.getLog(BinaryMapRouteReaderAdapter.class);
private static final int SHIFT_COORDINATES = 4;
@ -172,7 +171,7 @@ public class BinaryMapRouteReaderAdapter {
}
return tag;
}
public int onewayDirection(){
if(type == ONEWAY){
return intValue;
@ -293,10 +292,19 @@ public class BinaryMapRouteReaderAdapter {
public static class RouteRegion extends BinaryIndexPart {
public int regionsRead;
public List<RouteTypeRule> routeEncodingRules = new ArrayList<BinaryMapRouteReaderAdapter.RouteTypeRule>();
public int routeEncodingRulesBytes = 0;
public Map<String, Integer> decodingRules = null;
List<RouteSubregion> subregions = new ArrayList<RouteSubregion>();
List<RouteSubregion> basesubregions = new ArrayList<RouteSubregion>();
public int directionForward = -1;
public int directionBackward = -1;
public int directionTrafficSignalsForward = -1;
public int directionTrafficSignalsBackward = -1;
public int trafficSignals = -1;
public int stopSign = -1;
public int giveWaySign = -1;
int nameTypeRule = -1;
int refTypeRule = -1;
int destinationTypeRule = -1;
@ -353,9 +361,28 @@ public class BinaryMapRouteReaderAdapter {
destinationTypeRule = id;
} else if (tags.equals("destination:ref") || tags.equals("destination:ref:forward") || tags.equals("destination:ref:backward")) {
destinationRefTypeRule = id;
} else if (tags.equals("highway") && val.equals("traffic_signals")){
trafficSignals = id;
} else if (tags.equals("highway") && val.equals("stop")){
stopSign = id;
} else if (tags.equals("highway") && val.equals("give_way")){
giveWaySign = id;
} else if (tags.equals("traffic_signals:direction") && val != null){
if (val.equals("forward")) {
directionTrafficSignalsForward = id;
} else if (val.equals("backward")) {
directionTrafficSignalsBackward = id;
}
} else if (tags.equals("direction") && val != null) {
if (val.equals("forward")) {
directionForward = id;
} else if (val.equals("backward")) {
directionBackward = id;
}
}
}
public void completeRouteEncodingRules() {
for(int i = 0; i < routeEncodingRules.size(); i++) {
RouteTypeRule rtr = routeEncodingRules.get(i);
@ -588,6 +615,7 @@ public class BinaryMapRouteReaderAdapter {
protected void readRouteIndex(RouteRegion region) throws IOException {
int routeEncodingRule = 1;
int routeEncodingRulesSize = 0;
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
@ -600,10 +628,13 @@ public class BinaryMapRouteReaderAdapter {
break;
case OsmandOdb.OsmAndRoutingIndex.RULES_FIELD_NUMBER: {
int len = codedIS.readInt32();
if(routeEncodingRulesSize == 0) {
routeEncodingRulesSize = codedIS.getTotalBytesRead();
}
int oldLimit = codedIS.pushLimit(len);
readRouteEncodingRule(region, routeEncodingRule++);
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
codedIS.popLimit(oldLimit);
region.routeEncodingRulesBytes = codedIS.getTotalBytesRead() - routeEncodingRulesSize;
} break;
case OsmandOdb.OsmAndRoutingIndex.ROOTBOXES_FIELD_NUMBER :
case OsmandOdb.OsmAndRoutingIndex.BASEMAPBOXES_FIELD_NUMBER :{

View file

@ -11,6 +11,7 @@ import com.google.protobuf.WireFormat;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.data.IncompleteTransportRoute;
import net.osmand.data.TransportSchedule;
import net.osmand.data.TransportStop;
import net.osmand.data.TransportStopExit;
@ -71,7 +72,6 @@ public class BinaryMapTransportReaderAdapter {
return bottom;
}
IndexStringTable stringTable = null;
}
@ -251,9 +251,7 @@ public class BinaryMapTransportReaderAdapter {
return ((char) i)+"";
}
public void readIncompleteRoutesList(TLongObjectHashMap<net.osmand.data.IncompleteTransportRoute> incompleteRoutes,
int length, int offset) throws IOException {
codedIS.seek(offset);
public void readIncompleteRoutesList(TLongObjectHashMap<net.osmand.data.IncompleteTransportRoute> incompleteRoutes, int transportIndexStart) throws IOException {
boolean end = false;
while (!end) {
int t = codedIS.readTag();
@ -265,7 +263,7 @@ public class BinaryMapTransportReaderAdapter {
case OsmandOdb.IncompleteTransportRoutes.ROUTES_FIELD_NUMBER:
int l = codedIS.readRawVarint32();
int olds = codedIS.pushLimit(l);
net.osmand.data.IncompleteTransportRoute ir = readIncompleteRoute();
net.osmand.data.IncompleteTransportRoute ir = readIncompleteRoute(transportIndexStart);
net.osmand.data.IncompleteTransportRoute itr = incompleteRoutes.get(ir.getRouteId());
if(itr != null) {
itr.setNextLinkedRoute(ir);
@ -283,7 +281,7 @@ public class BinaryMapTransportReaderAdapter {
}
public net.osmand.data.IncompleteTransportRoute readIncompleteRoute() throws IOException {
public net.osmand.data.IncompleteTransportRoute readIncompleteRoute(int transportIndexStart) throws IOException {
net.osmand.data.IncompleteTransportRoute dataObject = new net.osmand.data.IncompleteTransportRoute();
boolean end = false;
while(!end){
@ -297,7 +295,12 @@ public class BinaryMapTransportReaderAdapter {
dataObject.setRouteId(codedIS.readUInt64());
break;
case OsmandOdb.IncompleteTransportRoute.ROUTEREF_FIELD_NUMBER :
dataObject.setRouteOffset(codedIS.readRawVarint32());
int delta = codedIS.readRawVarint32();
if (delta > transportIndexStart) {
dataObject.setRouteOffset(delta);
} else {
dataObject.setRouteOffset(transportIndexStart + delta);
}
break;
case OsmandOdb.IncompleteTransportRoute.OPERATOR_FIELD_NUMBER :
skipUnknownField(t);
@ -320,7 +323,6 @@ public class BinaryMapTransportReaderAdapter {
break;
}
}
return dataObject;
}

View file

@ -13,10 +13,6 @@ public class CommonWords {
frequentlyUsedWordsDictionary.put(string, frequentlyUsedWordsDictionary.size());
}
public static int getCommon(String name) {
// if(true) {
// // not ready for old versions yet
// return -1;
// }
Integer i = commonWordsDictionary.get(name);
return i == null ? -1 : i.intValue();
}
@ -28,7 +24,15 @@ public class CommonWords {
public static int getCommonSearch(String name) {
Integer i = commonWordsDictionary.get(name);
return i == null ? getFrequentlyUsed(name) : i.intValue() + frequentlyUsedWordsDictionary.size();
// higher means better for search
if (i == null) {
int fq = getFrequentlyUsed(name);
if (fq != -1) {
return commonWordsDictionary.size() + fq;
}
return -1;
}
return i.intValue();
}
public static int getCommonGeocoding(String name) {
@ -839,6 +843,7 @@ public class CommonWords {
addCommon("van");
addCommon("road");
addCommon("street");
addCommon("sector");
addCommon("drive");
addCommon("avenue");
addCommon("rue");

View file

@ -4,6 +4,7 @@ import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher;
import net.osmand.CollatorStringMatcher.StringMatcherMode;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
import net.osmand.data.Building;
import net.osmand.data.City;
import net.osmand.data.LatLon;
@ -23,9 +24,11 @@ import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import gnu.trove.set.hash.TLongHashSet;
@ -40,7 +43,7 @@ public class GeocodingUtilities {
public static final float STOP_SEARCHING_STREET_WITH_MULTIPLIER_RADIUS = 250;
public static final float STOP_SEARCHING_STREET_WITHOUT_MULTIPLIER_RADIUS = 400;
public static final int DISTANCE_STREET_NAME_PROXIMITY_BY_NAME = 15000;
public static final int DISTANCE_STREET_NAME_PROXIMITY_BY_NAME = 45000;
public static final float DISTANCE_STREET_FROM_CLOSEST_WITH_SAME_NAME = 1000;
public static final float THRESHOLD_MULTIPLIER_SKIP_BUILDINGS_AFTER = 1.5f;
@ -142,15 +145,12 @@ public class GeocodingUtilities {
RoutePlannerFrontEnd rp = new RoutePlannerFrontEnd();
List<GeocodingResult> lst = new ArrayList<GeocodingUtilities.GeocodingResult>();
List<RouteSegmentPoint> listR = new ArrayList<BinaryRoutePlanner.RouteSegmentPoint>();
rp.findRouteSegment(lat, lon, ctx, listR);
// we allow duplications to search in both files for boundary regions
rp.findRouteSegment(lat, lon, ctx, listR, false, true);
double distSquare = 0;
TLongHashSet set = new TLongHashSet();
Set<String> streetNames = new HashSet<String>();
Map<String, List<RouteRegion>> streetNames = new HashMap<>();
for (RouteSegmentPoint p : listR) {
RouteDataObject road = p.getRoad();
if (!set.add(road.getId())) {
continue;
}
// System.out.println(road.toString() + " " + Math.sqrt(p.distSquare));
String name = Algorithms.isEmpty(road.getName()) ? road.getRef("", false, true) : road.getName();
if (allowEmptyNames || !Algorithms.isEmpty(name)) {
@ -164,7 +164,13 @@ public class GeocodingUtilities {
sr.connectionPoint = new LatLon(MapUtils.get31LatitudeY(p.preciseY), MapUtils.get31LongitudeX(p.preciseX));
sr.regionFP = road.region.getFilePointer();
sr.regionLen = road.region.getLength();
if (streetNames.add(sr.streetName)) {
List<RouteRegion> plst = streetNames.get(sr.streetName);
if (plst == null) {
plst = new ArrayList<BinaryMapRouteReaderAdapter.RouteRegion>();
streetNames.put(sr.streetName, plst);
}
if (!plst.contains(road.region)) {
plst.add(road.region);
lst.add(sr);
}
}
@ -308,6 +314,50 @@ public class GeocodingUtilities {
return res;
}
public void filterDuplicateRegionResults(final List<GeocodingResult> res) {
Collections.sort(res, DISTANCE_COMPARATOR);
// filter duplicate city results (when building is in both regions on boundary)
for (int i = 0; i < res.size() - 1;) {
int cmp = cmpResult(res.get(i), res.get(i + 1));
if (cmp > 0) {
res.remove(i);
} else if (cmp < 0) {
res.remove(i + 1);
} else {
// nothing to delete
i++;
}
}
}
private int cmpResult(GeocodingResult gr1, GeocodingResult gr2) {
boolean eqStreet = Algorithms.stringsEqual(gr1.streetName, gr2.streetName);
if (eqStreet) {
boolean sameObj = false;
if (gr1.city != null && gr2.city != null) {
if (gr1.building != null && gr2.building != null) {
if (Algorithms.stringsEqual(gr1.building.getName(), gr2.building.getName())) {
// same building
sameObj = true;
}
} else if (gr1.building == null && gr2.building == null) {
// same street
sameObj = true;
}
}
if (sameObj) {
double cityDist1 = MapUtils.getDistance(gr1.searchPoint, gr1.city.getLocation());
double cityDist2 = MapUtils.getDistance(gr2.searchPoint, gr2.city.getLocation());
if (cityDist1 < cityDist2) {
return -1;
} else {
return 1;
}
}
}
return 0;
}
private List<GeocodingResult> loadStreetBuildings(final GeocodingResult road, BinaryMapIndexReader reader,
GeocodingResult street) throws IOException {
final List<GeocodingResult> streetBuildings = new ArrayList<GeocodingResult>();

View file

@ -1,12 +1,15 @@
package net.osmand.binary;
import net.osmand.Location;
import net.osmand.PlatformUtil;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
import net.osmand.util.TransliterationHelper;
import org.apache.commons.logging.Log;
import java.text.MessageFormat;
import java.util.Arrays;
@ -34,7 +37,7 @@ public class RouteDataObject {
public int[] nameIds;
// mixed array [0, height, cumulative_distance height, cumulative_distance, height, ...] - length is length(points)*2
public float[] heightDistanceArray = null;
private static final Log LOG = PlatformUtil.getLog(RouteDataObject.class);
public RouteDataObject(RouteRegion region) {
this.region = region;
}
@ -56,6 +59,7 @@ public class RouteDataObject {
this.pointsY = copy.pointsY;
this.types = copy.types;
this.names = copy.names;
this.nameIds = copy.nameIds;
this.restrictions = copy.restrictions;
this.restrictionsVia = copy.restrictionsVia;
this.pointTypes = copy.pointTypes;
@ -426,12 +430,19 @@ public class RouteDataObject {
int[] opointsX = pointsX;
int[] opointsY = pointsY;
int[][] opointTypes = pointTypes;
String[][] opointNames = pointNames;
int[][] opointNameTypes = pointNameTypes;
pointsX = new int[pointsX.length + 1];
pointsY = new int[pointsY.length + 1];
boolean insTypes = this.pointTypes != null && this.pointTypes.length > pos;
boolean insNames = this.pointNames != null && this.pointNames.length > pos;
if (insTypes) {
pointTypes = new int[opointTypes.length + 1][];
}
if (insNames) {
pointNames = new String[opointNames.length + 1][];
pointNameTypes = new int[opointNameTypes.length +1][];
}
int i = 0;
for (; i < pos; i++) {
pointsX[i] = opointsX[i];
@ -439,18 +450,32 @@ public class RouteDataObject {
if (insTypes) {
pointTypes[i] = opointTypes[i];
}
if (insNames) {
pointNames[i] = opointNames[i];
pointNameTypes[i] = opointNameTypes[i];
}
}
pointsX[i] = x31;
pointsY[i] = y31;
if (insTypes) {
pointTypes[i] = null;
}
if (insNames) {
pointNames[i] = null;
pointNameTypes[i] = null;
}
for (i = i + 1; i < pointsX.length; i++) {
pointsX[i] = opointsX[i - 1];
pointsY[i] = opointsY[i - 1];
if (insTypes && i < pointTypes.length) {
pointTypes[i] = opointTypes[i - 1];
}
if (insNames && i < pointNames.length) {
pointNames[i] = opointNames[i - 1];
}
if (insNames && i < pointNameTypes.length) {
pointNameTypes[i] = opointNameTypes[i - 1];
}
}
}
@ -1054,6 +1079,4 @@ public class RouteDataObject {
}
restrictionsVia[k] = viaWay;
}
}

View file

@ -21,7 +21,7 @@ public class StringBundle {
private static final DecimalFormat FIVE_DIGITS_FORMATTER = new DecimalFormat("#.#####");
private static final DecimalFormat SIX_DIGITS_FORMATTER = new DecimalFormat("#.######");
private Map<String, Item> map = new LinkedHashMap<>();
private Map<String, Item<?>> map = new LinkedHashMap<>();
public enum ItemType {
STRING,
@ -32,7 +32,7 @@ public class StringBundle {
public StringBundle() {
}
protected StringBundle(Map<String, Item> map) {
protected StringBundle(Map<String, Item<?>> map) {
this.map = map;
}
@ -156,16 +156,16 @@ public class StringBundle {
}
}
public static class StringListItem extends Item<List<Item>> {
public static class StringListItem extends Item<List<Item<?>>> {
private StringListItem(String name, List<Item> list) {
private StringListItem(String name, List<Item<?>> list) {
super(name, ItemType.LIST, list);
}
}
public static class StringMapItem extends Item<Map<String, Item>> {
public static class StringMapItem extends Item<Map<String, Item<?>>> {
private StringMapItem(String name, Map<String, Item> map) {
private StringMapItem(String name, Map<String, Item<?>> map) {
super(name, ItemType.MAP, map);
}
}
@ -177,11 +177,11 @@ public class StringBundle {
}
}
public Map<String, Item> getMap() {
public Map<String, Item<?>> getMap() {
return Collections.unmodifiableMap(map);
}
public Item getItem(String key) {
public Item<?> getItem(String key) {
return map.get(key);
}
@ -190,7 +190,7 @@ public class StringBundle {
}
public int getInt(String key, int defaultValue) {
Item item = map.get(key);
Item<?> item = map.get(key);
return item instanceof StringItem ? ((StringItem) item).asInt(defaultValue) : defaultValue;
}
@ -199,7 +199,7 @@ public class StringBundle {
}
public long getLong(String key, long defaultValue) {
Item item = map.get(key);
Item<?> item = map.get(key);
return item instanceof StringItem ? ((StringItem) item).asLong(defaultValue) : defaultValue;
}
@ -212,7 +212,7 @@ public class StringBundle {
}
public float getFloat(String key, float defaultValue) {
Item item = map.get(key);
Item<?> item = map.get(key);
return item instanceof StringItem ? ((StringItem) item).asFloat(defaultValue) : defaultValue;
}
@ -221,7 +221,7 @@ public class StringBundle {
}
public boolean getBoolean(String key, boolean defaultValue) {
Item item = map.get(key);
Item<?> item = map.get(key);
return item instanceof StringItem ? ((StringItem) item).asBoolean(defaultValue) : defaultValue;
}
@ -232,35 +232,13 @@ public class StringBundle {
}
public String getString(String key, String defaultValue) {
Item item = map.get(key);
Item<?> item = map.get(key);
return item instanceof StringItem ? ((StringItem) item).getValue() : defaultValue;
}
public void putObject(String key, StringExternalizable object) {
if (object != null) {
StringBundle bundle = newInstance();
object.writeToBundle(bundle);
map.put(key, new StringBundleItem(key, bundle));
}
}
public void putList(String key, String itemName, List<? extends StringExternalizable> list) {
if (list != null) {
List<Item> itemList = new ArrayList<>();
for (StringExternalizable ex : list) {
if (ex != null) {
StringBundle bundle = newInstance();
ex.writeToBundle(bundle);
itemList.add(new StringBundleItem(itemName, bundle));
}
}
map.put(key, new StringListItem(key, itemList));
}
}
public void putBundleList(String key, String itemName, List<StringBundle> list) {
if (list != null) {
List<Item> itemList = new ArrayList<>();
List<Item<?>> itemList = new ArrayList<>();
for (StringBundle bundle : list) {
itemList.add(new StringBundleItem(itemName, bundle));
}
@ -279,7 +257,7 @@ public class StringBundle {
}
public int[] getIntArray(String key, int[] defaultValue) {
Item item = map.get(key);
Item<?> item = map.get(key);
return item instanceof StringItem ? ((StringItem) item).asIntArray(defaultValue) : defaultValue;
}
@ -290,7 +268,7 @@ public class StringBundle {
}
public int[][] getIntIntArray(String key, int[][] defaultValue) {
Item item = map.get(key);
Item<?> item = map.get(key);
return item instanceof StringItem ? ((StringItem) item).asIntIntArray(defaultValue) : defaultValue;
}

View file

@ -16,10 +16,10 @@ public abstract class StringBundleWriter {
return bundle;
}
protected abstract void writeItem(String name, Item item);
protected abstract void writeItem(String name, Item<?> item);
public void writeBundle() {
for (Entry<String, Item> entry : bundle.getMap().entrySet()) {
for (Entry<String, Item<?>> entry : bundle.getMap().entrySet()) {
writeItem(entry.getKey(), entry.getValue());
}
}

View file

@ -25,7 +25,7 @@ public class StringBundleXmlWriter extends StringBundleWriter {
}
@Override
protected void writeItem(String name, Item item) {
protected void writeItem(String name, Item<?> item) {
if (serializer != null) {
try {
writeItemImpl(name, item);
@ -47,7 +47,7 @@ public class StringBundleXmlWriter extends StringBundleWriter {
}
}
private void writeItemImpl(String name, Item item) throws IOException {
private void writeItemImpl(String name, Item<?> item) throws IOException {
if (serializer != null && item != null) {
switch (item.getType()) {
case STRING: {
@ -58,13 +58,13 @@ public class StringBundleXmlWriter extends StringBundleWriter {
case LIST: {
StringListItem listItem = (StringListItem) item;
serializer.startTag(null, name);
List<Item> list = listItem.getValue();
for (Item i : list) {
List<Item<?>> list = listItem.getValue();
for (Item<?> i : list) {
if (i.getType() == StringBundle.ItemType.STRING) {
writeItemImpl(i.getName(), i);
}
}
for (Item i : list) {
for (Item<?> i : list) {
if (i.getType() != StringBundle.ItemType.STRING) {
writeItemImpl(i.getName(), i);
}
@ -75,14 +75,14 @@ public class StringBundleXmlWriter extends StringBundleWriter {
case MAP: {
StringMapItem mapItem = (StringMapItem) item;
serializer.startTag(null, name);
for (Entry<String, Item> entry : mapItem.getValue().entrySet()) {
Item i = entry.getValue();
for (Entry<String, Item<?>> entry : mapItem.getValue().entrySet()) {
Item<?> i = entry.getValue();
if (i.getType() == StringBundle.ItemType.STRING) {
writeItemImpl(entry.getKey(), i);
}
}
for (Entry<String, Item> entry : mapItem.getValue().entrySet()) {
Item i = entry.getValue();
for (Entry<String, Item<?>> entry : mapItem.getValue().entrySet()) {
Item<?> i = entry.getValue();
if (i.getType() != StringBundle.ItemType.STRING) {
writeItemImpl(entry.getKey(), i);
}

View file

@ -38,6 +38,8 @@ public class Amenity extends MapObject {
public static final String COLLECTION_TIMES = "collection_times";
public static final String CONTENT = "content";
public static final String CUISINE = "cuisine";
public static final String WIKIDATA = "wikidata";
public static final String WIKIMEDIA_COMMONS = "wikimedia_commons";
public static final String DISH = "dish";
public static final String REF = "ref";
public static final String OSM_DELETE_VALUE = "delete";
@ -93,33 +95,6 @@ public class Amenity extends MapObject {
return str;
}
public String unzipContent(String str) {
if (str != null) {
if (str.startsWith(" gz ")) {
try {
int ind = 4;
byte[] bytes = new byte[str.length() - ind];
for (int i = ind; i < str.length(); i++) {
char ch = str.charAt(i);
bytes[i - ind] = (byte) ((int) ch - 128 - 32);
}
GZIPInputStream gzn = new GZIPInputStream(new ByteArrayInputStream(bytes));
BufferedReader br = new BufferedReader(new InputStreamReader(gzn, "UTF-8"));
StringBuilder bld = new StringBuilder();
String s;
while ((s = br.readLine()) != null) {
bld.append(s);
}
br.close();
str = bld.toString();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return str;
}
public Map<String, String> getAdditionalInfo() {
if (additionalInfo == null) {

View file

@ -161,8 +161,12 @@ public class City extends MapObject {
public JSONObject toJSON(boolean includingBuildings) {
JSONObject json = super.toJSON();
json.put("type", type.name());
json.put("postcode", postcode);
if (type != null) {
json.put("type", type.name());
}
if (postcode != null) {
json.put("postcode", postcode);
}
JSONArray listOfStreetsArr = new JSONArray();
for (Street s : listOfStreets) {
listOfStreetsArr.put(s.toJSON(includingBuildings));

View file

@ -13,7 +13,11 @@ public class IncompleteTransportRoute {
}
public void setNextLinkedRoute(IncompleteTransportRoute nextLinkedRoute) {
this.nextLinkedRoute = nextLinkedRoute;
if (this.nextLinkedRoute == null) {
this.nextLinkedRoute = nextLinkedRoute;
} else {
this.nextLinkedRoute.setNextLinkedRoute(nextLinkedRoute);
}
}
public long getRouteId() {

View file

@ -8,6 +8,10 @@ import net.osmand.util.TransliterationHelper;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -18,6 +22,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.GZIPInputStream;
public abstract class MapObject implements Comparable<MapObject> {
@ -55,7 +60,7 @@ public abstract class MapObject implements Comparable<MapObject> {
public String getName() {
if (name != null) {
return name;
return unzipContent(name);
}
return ""; //$NON-NLS-1$
}
@ -73,7 +78,7 @@ public abstract class MapObject implements Comparable<MapObject> {
if (names == null) {
names = new HashMap<String, String>();
}
names.put(lang, name);
names.put(lang, unzipContent(name));
}
}
@ -95,19 +100,25 @@ public abstract class MapObject implements Comparable<MapObject> {
}
Map<String, String> mp = new HashMap<String, String>();
if (names != null) {
mp.putAll(names);
Iterator<Entry<String, String>> it = mp.entrySet().iterator();
while(it.hasNext()) {
Entry<String, String> e = it.next();
mp.put(e.getKey(), unzipContent(e.getValue()));
}
}
mp.put("en", enName);
mp.put("en", unzipContent(enName));
return mp;
}
public List<String> getAllNames() {
List<String> l = new ArrayList<String>();
if (!Algorithms.isEmpty(enName)) {
l.add(enName);
l.add(unzipContent(enName));
}
if (names != null) {
l.addAll(names.values());
for(String nm : names.values()) {
l.add(unzipContent(nm));
}
}
return l;
}
@ -179,7 +190,7 @@ public abstract class MapObject implements Comparable<MapObject> {
if (names != null) {
String nm = names.get(lang);
if (!Algorithms.isEmpty(nm)) {
return nm;
return unzipContent(nm);
}
if (transliterate) {
return TransliterationHelper.transliterate(getName());
@ -192,7 +203,7 @@ public abstract class MapObject implements Comparable<MapObject> {
public String getEnName(boolean transliterate) {
if (!Algorithms.isEmpty(enName)) {
return this.enName;
return unzipContent(this.enName);
} else if (!Algorithms.isEmpty(getName()) && transliterate) {
return TransliterationHelper.transliterate(getName());
}
@ -322,12 +333,12 @@ public abstract class MapObject implements Comparable<MapObject> {
public JSONObject toJSON() {
JSONObject json = new JSONObject();
json.put("name", name);
json.put("enName", enName);
json.put("name", unzipContent(name));
json.put("enName", unzipContent(enName));
if (names != null && names.size() > 0) {
JSONObject namesObj = new JSONObject();
for (Entry<String, String> e : names.entrySet()) {
namesObj.put(e.getKey(), e.getValue());
namesObj.put(e.getKey(), unzipContent(e.getValue()));
}
json.put("names", namesObj);
}
@ -339,6 +350,31 @@ public abstract class MapObject implements Comparable<MapObject> {
return json;
}
public String unzipContent(String str) {
if (str != null && str.startsWith(" gz ")) {
try {
int ind = 4;
byte[] bytes = new byte[str.length() - ind];
for (int i = ind; i < str.length(); i++) {
char ch = str.charAt(i);
bytes[i - ind] = (byte) ((int) ch - 128 - 32);
}
GZIPInputStream gzn = new GZIPInputStream(new ByteArrayInputStream(bytes));
BufferedReader br = new BufferedReader(new InputStreamReader(gzn, "UTF-8"));
StringBuilder bld = new StringBuilder();
String s;
while ((s = br.readLine()) != null) {
bld.append(s);
}
br.close();
str = bld.toString();
} catch (IOException e) {
e.printStackTrace();
}
}
return str;
}
protected static void parseJSON(JSONObject json, MapObject o) {
if (json.has("name")) {

View file

@ -22,8 +22,6 @@ public class TransportStop extends MapObject {
public int y31;
private List<TransportStopExit> exits;
private List<TransportRoute> routes = null;
private LinkedHashMap<String, int[]> referencesToRoutesMap;
private TransportStopAggregated transportStopAggregated;
public TransportStop() {}
@ -35,19 +33,6 @@ public class TransportStop extends MapObject {
public boolean isMissingStop() {
return MISSING_STOP_NAME.equals(getName());
}
public LinkedHashMap<String, int[]> getReferencesToRoutesMap() {
return referencesToRoutesMap;
}
public void putReferencesToRoutes(String repositoryFileName, int[] referencesToRoutes) {
LinkedHashMap<String, int[]> referencesToRoutesMap = this.referencesToRoutesMap;
if (referencesToRoutesMap == null) {
referencesToRoutesMap = new LinkedHashMap<>();
this.referencesToRoutesMap = referencesToRoutesMap;
}
referencesToRoutesMap.put(repositoryFileName, referencesToRoutes);
}
public void setRoutes(List<TransportRoute> routes) {
this.routes = routes;
@ -115,10 +100,6 @@ public class TransportStop extends MapObject {
return !isDeleted() && referencesToRoutes != null && referencesToRoutes.length > 0;
}
public boolean hasReferencesToRoutesMap() {
return !isDeleted() && referencesToRoutesMap != null && !referencesToRoutesMap.isEmpty();
}
public Amenity getAmenity() {
if (transportStopAggregated != null) {
return transportStopAggregated.getAmenity();

View file

@ -32,6 +32,8 @@ public interface ITileSource {
public String getReferer();
public String getUserAgent();
public void deleteTiles(String path);
public int getAvgSize();

View file

@ -7,7 +7,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.util.ArrayList;
@ -91,6 +90,7 @@ public class MapTileDownloader {
public final int yTile;
public String url;
public String referer = null;
public String userAgent = null;
public boolean error;
public DownloadRequest(String url, File fileToSave, int xTile, int yTile, int zoom) {
@ -267,7 +267,7 @@ public class MapTileDownloader {
request.setError(false);
try {
URLConnection connection = NetworkUtils.getHttpURLConnection(request.url);
connection.setRequestProperty("User-Agent", USER_AGENT); //$NON-NLS-1$
connection.setRequestProperty("User-Agent", Algorithms.isEmpty(request.userAgent) ? USER_AGENT : request.userAgent); //$NON-NLS-1$
if (request.referer != null)
connection.setRequestProperty("Referer", request.referer); //$NON-NLS-1$
connection.setConnectTimeout(CONNECTION_TIMEOUT);

View file

@ -68,6 +68,7 @@ public class TileSourceManager {
private String[] randomsArray;
private String rule;
private String referer;
private String userAgent;
private boolean hidden; // if hidden in configure map settings, for example mapillary sources
private boolean isRuleAcceptable = true;
@ -261,6 +262,14 @@ public class TileSourceManager {
this.referer = referer;
}
public String getUserAgent() {
return userAgent;
}
public void setUserAgent(String userAgent) {
this.userAgent = userAgent;
}
@Override
public int getTileSize() {
return tileSize;
@ -484,6 +493,9 @@ public class TileSourceManager {
if (!Algorithms.isEmpty(tm.getReferer())) {
properties.put("referer", tm.getReferer());
}
if (!Algorithms.isEmpty(tm.getUserAgent())) {
properties.put("user_agent", tm.getUserAgent());
}
properties.put("ext", tm.getTileFormat());
properties.put("min_zoom", tm.getMinimumZoomSupported() + "");
@ -708,6 +720,12 @@ public class TileSourceManager {
}
String randoms = attributes.get("randoms");
TileSourceTemplate templ = new TileSourceTemplate(name, urlTemplate, ext, maxZoom, minZoom, tileSize, bitDensity, avgTileSize);
if (attributes.get("referer") != null) {
templ.setReferer(attributes.get("referer"));
}
if (attributes.get("user_agent") != null) {
templ.setUserAgent(attributes.get("user_agent"));
}
if(expirationTime >= 0) {
templ.setExpirationTimeMinutes(expirationTime);
}

View file

@ -67,7 +67,6 @@ public abstract class AbstractPoiType {
return this instanceof PoiType && this.isAdditional();
}
public String getTranslation() {
if(translation == null) {
translation = registry.getTranslation(this);

View file

@ -17,6 +17,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@ -29,16 +30,19 @@ import java.util.TreeSet;
public class MapPoiTypes {
private static final String OTHER_MAP_CATEGORY = "Other";
private static MapPoiTypes DEFAULT_INSTANCE = null;
private static final Log log = PlatformUtil.getLog(MapRenderingTypes.class);
private String resourceName;
private List<PoiCategory> categories = new ArrayList<PoiCategory>();
private Set<String> forbiddenTypes = new HashSet<>();
private PoiCategory otherCategory;
private PoiCategory otherMapCategory;
public static final String WIKI_LANG = "wiki_lang";
public static final String WIKI_PLACE = "wiki_place";
public static final String OSM_WIKI_CATEGORY = "osmwiki";
public static final String SPEED_CAMERA = "speed_camera";
private PoiTranslator poiTranslator = null;
private boolean init;
@ -96,7 +100,7 @@ public class MapPoiTypes {
public PoiCategory getOtherMapCategory() {
if (otherMapCategory == null) {
otherMapCategory = getPoiCategoryByName("Other", true);
otherMapCategory = getPoiCategoryByName(OTHER_MAP_CATEGORY, true);
}
return otherMapCategory;
}
@ -111,7 +115,8 @@ public class MapPoiTypes {
public List<AbstractPoiType> getTopVisibleFilters() {
List<AbstractPoiType> lf = new ArrayList<AbstractPoiType>();
for (PoiCategory pc : categories) {
for (int i = 0; i < categories.size(); i++) {
PoiCategory pc = categories.get(i);
if (pc.isTopVisible()) {
lf.add(pc);
}
@ -131,7 +136,8 @@ public class MapPoiTypes {
}
public PoiCategory getOsmwiki() {
for (PoiCategory category : categories) {
for (int i = 0; i < categories.size(); i++) {
PoiCategory category = categories.get(i);
if (category.isWiki()) {
return category;
}
@ -167,7 +173,8 @@ public class MapPoiTypes {
}
public PoiType getPoiTypeByKey(String name) {
for (PoiCategory pc : categories) {
for (int i = 0; i < categories.size(); i++) {
PoiCategory pc = categories.get(i);
PoiType pt = pc.getPoiTypeByKeyName(name);
if (pt != null && !pt.isReference()) {
return pt;
@ -184,7 +191,8 @@ public class MapPoiTypes {
}
public AbstractPoiType getAnyPoiTypeByKey(String name) {
for (PoiCategory pc : categories) {
for (int i = 0; i < categories.size(); i++) {
PoiCategory pc = categories.get(i);
if (pc.getKeyName().equals(name)) {
return pc;
}
@ -232,7 +240,8 @@ public class MapPoiTypes {
public List<AbstractPoiType> getAllTypesTranslatedNames(StringMatcher matcher) {
List<AbstractPoiType> tm = new ArrayList<AbstractPoiType>();
for (PoiCategory pc : categories) {
for (int i = 0; i < categories.size(); i++) {
PoiCategory pc = categories.get(i);
if (pc == otherMapCategory) {
continue;
}
@ -295,15 +304,21 @@ public class MapPoiTypes {
}
if (create) {
PoiCategory lastCategory = new PoiCategory(this, name, categories.size());
if (!lastCategory.getKeyName().equals("Other")) {
if (!lastCategory.getKeyName().equals(OTHER_MAP_CATEGORY)) {
lastCategory.setTopVisible(true);
}
categories.add(lastCategory);
addCategory(lastCategory);
return lastCategory;
}
return otherCategory;
}
private void addCategory(PoiCategory category) {
List<PoiCategory> copy = new ArrayList<>(categories);
copy.add(category);
categories = copy;
}
public PoiTranslator getPoiTranslator() {
return poiTranslator;
}
@ -348,7 +363,12 @@ public class MapPoiTypes {
final Map<String, PoiType> allTypes = new LinkedHashMap<String, PoiType>();
final Map<String, List<PoiType>> categoryPoiAdditionalMap = new LinkedHashMap<String, List<PoiType>>();
final Map<AbstractPoiType, Set<String>> abstractTypeAdditionalCategories = new LinkedHashMap<AbstractPoiType, Set<String>>();
this.categories.clear();
final Map<String, PoiType> poiTypesByTag = new LinkedHashMap<String, PoiType>();
final Map<String, String> deprecatedTags = new LinkedHashMap<String, String>();
final Map<String, String> poiAdditionalCategoryIconNames = new LinkedHashMap<String, String>();
final List<PoiType> textPoiAdditionals = new ArrayList<PoiType>();
List<PoiCategory> categoriesList = new ArrayList<>();
try {
XmlPullParser parser = PlatformUtil.newXMLPullParser();
int tok;
@ -360,11 +380,13 @@ public class MapPoiTypes {
PoiType lastType = null;
Set<String> lastTypePoiAdditionalsCategories = new TreeSet<String>();
String lastPoiAdditionalCategory = null;
PoiCategory localOtherMapCategory = new PoiCategory(this, OTHER_MAP_CATEGORY, categoriesList.size());
categoriesList.add(localOtherMapCategory);
while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (tok == XmlPullParser.START_TAG) {
String name = parser.getName();
if (name.equals("poi_category")) {
lastCategory = new PoiCategory(this, parser.getAttributeValue("", "name"), categories.size());
lastCategory = new PoiCategory(this, parser.getAttributeValue("", "name"), categoriesList.size());
lastCategory.setTopVisible(Boolean.parseBoolean(parser.getAttributeValue("", "top")));
lastCategory.setNotEditableOsm("true".equals(parser.getAttributeValue("", "no_edit")));
lastCategory.setDefaultTag(parser.getAttributeValue("", "default_tag"));
@ -375,7 +397,7 @@ public class MapPoiTypes {
lastCategory.addExcludedPoiAdditionalCategories(parser.getAttributeValue("", "excluded_poi_additional_category").split(","));
lastCategoryPoiAdditionalsCategories.removeAll(lastCategory.getExcludedPoiAdditionalCategories());
}
categories.add(lastCategory);
categoriesList.add(lastCategory);
} else if (name.equals("poi_filter")) {
PoiFilter tp = new PoiFilter(this, lastCategory, parser.getAttributeValue("", "name"));
tp.setTopVisible(Boolean.parseBoolean(parser.getAttributeValue("", "top")));
@ -399,14 +421,17 @@ public class MapPoiTypes {
lastCategory.addPoiType(tp);
} else if (name.equals("poi_additional")) {
if (lastCategory == null) {
lastCategory = getOtherMapCategory();
lastCategory = localOtherMapCategory;
}
PoiType baseType = parsePoiAdditional(parser, lastCategory, lastFilter, lastType, null, null, lastPoiAdditionalCategory);
PoiType baseType = parsePoiAdditional(parser, lastCategory, lastFilter, lastType, null, null,
lastPoiAdditionalCategory, textPoiAdditionals);
if ("true".equals(parser.getAttributeValue("", "lang"))) {
for (String lng : MapRenderingTypes.langs) {
parsePoiAdditional(parser, lastCategory, lastFilter, lastType, lng, baseType, lastPoiAdditionalCategory);
parsePoiAdditional(parser, lastCategory, lastFilter, lastType, lng, baseType,
lastPoiAdditionalCategory, textPoiAdditionals);
}
parsePoiAdditional(parser, lastCategory, lastFilter, lastType, "en", baseType, lastPoiAdditionalCategory);
parsePoiAdditional(parser, lastCategory, lastFilter, lastType, "en", baseType,
lastPoiAdditionalCategory, textPoiAdditionals);
}
if (lastPoiAdditionalCategory != null) {
List<PoiType> categoryAdditionals = categoryPoiAdditionalMap.get(lastPoiAdditionalCategory);
@ -428,7 +453,7 @@ public class MapPoiTypes {
} else if (name.equals("poi_type")) {
if (lastCategory == null) {
lastCategory = getOtherMapCategory();
lastCategory = localOtherMapCategory;
}
if(!Algorithms.isEmpty(parser.getAttributeValue("", "deprecated_of"))){
String vl = parser.getAttributeValue("", "name");
@ -477,6 +502,7 @@ public class MapPoiTypes {
}
}
}
is.close();
} catch (IOException e) {
log.error("Unexpected error", e); //$NON-NLS-1$
@ -504,17 +530,25 @@ public class MapPoiTypes {
List<PoiType> poiAdditionals = categoryPoiAdditionalMap.get(category);
if (poiAdditionals != null) {
for (PoiType poiType : poiAdditionals) {
buildPoiAdditionalReference(poiType, entry.getKey());
buildPoiAdditionalReference(poiType, entry.getKey(), textPoiAdditionals);
}
}
}
}
findDefaultOtherCategory();
this.categories = categoriesList;
this.poiTypesByTag = poiTypesByTag;
this.deprecatedTags = deprecatedTags;
this.poiAdditionalCategoryIconNames = poiAdditionalCategoryIconNames;
this.textPoiAdditionals = textPoiAdditionals;
otherCategory = getPoiCategoryByName("user_defined_other");
if (otherCategory == null) {
throw new IllegalArgumentException("No poi category other");
}
init = true;
log.info("Time to init poi types " + (System.currentTimeMillis() - time)); //$NON-NLS-1$
}
private PoiType buildPoiAdditionalReference(PoiType poiAdditional, AbstractPoiType parent) {
private PoiType buildPoiAdditionalReference(PoiType poiAdditional, AbstractPoiType parent, List<PoiType> textPoiAdditionals) {
PoiCategory lastCategory = null;
PoiFilter lastFilter = null;
PoiType lastType = null;
@ -565,7 +599,8 @@ public class MapPoiTypes {
}
private PoiType parsePoiAdditional(XmlPullParser parser, PoiCategory lastCategory, PoiFilter lastFilter,
PoiType lastType, String lang, PoiType langBaseType, String poiAdditionalCategory) {
PoiType lastType, String lang, PoiType langBaseType,
String poiAdditionalCategory, List<PoiType> textPoiAdditionals) {
String oname = parser.getAttributeValue("", "name");
if (lang != null) {
oname += ":" + lang;
@ -647,13 +682,6 @@ public class MapPoiTypes {
return tp;
}
private void findDefaultOtherCategory() {
PoiCategory pc = getPoiCategoryByName("user_defined_other");
if (pc == null) {
throw new IllegalArgumentException("No poi category other");
}
otherCategory = pc;
}
public List<PoiCategory> getCategories(boolean includeMapCategory) {
ArrayList<PoiCategory> lst = new ArrayList<PoiCategory>(categories);
@ -699,7 +727,8 @@ public class MapPoiTypes {
public AbstractPoiType getAnyPoiAdditionalTypeByKey(String name) {
PoiType add = null;
for (PoiCategory pc : categories) {
for (int i = 0; i < categories.size(); i++) {
PoiCategory pc = categories.get(i);
add = getPoiAdditionalByKey(pc, name);
if (add != null) {
return add;
@ -805,7 +834,8 @@ public class MapPoiTypes {
if (!poiTypesByTag.isEmpty()) {
return;
}
for (PoiCategory poic : categories) {
for (int i = 0; i < categories.size(); i++) {
PoiCategory poic = categories.get(i);
for (PoiType p : poic.getPoiTypes()) {
initPoiType(p);
for (PoiType pts : p.getPoiAdditionals()) {
@ -918,4 +948,12 @@ public class MapPoiTypes {
return pat.isText();
}
}
public void setForbiddenTypes(Set<String> forbiddenTypes) {
this.forbiddenTypes = forbiddenTypes;
}
public boolean isTypeForbidden(String typeName) {
return forbiddenTypes.contains(typeName);
}
}

View file

@ -4,6 +4,7 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@ -153,6 +154,34 @@ public abstract class MapRenderingTypes {
return a;
}
protected MapRulType checkOrCreateTextRule(String targetTag, MapRulType ref) {
MapRulType mt = types.get(constructRuleKey(targetTag, null));
if (mt == null) {
MapRulType ct = MapRulType.createText(targetTag, ref);
mt = registerRuleType(ct);
}
return mt;
}
protected MapRulType checkOrMainRule(String tag, String value, int minzoom) {
MapRulType mt = types.get(constructRuleKey(tag, value));
if (mt == null) {
mt = registerRuleType(MapRulType.createMainEntity(tag, value));
mt.minzoom = minzoom;
mt.maxzoom = 21;
}
return mt;
}
protected MapRulType checkOrCreateAdditional(String tag, String value, MapRulType ref) {
MapRulType mt = types.get(constructRuleKey(tag, value));
if (mt == null) {
MapRulType ct = MapRulType.createAdditional(tag, value, ref);
mt = registerRuleType(ct);
}
return mt;
}
protected MapRulType getRuleType(String tag, String val, boolean poi, boolean map) {
Map<String, MapRulType> types = getEncodingRuleTypes();
tag = lc(tag);
@ -171,7 +200,6 @@ public abstract class MapRenderingTypes {
rType.map = parent.map;
rType.poi = parent.poi;
rType.onlyPoint = parent.onlyPoint;
rType.namePrefix = parent.namePrefix;
rType = registerRuleType(rType);
}
return rType;
@ -265,37 +293,112 @@ public abstract class MapRenderingTypes {
rtype.onlyPoint = Boolean.parseBoolean(parser.getAttributeValue("", "point")); //$NON-NLS-1$
rtype.relation = Boolean.parseBoolean(parser.getAttributeValue("", "relation")); //$NON-NLS-1$
rtype.relationGroup = Boolean.parseBoolean(parser.getAttributeValue("", "relationGroup")); //$NON-NLS-1$
if (rtype.isMain()) {
rtype.namePrefix = parser.getAttributeValue("", "namePrefix"); //$NON-NLS-1$
if (rtype.namePrefix == null) {
rtype.namePrefix = "";
if (rtype.isMain()) {
if (rtype.relationGroup) {
MapRulType mrt = MapRulType.createMainEntity(tag + "_" + value, null);
mrt.order = rtype.order;
mrt.category = rtype.category;
mrt.poi = rtype.poi;
mrt.map = rtype.map;
registerMapRule(parser, mrt);
}
String v = parser.getAttributeValue("", "nameTags");
if (v != null) {
String[] names = v.split(",");
rtype.names = new MapRulType[names.length * (langs.length + 1)];
int j = 0;
for (int i = 0; i < names.length; i++) {
String tagName = names[i];
if (rtype.namePrefix.length() > 0) {
tagName = rtype.namePrefix + tagName;
String groupSort = parser.getAttributeValue("", "relationGroupSort");
if (groupSort != null) {
rtype.relationSortTags = new LinkedHashMap<String, List<String>>();
String[] ls = groupSort.split(";");
for(String l : ls) {
int sp = l.indexOf('=');
String key = l;
String[] values = new String[0];
if(sp >= 0) {
key = l.substring(0, sp);
values = l.substring(sp +1).split(",");
}
MapRulType mt = MapRulType.createText(tagName);
mt = registerRuleType(mt);
rtype.names[j++] = mt;
for(String lng : langs) {
mt = MapRulType.createText(tagName + ":" + lng);
mt = registerRuleType(mt);
rtype.names[j++] = mt;
rtype.relationSortTags.put(key, Arrays.asList(values));
}
}
String additionalTags = parser.getAttributeValue("", "additionalTags");
String additionalPrefix = parser.getAttributeValue("", "additionalPrefix");
if (additionalTags != null) {
rtype.additionalTags = new LinkedHashMap<String, String>();
for(String tg : additionalTags.split(",")) {
String targetTag = tg;
if(!Algorithms.isEmpty(additionalPrefix)) {
targetTag = additionalPrefix + tg;
}
rtype.additionalTags.put(tg, targetTag);
}
}
rtype.relationGroupPrefix = parser.getAttributeValue("", "relationGroupPrefix"); //$NON-NLS-1$
String relationGroupAdditionalTags = parser.getAttributeValue("", "relationGroupAdditionalTags");
if (relationGroupAdditionalTags != null) {
rtype.relationGroupAdditionalTags = new LinkedHashMap<String, String>();
for(String tg : relationGroupAdditionalTags.split(",")) {
rtype.relationGroupAdditionalTags.put(tg, tg);
}
}
String nmts = parser.getAttributeValue("", "nameTags");
if (nmts != null) {
if (rtype.relation || rtype.relationGroup) {
String namePrefix = parser.getAttributeValue("", "namePrefix"); //$NON-NLS-1$
if (namePrefix == null) {
namePrefix = "";
}
rtype.relationNames = new LinkedHashMap<String, String>();
putNameTags(nmts, rtype.relationNames, namePrefix);
} else {
String[] nameSplit = nmts.split(",");
for (String nameTag : nameSplit) {
checkOrCreateTextRule(nameTag, null);
}
}
}
String rnmts = parser.getAttributeValue("", "relationGroupNameTags");
if (rnmts != null) {
rtype.relationGroupNameTags = new LinkedHashMap<String, String>();
putNameTags(rnmts, rtype.relationGroupNameTags, "");
}
}
return rtype;
}
private void putNameTags(String namesList, Map<String, String> names, String namePrefix) {
if (namesList != null) {
String[] nameSplit = namesList.split(",");
for (int i = 0; i < nameSplit.length; i++) {
String tagName = nameSplit[i];
String tagTargetName = tagName;
if (namePrefix.length() > 0) {
tagTargetName = namePrefix + tagName;
}
names.put(tagName, tagTargetName);
for (String lng : langs) {
names.put(tagName + ":" + lng, tagTargetName + ":" + lng);
}
}
}
}
protected void registerMapRule(XmlPullParser parser, MapRulType rtype) {
String val = parser.getAttributeValue("", "minzoom"); //$NON-NLS-1$
if (rtype.isMain()) {
rtype.minzoom = 15;
}
if (val != null) {
rtype.minzoom = Integer.parseInt(val);
}
val = parser.getAttributeValue("", "maxzoom"); //$NON-NLS-1$
rtype.maxzoom = 31;
if (val != null) {
rtype.maxzoom = Integer.parseInt(val);
}
registerRuleType(rtype);
}
protected MapRulType registerRuleType(MapRulType rt) {
String tag = rt.tagValuePattern.tag;
String val = rt.tagValuePattern.value;
@ -422,7 +525,14 @@ public abstract class MapRenderingTypes {
}
public static class MapRulType {
protected MapRulType[] names;
// relation part
protected Map<String, String> relationNames;
protected Map<String, String> additionalTags;
protected Map<String, List<String>> relationSortTags;
protected String relationGroupPrefix;
protected Map<String, String> relationGroupNameTags;
protected Map<String, String> relationGroupAdditionalTags;
protected TagValuePattern tagValuePattern;
protected boolean additional;
protected boolean additionalText;
@ -440,7 +550,6 @@ public abstract class MapRenderingTypes {
protected int minzoom;
protected int maxzoom;
protected boolean onlyPoint;
protected String namePrefix ="";
// inner id
@ -452,6 +561,16 @@ public abstract class MapRenderingTypes {
private MapRulType(){
}
private void copyMetadata(MapRulType ref) {
minzoom = ref.minzoom;
maxzoom = ref.maxzoom;
order = ref.order;
category = ref.category;
onlyPoint = ref.onlyPoint;
}
public boolean isPOI(){
return poi;
}
@ -471,24 +590,37 @@ public abstract class MapRenderingTypes {
return rt;
}
public static MapRulType createText(String tag) {
public static MapRulType createText(String tag, MapRulType ref) {
MapRulType rt = new MapRulType();
rt.additionalText = true;
rt.minzoom = 2;
rt.maxzoom = 31;
rt.tagValuePattern = new TagValuePattern(tag, null);
if (ref != null) {
rt.copyMetadata(ref);
}
rt.additionalText = true;
rt.tagValuePattern = new TagValuePattern(tag, null);
return rt;
}
public static MapRulType createAdditional(String tag, String value) {
public static MapRulType createAdditional(String tag, String value, MapRulType ref) {
MapRulType rt = new MapRulType();
rt.additional = true;
rt.minzoom = 2;
rt.maxzoom = 31;
if (ref != null) {
rt.copyMetadata(ref);
}
rt.additional = true;
rt.tagValuePattern = new TagValuePattern(tag, value);
return rt;
}
public static MapRulType createText(String tag) {
return createText(tag, null);
}
public static MapRulType createAdditional(String tag, String value) {
return createAdditional(tag, value, null);
}
public String getTag() {
return tagValuePattern.tag;
@ -549,14 +681,6 @@ public abstract class MapRenderingTypes {
return onlyPoint;
}
public boolean isRelation() {
return relation;
}
public boolean isRelationGroup() {
return relationGroup;
}
public int getFreq() {
return freq;

View file

@ -7,38 +7,62 @@ import java.util.List;
import java.util.Map;
public class PoiFilter extends AbstractPoiType {
private PoiCategory pc;
private PoiCategory pc;
private List<PoiType> poiTypes = new ArrayList<PoiType>();
private Map<String, PoiType> map = new LinkedHashMap<String, PoiType>();
public PoiFilter(MapPoiTypes registry, PoiCategory pc, String keyName){
public PoiFilter(MapPoiTypes registry, PoiCategory pc, String keyName) {
super(keyName, registry);
this.pc = pc;
}
public PoiCategory getPoiCategory() {
return pc;
}
public PoiType getPoiTypeByKeyName(String kn) {
return map.get(kn);
}
public void addExtraPoiTypes(Map<String, PoiType> poiTypesToAdd) {
List<PoiType> npoiTypes = null;
Map<String, PoiType> nmap = null;
for (PoiType poiType : poiTypesToAdd.values()) {
String keyName = poiType.getKeyName();
if (!map.containsKey(keyName) && !registry.isTypeForbidden(keyName)) {
if (npoiTypes == null) {
npoiTypes = new ArrayList<PoiType>(this.poiTypes);
nmap = new LinkedHashMap<>(map);
}
npoiTypes.add(poiType);
nmap.put(keyName, poiType);
}
}
if (npoiTypes != null) {
poiTypes = npoiTypes;
map = nmap;
}
}
public void addPoiType(PoiType type) {
if (registry.isTypeForbidden(type.keyName)) {
return;
}
if (!map.containsKey(type.getKeyName())) {
poiTypes.add(type);
map.put(type.getKeyName(), type);
} else {
PoiType prev = map.get(type.getKeyName());
if(prev.isReference()) {
if (prev.isReference()) {
poiTypes.remove(prev);
poiTypes.add(type);
map.put(type.getKeyName(), type);
}
}
}
public Map<PoiCategory, LinkedHashSet<String>> putTypes(Map<PoiCategory, LinkedHashSet<String>> acceptedTypes) {
if (!acceptedTypes.containsKey(pc)) {
acceptedTypes.put(pc, new LinkedHashSet<String>());
@ -65,9 +89,10 @@ public class PoiFilter extends AbstractPoiType {
}
}
}
public List<PoiType> getPoiTypes() {
return poiTypes;
}
}

View file

@ -26,8 +26,8 @@ public class PoiType extends AbstractPoiType {
private int order = 90;
public PoiType(MapPoiTypes poiTypes, PoiCategory category, PoiFilter filter, String name) {
super(name, poiTypes);
public PoiType(MapPoiTypes poiTypes, PoiCategory category, PoiFilter filter, String keyName) {
super(keyName, poiTypes);
this.category = category;
this.filter = filter;
}

View file

@ -301,8 +301,11 @@ public abstract class Entity implements Serializable {
String values = getTag(OSMTagKey.IS_IN);
if (values == null) {
String city = getTag(OSMTagKey.ADDR_CITY);
String place = getTag(OSMTagKey.ADDR_PLACE);
if(!Algorithms.isEmpty(city)) {
return Collections.singleton(city.trim());
} else if(!Algorithms.isEmpty(place)) {
return Collections.singleton(place.trim());
}
return Collections.emptySet();
}

View file

@ -34,9 +34,11 @@ public class OSMSettings {
// address
PLACE("place"), //$NON-NLS-1$
ADDR_HOUSE_NUMBER("addr:housenumber"), //$NON-NLS-1$
ADDR2_HOUSE_NUMBER("addr2:housenumber"), //$NON-NLS-1$
ADDR_HOUSE_NAME("addr:housename"), //$NON-NLS-1$
ADDR_STREET("addr:street"), //$NON-NLS-1$
ADDR_STREET2("addr:street2"), //$NON-NLS-1$
ADDR2_STREET("addr2:street"), //$NON-NLS-1$
ADDR_CITY("addr:city"), //$NON-NLS-1$
ADDR_PLACE("addr:place"), //$NON-NLS-1$
ADDR_POSTCODE("addr:postcode"), //$NON-NLS-1$

View file

@ -87,6 +87,11 @@ public class OsmMapUtils {
}
public static LatLon getComplexPolyCenter(Collection<Node> outer, List<List<Node>> inner) {
if (outer.size() > 3 && outer.size() <= 5 && inner == null) {
List<Node> sub = new ArrayList<>(outer);
return getWeightCenterForNodes(sub.subList(0, sub.size()-1));
}
final List<List<LatLon>> rings = new ArrayList<>();
List<LatLon> outerRing = new ArrayList<>();
@ -551,6 +556,9 @@ public class OsmMapUtils {
// take centroid as the first best guess
Cell bestCell = getCentroidCell(rings);
if(bestCell == null) {
return new LatLon(minX, minY);
}
// special case for rectangular polygons
Cell bboxCell = new Cell(minX + width / 2, minY + height / 2, 0, rings);
@ -601,10 +609,13 @@ public class OsmMapUtils {
area += f * 3;
}
if (area == 0) {
LatLon p = points.get(0);
return new Cell(p.getLatitude(), p.getLongitude(), 0, rings);
}
if (area == 0) {
if (points.size() == 0) {
return null;
}
LatLon p = points.get(0);
return new Cell(p.getLatitude(), p.getLongitude(), 0, rings);
}
return new Cell(x / area, y / area, 0, rings);
}

View file

@ -1,93 +1,104 @@
package net.osmand.osm.io;
/**
* @source http://www.javaworld.com/javaworld/javatips/jw-javatip47.html -- 24.11.2008, (mb)
*/
public class Base64 {
/*******************************************************************************************************************
* BASE 64 encoding of a String or an array of bytes. See also RFC 1421.
*
* @author Unknown
* @author David W. Croft
* @version 1998-06-08
******************************************************************************************************************/
/*******************************************************************************************************************
* BASE 64 encoding of a String or an array of bytes. See also RFC 1421.
*
* @author Unknown
* @author David W. Croft
* @version 1998-06-08
******************************************************************************************************************/
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
public static final char[] alphabet = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0 to 7
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 8 to 15
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16 to 23
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 24 to 31
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 32 to 39
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 40 to 47
'w', 'x', 'y', 'z', '0', '1', '2', '3', // 48 to 55
'4', '5', '6', '7', '8', '9', '+', '/' }; // 56 to 63
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
public static final char[] alphabet = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0 to 7
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 8 to 15
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16 to 23
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 24 to 31
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 32 to 39
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 40 to 47
'w', 'x', 'y', 'z', '0', '1', '2', '3', // 48 to 55
'4', '5', '6', '7', '8', '9', '+', '/' }; // 56 to 63
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
public static String encode(final String s)
//////////////////////////////////////////////////////////////////////
{
return encode(s.getBytes());
}
public static int indexOf(char c) {
for (int i = 0; i < alphabet.length; i++) {
if (alphabet[i] == c) {
return i;
}
}
return -1;
}
public static String encode(final byte[] octetString)
//////////////////////////////////////////////////////////////////////
{
int bits24;
int bits6;
final char[] out = new char[((octetString.length - 1) / 3 + 1) * 4];
public static String encode(final String s)
//////////////////////////////////////////////////////////////////////
{
return encode(s.getBytes());
}
int outIndex = 0;
int i = 0;
public static String encode(final byte[] octetString)
//////////////////////////////////////////////////////////////////////
{
int bits24;
int bits6;
while ((i + 3) <= octetString.length) {
// store the octets
bits24 = (octetString[i++] & 0xFF) << 16;
bits24 |= (octetString[i++] & 0xFF) << 8;
bits24 |= (octetString[i++] & 0xFF);
final char[] out = new char[((octetString.length - 1) / 3 + 1) * 4];
bits6 = (bits24 & 0x00FC0000) >> 18;
out[outIndex++] = alphabet[bits6];
bits6 = (bits24 & 0x0003F000) >> 12;
out[outIndex++] = alphabet[bits6];
bits6 = (bits24 & 0x00000FC0) >> 6;
out[outIndex++] = alphabet[bits6];
bits6 = (bits24 & 0x0000003F);
out[outIndex++] = alphabet[bits6];
}
int outIndex = 0;
int i = 0;
if (octetString.length - i == 2) {
// store the octets
bits24 = (octetString[i] & 0xFF) << 16;
bits24 |= (octetString[i + 1] & 0xFF) << 8;
while ((i + 3) <= octetString.length) {
// store the octets
bits24 = (octetString[i++] & 0xFF) << 16;
bits24 |= (octetString[i++] & 0xFF) << 8;
bits24 |= (octetString[i++] & 0xFF);
bits6 = (bits24 & 0x00FC0000) >> 18;
out[outIndex++] = alphabet[bits6];
bits6 = (bits24 & 0x0003F000) >> 12;
out[outIndex++] = alphabet[bits6];
bits6 = (bits24 & 0x00000FC0) >> 6;
out[outIndex++] = alphabet[bits6];
bits6 = (bits24 & 0x00FC0000) >> 18;
out[outIndex++] = alphabet[bits6];
bits6 = (bits24 & 0x0003F000) >> 12;
out[outIndex++] = alphabet[bits6];
bits6 = (bits24 & 0x00000FC0) >> 6;
out[outIndex++] = alphabet[bits6];
bits6 = (bits24 & 0x0000003F);
out[outIndex++] = alphabet[bits6];
}
// padding
out[outIndex++] = '=';
} else if (octetString.length - i == 1) {
// store the octets
bits24 = (octetString[i] & 0xFF) << 16;
if (octetString.length - i == 2) {
// store the octets
bits24 = (octetString[i] & 0xFF) << 16;
bits24 |= (octetString[i + 1] & 0xFF) << 8;
bits6 = (bits24 & 0x00FC0000) >> 18;
out[outIndex++] = alphabet[bits6];
bits6 = (bits24 & 0x0003F000) >> 12;
out[outIndex++] = alphabet[bits6];
bits6 = (bits24 & 0x00FC0000) >> 18;
out[outIndex++] = alphabet[bits6];
bits6 = (bits24 & 0x0003F000) >> 12;
out[outIndex++] = alphabet[bits6];
bits6 = (bits24 & 0x00000FC0) >> 6;
out[outIndex++] = alphabet[bits6];
// padding
out[outIndex++] = '=';
out[outIndex++] = '=';
}
// padding
out[outIndex++] = '=';
} else if (octetString.length - i == 1) {
// store the octets
bits24 = (octetString[i] & 0xFF) << 16;
return new String(out);
}
bits6 = (bits24 & 0x00FC0000) >> 18;
out[outIndex++] = alphabet[bits6];
bits6 = (bits24 & 0x0003F000) >> 12;
out[outIndex++] = alphabet[bits6];
// padding
out[outIndex++] = '=';
out[outIndex++] = '=';
}
return new String(out);
}
}

View file

@ -8,6 +8,9 @@ import java.util.Map;
public class RenderingRuleStorageProperties {
public static final String UI_CATEGORY_HIDDEN = "ui_hidden";
public static final String UI_CATEGORY_DETAILS = "details";
public static final String UI_CATEGORY_HIDE = "hide";
public static final String UI_CATEGORY_ROUTES = "routes";
public static final String A_ENGINE_V1 = "engine_v1";
public static final String A_APP_MODE= "appMode";
public static final String A_BASE_APP_MODE = "baseAppMode";

View file

@ -7,7 +7,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@ -18,6 +17,7 @@ import java.util.Map.Entry;
import java.util.Stack;
import net.osmand.PlatformUtil;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log;
import org.xmlpull.v1.XmlPullParser;
@ -26,7 +26,7 @@ import org.xmlpull.v1.XmlPullParserException;
public class RenderingRulesStorage {
private final static Log log = PlatformUtil.getLog(RenderingRulesStorage.class);
static boolean STORE_ATTTRIBUTES = false;
static boolean STORE_ATTRIBUTES = false;
// keep sync !
// keep sync ! not change values
@ -40,6 +40,10 @@ public class RenderingRulesStorage {
private final static int SHIFT_TAG_VAL = 16;
private final static String SEQ_ATTR_KEY = "seq";
private final static String SEQ_PLACEHOLDER = "#SEQ";
// C++
List<String> dictionary = new ArrayList<String>();
Map<String, Integer> dictionaryMap = new LinkedHashMap<String, Integer>();
@ -183,31 +187,86 @@ public class RenderingRulesStorage {
}
}
private class XmlTreeSequence {
XmlTreeSequence parent;
String seqOrder;
Map<String, String> attrsMap = new LinkedHashMap<String, String>();
String name;
List<XmlTreeSequence> children = new ArrayList<RenderingRulesStorage.XmlTreeSequence>();
private void process(RenderingRulesHandler handler, int el) throws XmlPullParserException, IOException {
Map<String, String> seqAttrsMap = new HashMap<String, String>(attrsMap);
if (attrsMap.containsKey(SEQ_ATTR_KEY)) {
attrsMap.remove(SEQ_ATTR_KEY);
}
for (Entry<String, String> attr: attrsMap.entrySet()) {
if (attr.getValue().contains(SEQ_PLACEHOLDER)) {
seqAttrsMap.put(attr.getKey(), attr.getValue().replace(SEQ_PLACEHOLDER, el+""));
} else {
seqAttrsMap.put(attr.getKey(), attr.getValue());
}
}
handler.startElement(seqAttrsMap, name);
for(XmlTreeSequence s : children) {
s.process(handler, el);
}
handler.endElement(name);
}
}
private class RenderingRulesHandler {
private final XmlPullParser parser;
private int state;
Stack<RenderingRule> stack = new Stack<RenderingRule>();
Map<String, String> attrsMap = new LinkedHashMap<String, String>();
private final RenderingRulesStorageResolver resolver;
private RenderingRulesStorage dependsStorage;
public RenderingRulesHandler(XmlPullParser parser, RenderingRulesStorageResolver resolver){
this.parser = parser;
this.resolver = resolver;
}
public void parse(InputStream is) throws XmlPullParserException, IOException {
XmlPullParser parser = this.parser;
Map<String, String> attrsMap = new LinkedHashMap<String, String>();
parser.setInput(is, "UTF-8");
int tok;
XmlTreeSequence currentSeqElement = null;
while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (tok == XmlPullParser.START_TAG) {
startElement(parser.getName());
attrsMap.clear();
parseAttributes(parser, attrsMap);
String name = parser.getName();
if (!Algorithms.isEmpty(parser.getAttributeValue("", SEQ_ATTR_KEY)) || currentSeqElement != null) {
XmlTreeSequence seq = new XmlTreeSequence();
seq.name = name;
seq.attrsMap = new HashMap<String, String>(attrsMap);
seq.parent = currentSeqElement;
if (currentSeqElement == null) {
seq.seqOrder = parser.getAttributeValue("", SEQ_ATTR_KEY);
} else {
currentSeqElement.children.add(seq);
seq.seqOrder = currentSeqElement.seqOrder;
}
currentSeqElement = seq;
} else {
startElement(attrsMap, name);
}
} else if (tok == XmlPullParser.END_TAG) {
endElement(parser.getName());
if(currentSeqElement == null) {
endElement(parser.getName());
} else {
XmlTreeSequence process = currentSeqElement;
currentSeqElement = currentSeqElement.parent;
if (currentSeqElement == null) {
// Here we process sequence element
int seqEnd = Integer.parseInt(process.seqOrder.substring(process.seqOrder.indexOf(':') + 1, process.seqOrder.length()));
for(int i = 1; i < seqEnd; i++) {
process.process(this, i);
}
}
}
}
}
@ -226,16 +285,14 @@ public class RenderingRulesStorage {
return true;
}
public void startElement(String name) throws XmlPullParserException, IOException {
public void startElement(Map<String, String> attrsMap, String name) throws XmlPullParserException, IOException {
boolean stateChanged = false;
final boolean isCase = isCase(name);
final boolean isSwitch = isSwitch(name);
if(isCase || isSwitch){ //$NON-NLS-1$
attrsMap.clear();
boolean top = stack.size() == 0 || isTopCase();
parseAttributes(attrsMap);
RenderingRule renderingRule = new RenderingRule(attrsMap, isSwitch, RenderingRulesStorage.this);
if(top || STORE_ATTTRIBUTES){
if(top || STORE_ATTRIBUTES){
renderingRule.storeAttributes(attrsMap);
}
if (stack.size() > 0 && stack.peek() instanceof RenderingRule) {
@ -244,10 +301,8 @@ public class RenderingRulesStorage {
}
stack.push(renderingRule);
} else if(isApply(name)){ //$NON-NLS-1$
attrsMap.clear();
parseAttributes(attrsMap);
RenderingRule renderingRule = new RenderingRule(attrsMap, false, RenderingRulesStorage.this);
if(STORE_ATTTRIBUTES) {
if(STORE_ATTRIBUTES) {
renderingRule.storeAttributes(attrsMap);
}
if (stack.size() > 0 && stack.peek() instanceof RenderingRule) {
@ -272,14 +327,14 @@ public class RenderingRulesStorage {
state = POLYGON_RULES;
stateChanged = true;
} else if("renderingAttribute".equals(name)){ //$NON-NLS-1$
String attr = parser.getAttributeValue("", "name");
String attr = attrsMap.get("name");
RenderingRule root = new RenderingRule(new HashMap<String, String>(), false, RenderingRulesStorage.this);
renderingAttributes.put(attr, root);
stack.push(root);
} else if("renderingProperty".equals(name)){ //$NON-NLS-1$
String attr = parser.getAttributeValue("", "attr");
String attr = attrsMap.get("attr");
RenderingRuleProperty prop;
String type = parser.getAttributeValue("", "type");
String type = attrsMap.get("type");
if("boolean".equalsIgnoreCase(type)){
prop = RenderingRuleProperty.createInputBooleanProperty(attr);
} else if("string".equalsIgnoreCase(type)){
@ -287,20 +342,20 @@ public class RenderingRulesStorage {
} else {
prop = RenderingRuleProperty.createInputIntProperty(attr);
}
prop.setDescription(parser.getAttributeValue("", "description"));
prop.setDefaultValueDescription(parser.getAttributeValue("", "defaultValueDescription"));
prop.setCategory(parser.getAttributeValue("", "category"));
prop.setName(parser.getAttributeValue("", "name"));
if(parser.getAttributeValue("", "possibleValues") != null){
prop.setPossibleValues(parser.getAttributeValue("", "possibleValues").split(","));
prop.setDescription(attrsMap.get("description"));
prop.setDefaultValueDescription(attrsMap.get("defaultValueDescription"));
prop.setCategory(attrsMap.get("category"));
prop.setName(attrsMap.get("name"));
if (attrsMap.get("possibleValues") != null) {
prop.setPossibleValues(attrsMap.get("possibleValues").split(","));
}
PROPS.registerRule(prop);
} else if("renderingConstant".equals(name)){ //$NON-NLS-1$
if(!renderingConstants.containsKey(parser.getAttributeValue("", "name"))){
renderingConstants.put(parser.getAttributeValue("", "name"), parser.getAttributeValue("", "value"));
if(!renderingConstants.containsKey(attrsMap.get("name"))){
renderingConstants.put(attrsMap.get("name"), attrsMap.get("value"));
}
} else if("renderingStyle".equals(name)){ //$NON-NLS-1$
String depends = parser.getAttributeValue("", "depends");
String depends = attrsMap.get("depends");
if(depends != null && depends.length()> 0){
this.dependsStorage = resolver.resolve(depends, resolver);
}
@ -311,7 +366,7 @@ public class RenderingRulesStorage {
PROPS = new RenderingRuleStorageProperties(dependsStorage.PROPS);
}
internalRenderingName = parser.getAttributeValue("", "name");
internalRenderingName = attrsMap.get("name");
} else if("renderer".equals(name)){ //$NON-NLS-1$
throw new XmlPullParserException("Rendering style is deprecated and no longer supported.");
@ -337,7 +392,7 @@ public class RenderingRulesStorage {
return "group".equals(name) || "switch".equals(name);
}
private Map<String, String> parseAttributes(Map<String, String> m) {
private Map<String, String> parseAttributes(XmlPullParser parser, Map<String, String> m) {
for (int i = 0; i < parser.getAttributeCount(); i++) {
String name = parser.getAttributeName(i);
String vl = parser.getAttributeValue(i);
@ -396,7 +451,7 @@ public class RenderingRulesStorage {
vl = ns.remove("value");
// reset rendering rule attributes
renderingRule.init(ns);
if(STORE_ATTTRIBUTES) {
if(STORE_ATTRIBUTES) {
renderingRule.storeAttributes(ns);
}
@ -469,7 +524,7 @@ public class RenderingRulesStorage {
public static void main(String[] args) throws XmlPullParserException, IOException {
STORE_ATTTRIBUTES = true;
STORE_ATTRIBUTES = true;
// InputStream is = RenderingRulesStorage.class.getResourceAsStream("default.render.xml");
final String loc = "/Users/victorshcherb/osmand/repos/resources/rendering_styles/";
String defaultFile = loc + "UniRS.render.xml";

View file

@ -21,7 +21,7 @@ import org.xmlpull.v1.XmlPullParserException;
public class RenderingRulesStoragePrinter {
public static void main(String[] args) throws XmlPullParserException, IOException {
RenderingRulesStorage.STORE_ATTTRIBUTES = true;
RenderingRulesStorage.STORE_ATTRIBUTES = true;
// InputStream is = RenderingRulesStorage.class.getResourceAsStream("default.render.xml");
String defaultFile = "/Users/victorshcherb/osmand/repos/resources/rendering_styles/default.render.xml";
if(args.length > 0) {

View file

@ -11,6 +11,7 @@ import java.util.PriorityQueue;
import net.osmand.PlatformUtil;
import net.osmand.binary.RouteDataObject;
import net.osmand.data.LatLon;
import net.osmand.osm.MapRenderingTypes;
import net.osmand.util.MapUtils;
@ -64,13 +65,10 @@ public class BinaryRoutePlanner {
* Calculate route between start.segmentEnd and end.segmentStart (using A* algorithm)
* return list of segments
*/
@SuppressWarnings("unused")
FinalRouteSegment searchRouteInternal(final RoutingContext ctx, RouteSegmentPoint start, RouteSegmentPoint end,
RouteSegment recalculationEnd ) throws InterruptedException, IOException {
// measure time
ctx.timeToLoad = 0;
ctx.memoryOverhead = 1000;
ctx.visitedSegments = 0;
// Initializing priority queue to visit way segments
Comparator<RouteSegment> nonHeuristicSegmentsComparator = new NonHeuristicSegmentsComparator();
@ -121,7 +119,9 @@ public class BinaryRoutePlanner {
if (ctx.memoryOverhead > ctx.config.memoryLimitation * 0.95) {
throw new IllegalStateException("There is not enough memory " + ctx.config.memoryLimitation / (1 << 20) + " Mb");
}
ctx.visitedSegments ++;
if (ctx.calculationProgress != null) {
ctx.calculationProgress.visitedSegments++;
}
if (forwardSearch) {
boolean doNotAddIntersections = onlyBackward;
processRouteSegment(ctx, false, graphDirectSegments, visitedDirectSegments,
@ -138,7 +138,7 @@ public class BinaryRoutePlanner {
checkIfGraphIsEmpty(ctx, ctx.getPlanRoadDirection() >= 0, graphDirectSegments, start, visitedDirectSegments,
"Route is not found from selected start point.");
if (ctx.planRouteIn2Directions()) {
forwardSearch = (nonHeuristicSegmentsComparator.compare(graphDirectSegments.peek(), graphReverseSegments.peek()) < 0);
forwardSearch = nonHeuristicSegmentsComparator.compare(graphDirectSegments.peek(), graphReverseSegments.peek()) <= 0;
// if (graphDirectSegments.size() * 2 > graphReverseSegments.size()) {
// forwardSearch = false;
// } else if (graphDirectSegments.size() < 2 * graphReverseSegments.size()) {
@ -165,8 +165,14 @@ public class BinaryRoutePlanner {
throw new InterruptedException("Route calculation interrupted");
}
}
ctx.visitedSegments = visitedDirectSegments.size() + visitedOppositeSegments.size();
printDebugMemoryInformation(ctx, graphDirectSegments, graphReverseSegments, visitedDirectSegments, visitedOppositeSegments);
if (ctx.calculationProgress != null) {
ctx.calculationProgress.visitedDirectSegments += visitedDirectSegments.size();
ctx.calculationProgress.visitedOppositeSegments += visitedOppositeSegments.size();
ctx.calculationProgress.directQueueSize += graphDirectSegments.size(); // Math.max(ctx.directQueueSize,
// graphDirectSegments.size());
ctx.calculationProgress.oppositeQueueSize += graphReverseSegments.size();
ctx.calculationProgress.visitedOppositeSegments += visitedOppositeSegments.size();
}
return finalSegment;
}
@ -367,23 +373,21 @@ public class BinaryRoutePlanner {
log.warn(logMsg);
}
public void printDebugMemoryInformation(RoutingContext ctx, PriorityQueue<RouteSegment> graphDirectSegments, PriorityQueue<RouteSegment> graphReverseSegments,
TLongObjectHashMap<RouteSegment> visitedDirectSegments,TLongObjectHashMap<RouteSegment> visitedOppositeSegments) {
printInfo(String.format("Time. Total: %.2f, to load: %.2f, to load headers: %.2f, to calc dev: %.2f, to calc rules: %.2f ",
(System.nanoTime() - ctx.timeToCalculate) / 1e6, ctx.timeToLoad / 1e6,
ctx.timeToLoadHeaders / 1e6, ctx.timeNanoToCalcDeviation / 1e6, GeneralRouter.TIMER / 1e6));
GeneralRouter.TIMER = 0;
int maxLoadedTiles = Math.max(ctx.maxLoadedTiles, ctx.getCurrentlyLoadedTiles());
printInfo("Current loaded tiles : " + ctx.getCurrentlyLoadedTiles() + ", maximum loaded tiles " + maxLoadedTiles);
printInfo("Loaded tiles " + ctx.loadedTiles + " (distinct " + ctx.distinctLoadedTiles + "), unloaded tiles " + ctx.unloadedTiles +
", loaded more than once same tiles "
+ ctx.loadedPrevUnloadedTiles);
printInfo("Visited segments " + ctx.visitedSegments + ", relaxed roads " + ctx.relaxedSegments);
if (graphDirectSegments != null && graphReverseSegments != null) {
printInfo("Priority queues sizes : " + graphDirectSegments.size() + "/" + graphReverseSegments.size());
}
if (visitedDirectSegments != null && visitedOppositeSegments != null) {
printInfo("Visited interval sizes: " + visitedDirectSegments.size() + "/" + visitedOppositeSegments.size());
public static void printDebugMemoryInformation(RoutingContext ctx) {
if (ctx.calculationProgress != null) {
RouteCalculationProgress p = ctx.calculationProgress;
printInfo(String.format("Time. Total: %.2f, to load: %.2f, to load headers: %.2f, to find start/end: %.2f, extra: %.2f ",
p.timeToCalculate / 1e6, p.timeToLoad / 1e6, p.timeToLoadHeaders / 1e6,
p.timeToFindInitialSegments / 1e6, p.timeNanoToCalcDeviation / 1e6));
// GeneralRouter.TIMER = 0;
int maxLoadedTiles = Math.max(p.maxLoadedTiles, ctx.getCurrentlyLoadedTiles());
printInfo("Current loaded tiles : " + ctx.getCurrentlyLoadedTiles() + ", maximum loaded tiles "
+ maxLoadedTiles);
printInfo("Loaded tiles " + p.loadedTiles + " (distinct " + p.distinctLoadedTiles + "), unloaded tiles "
+ p.unloadedTiles + ", loaded more than once same tiles " + p.loadedPrevUnloadedTiles);
printInfo("Visited segments: " + ctx.getVisitedSegments() + ", relaxed roads " + p.relaxedSegments);
printInfo("Priority queues sizes : " + p.directQueueSize + "/" + p.oppositeQueueSize);
printInfo("Visited interval sizes: " + p.visitedDirectSegments + "/" + p.visitedOppositeSegments);
}
}
@ -441,7 +445,8 @@ public class BinaryRoutePlanner {
segmentDist += squareRootDist(x, y, prevx, prevy);
// 2.1 calculate possible obstacle plus time
double obstacle = ctx.getRouter().defineRoutingObstacle(road, segmentPoint);
double obstacle = ctx.getRouter().defineRoutingObstacle(road, segmentPoint, (dir && !reverseWaySearch));
if (obstacle < 0) {
directionAllowed = false;
continue;
@ -798,41 +803,40 @@ public class BinaryRoutePlanner {
" distToEnd=" + distanceToEnd +
" segmentPoint=" + segmentPoint + " -- ", next, true);
}
if (!visitedSegments.containsKey(calculateRoutePointId(next, next.isPositive()))) {
if (next.getParentRoute() == null
|| ctx.roadPriorityComparator(next.distanceFromStart, next.distanceToEnd,
distFromStart, distanceToEnd) > 0) {
next.distanceFromStart = distFromStart;
next.distanceToEnd = distanceToEnd;
if (TRACE_ROUTING) {
printRoad(" "+segmentPoint+">>" , next, null);
}
// put additional information to recover whole route after
next.setParentRoute(segment);
next.setParentSegmentEnd(segmentPoint);
graphSegments.add(next);
}
} else {
RouteSegment visIt = visitedSegments.get(calculateRoutePointId(next, next.isPositive()));
boolean toAdd = true;
if (visIt != null) {
// the segment was already visited! We need to follow better route if it exists
// that is very exceptional situation and almost exception, it can happen
// that is very exceptional situation and almost exception, it can happen
// 1. when we underestimate distnceToEnd - wrong h()
// 2. because we process not small segments but the whole road, it could be that
// 2. because we process not small segments but the whole road, it could be that
// deviation from the road is faster than following the whole road itself!
if (distFromStart < next.distanceFromStart) {
if (TRACE_ROUTING) {
printRoad(">?", visitedSegments.get(calculateRoutePointId(next, next.isPositive())),
next.isPositive());
}
if (distFromStart < visIt.distanceFromStart && next.getParentRoute() == null) {
toAdd = true;
if (ctx.config.heuristicCoefficient <= 1) {
System.err.println("! Alert distance from start " + distFromStart + " < "
+ next.distanceFromStart + " id=" + next.road.id);
}
// A: we can't change parent route just here, because we need to update visitedSegments
// presumably we can do visitedSegments.put(calculateRoutePointId(next), next);
// next.distanceFromStart = distFromStart;
// next.setParentRoute(segment);
// next.setParentSegmentEnd(segmentPoint);
if (ctx.visitor != null) {
// ctx.visitor.visitSegment(next, false);
+ visIt.distanceFromStart + " id=" + next.road.id);
}
} else {
toAdd = false;
}
}
if (toAdd && (next.getParentRoute() == null || ctx.roadPriorityComparator(next.distanceFromStart,
next.distanceToEnd, distFromStart, distanceToEnd) > 0)) {
next.distanceFromStart = distFromStart;
next.distanceToEnd = distanceToEnd;
if (TRACE_ROUTING) {
printRoad(" " + segmentPoint + ">>", next, null);
}
// put additional information to recover whole route after
next.setParentRoute(segment);
next.setParentSegmentEnd(segmentPoint);
graphSegments.add(next);
}
}
}
@ -851,15 +855,36 @@ public class BinaryRoutePlanner {
}
public static class RouteSegmentPoint extends RouteSegment {
public RouteSegmentPoint(RouteDataObject road, int segmentStart, double distSquare) {
super(road, segmentStart);
this.distSquare = distSquare;
this.preciseX = road.getPoint31XTile(segmentStart);
this.preciseY = road.getPoint31YTile(segmentStart);
}
public RouteSegmentPoint(RouteSegmentPoint pnt) {
super(pnt.road, pnt.segStart);
this.distSquare = pnt.distSquare;
this.preciseX = pnt.preciseX;
this.preciseY = pnt.preciseY;
}
public double distSquare;
public int preciseX;
public int preciseY;
public List<RouteSegmentPoint> others;
public LatLon getPreciseLatLon() {
return new LatLon(MapUtils.get31LatitudeY(preciseY), MapUtils.get31LongitudeX(preciseX));
}
@Override
public String toString() {
return String.format("%d (%s): %s", segStart, getPreciseLatLon(), road);
}
}
public static class RouteSegment {

View file

@ -20,6 +20,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.set.hash.TLongHashSet;
public class GeneralRouter implements VehicleRouter {
@ -40,13 +41,14 @@ public class GeneralRouter implements VehicleRouter {
public static final String VEHICLE_HEIGHT = "height";
public static final String VEHICLE_WEIGHT = "weight";
public static final String VEHICLE_WIDTH = "width";
public static final String VEHICLE_LENGTH = "length";
private static boolean USE_CACHE = true;
public static long TIMER = 0;
private final RouteAttributeContext[] objectAttributes;
public final Map<String, String> attributes;
private final Map<String, RoutingParameter> parameters;
private final Map<String, RoutingParameter> parameters;
private final Map<String, Integer> universalRules;
private final List<String> universalRulesById;
private final Map<String, BitSet> tagRuleMask;
@ -70,6 +72,8 @@ public class GeneralRouter implements VehicleRouter {
private float defaultSpeed = 1f;
// speed in m/s
private float maxSpeed = 10f;
// speed in m/s (used for shortest route)
private float maxVehicleSpeed;
private TLongHashSet impassableRoads;
private GeneralRouterProfile profile;
@ -147,6 +151,7 @@ public class GeneralRouter implements VehicleRouter {
if (params.containsKey(MAX_SPEED)) {
maxSpeed = parseSilentFloat(params.get(MAX_SPEED), maxSpeed);
}
maxVehicleSpeed = maxSpeed;
if (shortestRoute) {
maxSpeed = Math.min(CAR_SHORTEST_DEFAULT_SPEED, maxSpeed);
}
@ -353,33 +358,71 @@ public class GeneralRouter implements VehicleRouter {
}
@Override
public float defineObstacle(RouteDataObject road, int point) {
public float defineObstacle(RouteDataObject road, int point, boolean dir) {
int[] pointTypes = road.getPointTypes(point);
if(pointTypes != null) {
Float obst = getCache(RouteDataObjectAttribute.OBSTACLES, road.region, pointTypes);
Float obst = getCache(RouteDataObjectAttribute.OBSTACLES, road.region, pointTypes, dir);
if(obst == null) {
obst = getObjContext(RouteDataObjectAttribute.OBSTACLES).evaluateFloat(road.region, pointTypes, 0);
putCache(RouteDataObjectAttribute.OBSTACLES, road.region, pointTypes, obst);
int[] filteredPointTypes = filterDirectionTags(road, pointTypes, dir);
obst = getObjContext(RouteDataObjectAttribute.OBSTACLES).evaluateFloat(road.region, filteredPointTypes, 0);
putCache(RouteDataObjectAttribute.OBSTACLES, road.region, pointTypes, obst, dir);
}
return obst;
}
return 0;
}
TIntArrayList filteredRules = new TIntArrayList();
@Override
public float defineRoutingObstacle(RouteDataObject road, int point) {
public float defineRoutingObstacle(RouteDataObject road, int point, boolean dir) {
int[] pointTypes = road.getPointTypes(point);
if(pointTypes != null){
Float obst = getCache(RouteDataObjectAttribute.ROUTING_OBSTACLES, road.region, pointTypes);
if(obst == null) {
obst = getObjContext(RouteDataObjectAttribute.ROUTING_OBSTACLES).evaluateFloat(road.region, pointTypes, 0);
putCache(RouteDataObjectAttribute.ROUTING_OBSTACLES, road.region, pointTypes, obst);
if(pointTypes != null) {
Float obst = getCache(RouteDataObjectAttribute.ROUTING_OBSTACLES, road.region, pointTypes, dir);
if(obst == null) {
int[] filteredPointTypes = filterDirectionTags(road, pointTypes, dir);
obst = getObjContext(RouteDataObjectAttribute.ROUTING_OBSTACLES).evaluateFloat(road.region, filteredPointTypes, 0);
putCache(RouteDataObjectAttribute.ROUTING_OBSTACLES, road.region, pointTypes, obst, dir);
}
return obst;
}
return 0;
}
private int[] filterDirectionTags(RouteDataObject road, int[] pointTypes, boolean dir) {
int wayOppositeDirection = dir ? -1 : 1;
int direction = 0;
int tdirection = 0;
for (int i = 0; i < pointTypes.length; i++) {
if (pointTypes[i] == road.region.directionBackward) {
direction = -1;
} else if(pointTypes[i] == road.region.directionForward) {
direction = 1;
} else if (pointTypes[i] == road.region.directionTrafficSignalsBackward) {
tdirection = -1;
} else if(pointTypes[i] == road.region.directionTrafficSignalsForward) {
tdirection = 1;
}
}
if (direction != 0 || tdirection != 0) {
TIntArrayList filteredRules = new TIntArrayList();
for (int i = 0; i < pointTypes.length; i++) {
boolean skip = false;
if ((pointTypes[i] == road.region.stopSign || pointTypes[i] == road.region.giveWaySign)
&& direction == wayOppositeDirection) {
skip = true;
} else if (pointTypes[i] == road.region.trafficSignals && tdirection == wayOppositeDirection) {
skip = true;
}
if (!skip) {
filteredRules.add(pointTypes[i]);
}
}
return filteredRules.toArray();
}
return pointTypes;
}
@Override
public double defineHeightObstacle(RouteDataObject road, short startIndex, short endIndex) {
if(!heightObstacles) {
@ -449,10 +492,15 @@ public class GeneralRouter implements VehicleRouter {
@Override
public float defineVehicleSpeed(RouteDataObject road) {
// don't use cache cause max/min is different for routing speed
if (maxVehicleSpeed != maxSpeed) {
float spd = getObjContext(RouteDataObjectAttribute.ROAD_SPEED).evaluateFloat(road, defaultSpeed);
return Math.max(Math.min(spd, maxVehicleSpeed), minSpeed);
}
Float sp = getCache(RouteDataObjectAttribute.ROAD_SPEED, road);
if (sp == null) {
float spd = getObjContext(RouteDataObjectAttribute.ROAD_SPEED).evaluateFloat(road, defaultSpeed);
sp = Math.max(Math.min(spd, maxSpeed), minSpeed);
sp = Math.max(Math.min(spd, maxVehicleSpeed), minSpeed);
putCache(RouteDataObjectAttribute.ROAD_SPEED, road, sp);
}
return sp;
@ -463,16 +511,21 @@ public class GeneralRouter implements VehicleRouter {
Float sp = getCache(RouteDataObjectAttribute.ROAD_PRIORITIES, road);
if(sp == null) {
sp = getObjContext(RouteDataObjectAttribute.ROAD_PRIORITIES).evaluateFloat(road, 1f);
putCache(RouteDataObjectAttribute.ROAD_PRIORITIES, road, sp);
putCache(RouteDataObjectAttribute.ROAD_PRIORITIES, road, sp, false);
}
return sp;
}
private void putCache(RouteDataObjectAttribute attr, RouteDataObject road, Float val) {
putCache(attr, road.region, road.types, val);
putCache(attr, road.region, road.types, val, false);
}
private void putCache(RouteDataObjectAttribute attr, RouteRegion reg, int[] types, Float val) {
private void putCache(RouteDataObjectAttribute attr, RouteDataObject road, Float val, boolean extra) {
putCache(attr, road.region, road.types, val, extra);
}
private void putCache(RouteDataObjectAttribute attr, RouteRegion reg, int[] types, Float val, boolean extra) {
// TIMER -= System.nanoTime();
Map<RouteRegion, Map<IntHolder, Float>> ch = evalCache[attr.ordinal()];
if (USE_CACHE) {
Map<IntHolder, Float> rM = ch.get(reg);
@ -480,40 +533,56 @@ public class GeneralRouter implements VehicleRouter {
rM = new HashMap<IntHolder, Float>();
ch.put(reg, rM);
}
rM.put(new IntHolder(types), val);
rM.put(new IntHolder(types, extra), val);
}
TIMER += System.nanoTime();
// TIMER += System.nanoTime();
}
class IntHolder {
private final int[] array;
IntHolder(int[] ts) { array = ts; }
@Override public int hashCode() { return Arrays.hashCode(array); }
@Override public boolean equals(Object other) {
if (array == other) { return true; }
if (! (other instanceof IntHolder) ) {
return false;
}
//noinspection unchecked
return Arrays.equals(array, ((IntHolder) other).array);
}
private final int[] array;
private final boolean extra;
IntHolder(int[] ts, boolean extra) {
array = ts;
this.extra = extra;
}
@Override
public int hashCode() {
return Arrays.hashCode(array) + (extra ? 1 : 0);
}
@Override
public boolean equals(Object other) {
if (array == other) {
return true;
}
if (!(other instanceof IntHolder)) {
return false;
}
if (((IntHolder) other).extra != this.extra) {
return false;
}
// noinspection unchecked
return Arrays.equals(array, ((IntHolder) other).array);
}
}
private Float getCache(RouteDataObjectAttribute attr, RouteDataObject road) {
return getCache(attr, road.region, road.types);
return getCache(attr, road.region, road.types, false);
}
private Float getCache(RouteDataObjectAttribute attr, RouteRegion reg, int[] types) {
private Float getCache(RouteDataObjectAttribute attr, RouteRegion reg, int[] types, boolean extra) {
Map<RouteRegion, Map<IntHolder, Float>> ch = evalCache[attr.ordinal()];
TIMER -= System.nanoTime();
// TIMER -= System.nanoTime();
if (USE_CACHE) {
Map<IntHolder, Float> rM = ch.get(reg);
if (rM == null) {
return null;
}
Float vl = rM.get(new IntHolder(types));
Float vl = rM.get(new IntHolder(types, extra));
if(vl != null) {
TIMER += System.nanoTime();
// TIMER += System.nanoTime();
return vl;
}
}

View file

@ -12,12 +12,29 @@ public class RouteCalculationProgress {
public float totalEstimatedDistance = 0;
public float routingCalculatedTime = 0;
public int loadedTiles = 0;
public int relaxedSegments = 0;
public int visitedSegments = 0;
public int visitedDirectSegments = 0;
public int visitedOppositeSegments = 0;
public int directQueueSize = 0;
public int oppositeQueueSize = 0;
public int totalIterations = 1;
public int iteration = -1;
public long timeNanoToCalcDeviation = 0;
public long timeToLoad = 0;
public long timeToLoadHeaders = 0;
public long timeToFindInitialSegments = 0;
public long timeToCalculate = 0;
public int distinctLoadedTiles = 0;
public int maxLoadedTiles = 0;
public int loadedPrevUnloadedTiles = 0;
public int unloadedTiles = 0;
public int loadedTiles = 0;
public boolean isCancelled;
public boolean requestPrivateAccessRouting;
@ -32,14 +49,16 @@ public class RouteCalculationProgress {
pr = Math.min(p * p / (all * all), 1);
}
float progress = INITIAL_PROGRESS;
if (totalIterations > 1) {
if (totalIterations <= 1) {
progress = INITIAL_PROGRESS + pr * (1 - INITIAL_PROGRESS);
} else if (totalIterations <= 2) {
if (iteration < 1) {
progress = pr * FIRST_ITERATION + INITIAL_PROGRESS;
} else {
progress = (INITIAL_PROGRESS + FIRST_ITERATION) + pr * (1 - FIRST_ITERATION - INITIAL_PROGRESS);
}
} else {
progress = INITIAL_PROGRESS + pr * (1 - INITIAL_PROGRESS);
progress = (float) ((iteration + Math.min(pr, 0.7)) / totalIterations);
}
return Math.min(progress * 100f, 99);
}

View file

@ -1,7 +1,8 @@
package net.osmand.router;
import net.osmand.GPXUtilities.GPXExtensionsWriter;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.RouteSegment;
import net.osmand.GPXUtilities.RouteType;
import net.osmand.GPXUtilities.Track;
import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.GPXUtilities.WptPt;
@ -9,12 +10,8 @@ import net.osmand.Location;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule;
import net.osmand.binary.RouteDataBundle;
import net.osmand.binary.StringBundle;
import net.osmand.binary.StringBundleWriter;
import net.osmand.binary.StringBundleXmlWriter;
import net.osmand.util.Algorithms;
import org.xmlpull.v1.XmlSerializer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -37,8 +34,7 @@ public class RouteExporter {
public GPXFile exportRoute() {
RouteDataResources resources = new RouteDataResources(locations);
final RouteDataBundle bundle = new RouteDataBundle(resources);
List<StringBundle> routeItems = new ArrayList<>();
if (!Algorithms.isEmpty(route)) {
for (RouteSegmentResult sr : route) {
sr.collectTypes(resources);
@ -47,15 +43,12 @@ public class RouteExporter {
sr.collectNames(resources);
}
List<StringBundle> routeItems = new ArrayList<>();
for (RouteSegmentResult sr : route) {
RouteDataBundle itemBundle = new RouteDataBundle(resources);
sr.writeToBundle(itemBundle);
routeItems.add(itemBundle);
}
bundle.putBundleList("route", "segment", routeItems);
}
List<StringBundle> typeList = new ArrayList<>();
Map<RouteTypeRule, Integer> rules = resources.getRules();
for (RouteTypeRule rule : rules.keySet()) {
@ -63,7 +56,6 @@ public class RouteExporter {
rule.writeToBundle(typeBundle);
typeList.add(typeBundle);
}
bundle.putBundleList("types", "type", typeList);
GPXFile gpx = new GPXFile(OSMAND_ROUTER_V2);
Track track = new Track();
@ -75,7 +67,6 @@ public class RouteExporter {
if (locations == null || locations.isEmpty()) {
return gpx;
}
for (int i = 0; i < locations.size(); i++) {
Location loc = locations.get(i);
WptPt pt = new WptPt();
@ -92,21 +83,22 @@ public class RouteExporter {
}
trkSegment.points.add(pt);
}
if (points != null) {
for (WptPt pt : points) {
gpx.addPoint(pt);
}
}
GPXExtensionsWriter extensionsWriter = new GPXExtensionsWriter() {
@Override
public void writeExtensions(XmlSerializer serializer) {
StringBundleWriter bundleWriter = new StringBundleXmlWriter(bundle, serializer);
bundleWriter.writeBundle();
}
};
gpx.setExtensionsWriter(extensionsWriter);
List<RouteSegment> routeSegments = new ArrayList<>();
for (StringBundle item : routeItems) {
routeSegments.add(RouteSegment.fromStringBundle(item));
}
gpx.routeSegments = routeSegments;
List<RouteType> routeTypes = new ArrayList<>();
for (StringBundle item : typeList) {
routeTypes.add(RouteType.fromStringBundle(item));
}
gpx.routeTypes = routeTypes;
return gpx;
}

View file

@ -1,8 +1,9 @@
package net.osmand.router;
import net.osmand.GPXUtilities;
import net.osmand.GPXUtilities.GPXExtensionsReader;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.RouteSegment;
import net.osmand.GPXUtilities.RouteType;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.Location;
import net.osmand.PlatformUtil;
@ -10,11 +11,8 @@ import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
import net.osmand.binary.RouteDataBundle;
import net.osmand.binary.RouteDataObject;
import net.osmand.binary.StringBundle;
import net.osmand.binary.StringBundleReader;
import net.osmand.binary.StringBundleXmlReader;
import org.apache.commons.logging.Log;
import org.xmlpull.v1.XmlPullParser;
import java.io.File;
import java.io.FileInputStream;
@ -31,6 +29,10 @@ public class RouteImporter {
private File file;
private GPXFile gpxFile;
private List<RouteSegmentResult> route = new ArrayList<>();
private RouteRegion region = new RouteRegion();
private RouteDataResources resources = new RouteDataResources();
public RouteImporter(File file) {
this.file = file;
}
@ -40,90 +42,14 @@ public class RouteImporter {
}
public List<RouteSegmentResult> importRoute() {
final List<RouteSegmentResult> route = new ArrayList<>();
final RouteRegion region = new RouteRegion();
final RouteDataResources resources = new RouteDataResources();
GPXExtensionsReader extensionsReader = new GPXExtensionsReader() {
@Override
public boolean readExtensions(GPXFile res, XmlPullParser parser) throws Exception {
if (!resources.hasLocations()) {
List<Location> locations = resources.getLocations();
double lastElevation = HEIGHT_UNDEFINED;
if (res.tracks.size() > 0 && res.tracks.get(0).segments.size() > 0 && res.tracks.get(0).segments.get(0).points.size() > 0) {
for (WptPt point : res.tracks.get(0).segments.get(0).points) {
Location loc = new Location("", point.getLatitude(), point.getLongitude());
if (!Double.isNaN(point.ele)) {
loc.setAltitude(point.ele);
lastElevation = point.ele;
} else if (lastElevation != HEIGHT_UNDEFINED) {
loc.setAltitude(lastElevation);
}
locations.add(loc);
}
}
}
String tag = parser.getName();
if ("route".equals(tag)) {
int tok;
while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (tok == XmlPullParser.START_TAG) {
tag = parser.getName();
if ("segment".equals(tag)) {
StringBundleReader bundleReader = new StringBundleXmlReader(parser);
RouteDataObject object = new RouteDataObject(region);
RouteSegmentResult segment = new RouteSegmentResult(object);
bundleReader.readBundle();
segment.readFromBundle(new RouteDataBundle(resources, bundleReader.getBundle()));
route.add(segment);
}
} else if (tok == XmlPullParser.END_TAG) {
tag = parser.getName();
if ("route".equals(tag)) {
return true;
}
}
}
} else if ("types".equals(tag)) {
int tok;
int i = 0;
while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (tok == XmlPullParser.START_TAG) {
tag = parser.getName();
if ("type".equals(tag)) {
StringBundleReader bundleReader = new StringBundleXmlReader(parser);
bundleReader.readBundle();
StringBundle bundle = bundleReader.getBundle();
String t = bundle.getString("t", null);
String v = bundle.getString("v", null);
region.initRouteEncodingRule(i++, t, v);
}
} else if (tok == XmlPullParser.END_TAG) {
tag = parser.getName();
if ("types".equals(tag)) {
return true;
}
}
}
}
return false;
}
};
if (gpxFile != null) {
GPXUtilities.loadGPXFile(null, gpxFile, extensionsReader);
for (RouteSegmentResult segment : route) {
segment.fillNames(resources);
}
parseRoute();
} else if (file != null) {
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
GPXFile gpxFile = GPXUtilities.loadGPXFile(fis, null, extensionsReader);
for (RouteSegmentResult segment : route) {
segment.fillNames(resources);
}
gpxFile = GPXUtilities.loadGPXFile(fis);
parseRoute();
gpxFile.path = file.getAbsolutePath();
gpxFile.modifiedTime = file.lastModified();
} catch (IOException e) {
@ -139,7 +65,51 @@ public class RouteImporter {
}
}
}
return route;
}
private void parseRoute() {
collectLocations();
collectSegments();
collectTypes();
for (RouteSegmentResult segment : route) {
segment.fillNames(resources);
}
}
private void collectLocations() {
List<Location> locations = resources.getLocations();
double lastElevation = HEIGHT_UNDEFINED;
if (gpxFile.tracks.size() > 0 && gpxFile.tracks.get(0).segments.size() > 0 && gpxFile.tracks.get(0).segments.get(0).points.size() > 0) {
for (WptPt point : gpxFile.tracks.get(0).segments.get(0).points) {
Location loc = new Location("", point.getLatitude(), point.getLongitude());
if (!Double.isNaN(point.ele)) {
loc.setAltitude(point.ele);
lastElevation = point.ele;
} else if (lastElevation != HEIGHT_UNDEFINED) {
loc.setAltitude(lastElevation);
}
locations.add(loc);
}
}
}
private void collectSegments() {
for (RouteSegment segment : gpxFile.routeSegments) {
RouteDataObject object = new RouteDataObject(region);
RouteSegmentResult segmentResult = new RouteSegmentResult(object);
segmentResult.readFromBundle(new RouteDataBundle(resources, segment.toStringBundle()));
route.add(segmentResult);
}
}
private void collectTypes() {
int i = 0;
for (RouteType routeType : gpxFile.routeTypes) {
StringBundle bundle = routeType.toStringBundle();
String t = bundle.getString("t", null);
String v = bundle.getString("v", null);
region.initRouteEncodingRule(i++, t, v);
}
}
}

View file

@ -1,16 +1,10 @@
package net.osmand.router;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import net.osmand.LocationsHolder;
import net.osmand.NativeLibrary;
import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapRouteReaderAdapter;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
@ -23,6 +17,16 @@ import net.osmand.util.MapUtils;
import org.apache.commons.logging.Log;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import gnu.trove.list.array.TIntArrayList;
public class RoutePlannerFrontEnd {
protected static final Log log = PlatformUtil.getLog(RoutePlannerFrontEnd.class);
@ -39,6 +43,77 @@ public class RoutePlannerFrontEnd {
NORMAL,
COMPLEX
}
public static class GpxRouteApproximation {
// ! MAIN parameter to approximate (35m good for custom recorded tracks)
public double MINIMUM_POINT_APPROXIMATION = 50; // 35 m good for small deviations
// This parameter could speed up or slow down evaluation (better to make bigger for long routes and smaller for short)
public double MAXIMUM_STEP_APPROXIMATION = 3000;
// don't search subsegments shorter than specified distance (also used to step back for car turns)
public double MINIMUM_STEP_APPROXIMATION = 100;
// Parameter to smoother the track itself (could be 0 if it's not recorded track)
public double SMOOTHEN_POINTS_NO_ROUTE = 5;
public final RoutingContext ctx;
public int routeCalculations = 0;
public int routePointsSearched = 0;
public int routeDistCalculations = 0;
public List<GpxPoint> finalPoints = new ArrayList<GpxPoint>();
public List<RouteSegmentResult> result = new ArrayList<RouteSegmentResult>();
public int routeDistance;
public int routeGapDistance;
public int routeDistanceUnmatched;
public GpxRouteApproximation(RoutingContext ctx) {
this.ctx = ctx;
}
public GpxRouteApproximation(GpxRouteApproximation gctx) {
this.ctx = gctx.ctx;
this.routeDistance = gctx.routeDistance;
}
@Override
public String toString() {
return String.format(">> GPX approximation (%d of %d m route calcs, %d route points searched) for %d m: %d m umatched",
routeCalculations, routeDistCalculations, routePointsSearched, routeDistance, routeDistanceUnmatched);
}
public double distFromLastPoint(LatLon startPoint) {
if (result.size() > 0) {
return MapUtils.getDistance(getLastPoint(), startPoint);
}
return 0;
}
public LatLon getLastPoint() {
if (result.size() > 0) {
return result.get(result.size() - 1).getEndPoint();
}
return null;
}
}
public static class GpxPoint {
public int ind;
public LatLon loc;
public double cumDist;
public RouteSegmentPoint pnt;
public List<RouteSegmentResult> routeToTarget;
public List<RouteSegmentResult> stepBackRoute;
public int targetInd = -1;
public boolean straightLine = false;
public GpxPoint() {
}
public GpxPoint(GpxPoint point) {
this.ind = point.ind;
this.loc = point.loc;
this.cumDist = point.cumDist;
}
}
public RoutingContext buildRoutingContext(RoutingConfiguration config, NativeLibrary nativeLibrary, BinaryMapIndexReader[] map, RouteCalculationMode rm) {
return new RoutingContext(config, nativeLibrary, map, rm);
@ -61,15 +136,21 @@ public class RoutePlannerFrontEnd {
}
public RouteSegmentPoint findRouteSegment(double lat, double lon, RoutingContext ctx, List<RouteSegmentPoint> list, boolean transportStop) throws IOException {
return findRouteSegment(lat, lon, ctx, list, false, false);
}
public RouteSegmentPoint findRouteSegment(double lat, double lon, RoutingContext ctx, List<RouteSegmentPoint> list, boolean transportStop,
boolean allowDuplications) throws IOException {
long now = System.nanoTime();
int px = MapUtils.get31TileNumberX(lon);
int py = MapUtils.get31TileNumberY(lat);
ArrayList<RouteDataObject> dataObjects = new ArrayList<RouteDataObject>();
ctx.loadTileData(px, py, 17, dataObjects);
ctx.loadTileData(px, py, 17, dataObjects, allowDuplications);
if (dataObjects.isEmpty()) {
ctx.loadTileData(px, py, 15, dataObjects);
ctx.loadTileData(px, py, 15, dataObjects, allowDuplications);
}
if (dataObjects.isEmpty()) {
ctx.loadTileData(px, py, 14, dataObjects);
ctx.loadTileData(px, py, 14, dataObjects, allowDuplications);
}
if (list == null) {
list = new ArrayList<BinaryRoutePlanner.RouteSegmentPoint>();
@ -91,7 +172,7 @@ public class RoutePlannerFrontEnd {
}
if (road != null) {
if(!transportStop) {
float prio = ctx.getRouter().defineSpeedPriority(road.road);
float prio = Math.max(ctx.getRouter().defineSpeedPriority(road.road), 0.3f);
if (prio > 0) {
road.distSquare = (road.distSquare + GPS_POSSIBLE_ERROR * GPS_POSSIBLE_ERROR)
/ (prio * prio);
@ -111,6 +192,9 @@ public class RoutePlannerFrontEnd {
return Double.compare(o1.distSquare, o2.distSquare);
}
});
if (ctx.calculationProgress != null) {
ctx.calculationProgress.timeToFindInitialSegments += (System.nanoTime() - now);
}
if (list.size() > 0) {
RouteSegmentPoint ps = null;
if (ctx.publicTransport) {
@ -138,7 +222,6 @@ public class RoutePlannerFrontEnd {
return null;
}
public List<RouteSegmentResult> searchRoute(final RoutingContext ctx, LatLon start, LatLon end, List<LatLon> intermediates) throws IOException, InterruptedException {
return searchRoute(ctx, start, end, intermediates, null);
}
@ -147,6 +230,419 @@ public class RoutePlannerFrontEnd {
useSmartRouteRecalculation = use;
}
public GpxRouteApproximation searchGpxRoute(GpxRouteApproximation gctx, List<GpxPoint> gpxPoints, ResultMatcher<GpxRouteApproximation> resultMatcher) throws IOException, InterruptedException {
long timeToCalculate = System.nanoTime();
gctx.ctx.keepNativeRoutingContext = true;
if (gctx.ctx.calculationProgress == null) {
gctx.ctx.calculationProgress = new RouteCalculationProgress();
}
GpxPoint start = null;
GpxPoint prev = null;
if (gpxPoints.size() > 0) {
gctx.ctx.calculationProgress.totalIterations = (int) (gpxPoints.get(gpxPoints.size() - 1).cumDist / gctx.MAXIMUM_STEP_APPROXIMATION + 1);
start = gpxPoints.get(0);
}
while (start != null && !gctx.ctx.calculationProgress.isCancelled) {
if (Thread.currentThread().isInterrupted()) {
return null;
}
double routeDist = gctx.MAXIMUM_STEP_APPROXIMATION;
GpxPoint next = findNextGpxPointWithin(gctx, gpxPoints, start, routeDist);
boolean routeFound = false;
if (next != null && initRoutingPoint(start, gctx, gctx.MINIMUM_POINT_APPROXIMATION)) {
gctx.ctx.calculationProgress.totalEstimatedDistance = 0;
gctx.ctx.calculationProgress.iteration = (int) (next.cumDist / gctx.MAXIMUM_STEP_APPROXIMATION);
while (routeDist >= gctx.MINIMUM_STEP_APPROXIMATION && !routeFound) {
routeFound = initRoutingPoint(next, gctx, gctx.MINIMUM_POINT_APPROXIMATION);
if (routeFound) {
routeFound = findGpxRouteSegment(gctx, gpxPoints, start, next, prev != null);
if (routeFound) {
// route is found - cut the end of the route and move to next iteration
// start.stepBackRoute = new ArrayList<RouteSegmentResult>();
// boolean stepBack = true;
boolean stepBack = stepBackAndFindPrevPointInRoute(gctx, gpxPoints, start, next);
if (!stepBack) {
// not supported case (workaround increase MAXIMUM_STEP_APPROXIMATION)
log.info("Consider to increase MAXIMUM_STEP_APPROXIMATION to: " + routeDist * 2);
start.routeToTarget = null;
routeFound = false;
break;
}
}
}
if (!routeFound) {
// route is not found move next point closer to start point (distance / 2)
routeDist = routeDist / 2;
if (routeDist < gctx.MINIMUM_STEP_APPROXIMATION && routeDist > gctx.MINIMUM_STEP_APPROXIMATION / 2 + 1) {
routeDist = gctx.MINIMUM_STEP_APPROXIMATION;
}
next = findNextGpxPointWithin(gctx, gpxPoints, start, routeDist);
if (next != null) {
routeDist = Math.min(next.cumDist - start.cumDist, routeDist);
}
}
}
}
// route is not found skip segment and keep it as straight line on display
if (!routeFound) {
// route is not found, move start point by
next = findNextGpxPointWithin(gctx, gpxPoints, start, gctx.MINIMUM_STEP_APPROXIMATION);
if (prev != null) {
prev.routeToTarget.addAll(prev.stepBackRoute);
makeSegmentPointPrecise(prev.routeToTarget.get(prev.routeToTarget.size() - 1), start.loc, false);
if (next != null) {
log.warn("NOT found route from: " + start.pnt.getRoad() + " at " + start.pnt.getSegmentStart());
}
}
prev = null;
} else {
prev = start;
}
start = next;
}
if (gctx.ctx.calculationProgress != null) {
gctx.ctx.calculationProgress.timeToCalculate = System.nanoTime() - timeToCalculate;
}
gctx.ctx.deleteNativeRoutingContext();
BinaryRoutePlanner.printDebugMemoryInformation(gctx.ctx);
calculateGpxRoute(gctx, gpxPoints);
if (!gctx.result.isEmpty() && !gctx.ctx.calculationProgress.isCancelled) {
new RouteResultPreparation().printResults(gctx.ctx, gpxPoints.get(0).loc, gpxPoints.get(gpxPoints.size() - 1).loc, gctx.result);
System.out.println(gctx);
}
if (resultMatcher != null) {
resultMatcher.publish(gctx.ctx.calculationProgress.isCancelled ? null : gctx);
}
return gctx;
}
private boolean stepBackAndFindPrevPointInRoute(GpxRouteApproximation gctx,
List<GpxPoint> gpxPoints, GpxPoint start, GpxPoint next) throws IOException {
// step back to find to be sure
// 1) route point is behind GpxPoint - MINIMUM_POINT_APPROXIMATION (end route point could slightly ahead)
// 2) we don't miss correct turn i.e. points could be attached to muliple routes
// 3) to make sure that we perfectly connect to RoadDataObject points
double STEP_BACK_DIST = Math.max(gctx.MINIMUM_POINT_APPROXIMATION, gctx.MINIMUM_STEP_APPROXIMATION);
double d = 0;
int segmendInd = start.routeToTarget.size() - 1;
boolean search = true;
start.stepBackRoute = new ArrayList<RouteSegmentResult>();
mainLoop: for (; segmendInd >= 0 && search; segmendInd--) {
RouteSegmentResult rr = start.routeToTarget.get(segmendInd);
boolean minus = rr.getStartPointIndex() < rr.getEndPointIndex();
int nextInd;
for (int j = rr.getEndPointIndex(); j != rr.getStartPointIndex(); j = nextInd) {
nextInd = minus ? j - 1 : j + 1;
d += MapUtils.getDistance(rr.getPoint(j), rr.getPoint(nextInd));
if (d > STEP_BACK_DIST) {
if (nextInd == rr.getStartPointIndex()) {
segmendInd--;
} else {
start.stepBackRoute.add(new RouteSegmentResult(rr.getObject(), nextInd, rr.getEndPointIndex()));
rr.setEndPointIndex(nextInd);
}
search = false;
break mainLoop;
}
}
}
if (segmendInd == -1) {
// here all route segments - 1 is longer than needed distance to step back
return false;
}
while (start.routeToTarget.size() > segmendInd + 1) {
RouteSegmentResult removed = start.routeToTarget.remove(segmendInd + 1);
start.stepBackRoute.add(removed);
}
RouteSegmentResult res = start.routeToTarget.get(segmendInd);
next.pnt = new RouteSegmentPoint(res.getObject(), res.getEndPointIndex(), 0);
return true;
}
private void calculateGpxRoute(GpxRouteApproximation gctx, List<GpxPoint> gpxPoints) {
RouteRegion reg = new RouteRegion();
reg.initRouteEncodingRule(0, "highway", RouteResultPreparation.UNMATCHED_HIGHWAY_TYPE);
List<LatLon> lastStraightLine = null;
GpxPoint straightPointStart = null;
for (int i = 0; i < gpxPoints.size() && !gctx.ctx.calculationProgress.isCancelled; ) {
GpxPoint pnt = gpxPoints.get(i);
if (pnt.routeToTarget != null && !pnt.routeToTarget.isEmpty()) {
LatLon startPoint = pnt.routeToTarget.get(0).getStartPoint();
if (lastStraightLine != null) {
lastStraightLine.add(startPoint);
addStraightLine(gctx, lastStraightLine, straightPointStart, reg);
lastStraightLine = null;
}
if (gctx.distFromLastPoint(startPoint) > 1) {
gctx.routeGapDistance += gctx.distFromLastPoint(startPoint);
System.out.println(String.format("????? gap of route point = %f, gap of actual gpxPoint = %f, %s ",
gctx.distFromLastPoint(startPoint), gctx.distFromLastPoint(pnt.loc), pnt.loc));
}
gctx.finalPoints.add(pnt);
gctx.result.addAll(pnt.routeToTarget);
i = pnt.targetInd;
} else {
// add straight line from i -> i+1
if (lastStraightLine == null) {
lastStraightLine = new ArrayList<LatLon>();
straightPointStart = pnt;
// make smooth connection
if (gctx.distFromLastPoint(pnt.loc) > 1) {
lastStraightLine.add(gctx.getLastPoint());
}
}
lastStraightLine.add(pnt.loc);
i++;
}
}
if (lastStraightLine != null) {
addStraightLine(gctx, lastStraightLine, straightPointStart, reg);
lastStraightLine = null;
}
// clean turns to recaculate them
cleanupResultAndAddTurns(gctx);
}
public static RouteSegmentResult generateStraightLineSegment(float averageSpeed, List<LatLon> points) {
RouteRegion reg = new RouteRegion();
reg.initRouteEncodingRule(0, "highway", RouteResultPreparation.UNMATCHED_HIGHWAY_TYPE);
RouteDataObject rdo = new RouteDataObject(reg);
int size = points.size();
TIntArrayList x = new TIntArrayList(size);
TIntArrayList y = new TIntArrayList(size);
double distance = 0;
double distOnRoadToPass = 0;
LatLon prev = null;
for (int i = 0; i < size; i++) {
LatLon l = points.get(i);
if (l != null) {
x.add(MapUtils.get31TileNumberX(l.getLongitude()));
y.add(MapUtils.get31TileNumberY(l.getLatitude()));
if (prev != null) {
double d = MapUtils.getDistance(l, prev);
distance += d;
distOnRoadToPass += d / averageSpeed;
}
}
prev = l;
}
rdo.pointsX = x.toArray();
rdo.pointsY = y.toArray();
rdo.types = new int[] { 0 } ;
rdo.id = -1;
RouteSegmentResult segment = new RouteSegmentResult(rdo, 0, rdo.getPointsLength() - 1);
segment.setSegmentTime((float) distOnRoadToPass);
segment.setSegmentSpeed(averageSpeed);
segment.setDistance((float) distance);
segment.setTurnType(TurnType.straight());
return segment;
}
public List<GpxPoint> generateGpxPoints(GpxRouteApproximation gctx, LocationsHolder locationsHolder) {
List<GpxPoint> gpxPoints = new ArrayList<>(locationsHolder.getSize());
GpxPoint prev = null;
for(int i = 0; i < locationsHolder.getSize(); i++) {
GpxPoint p = new GpxPoint();
p.ind = i;
p.loc = locationsHolder.getLatLon(i);
if (prev != null) {
p.cumDist = MapUtils.getDistance(p.loc, prev.loc) + prev.cumDist;
}
gpxPoints.add(p);
gctx.routeDistance = (int) p.cumDist;
prev = p;
}
return gpxPoints;
}
private void cleanupResultAndAddTurns(GpxRouteApproximation gctx) {
// cleanup double joints
int LOOK_AHEAD = 4;
for(int i = 0; i < gctx.result.size() && !gctx.ctx.calculationProgress.isCancelled; i++) {
RouteSegmentResult s = gctx.result.get(i);
for(int j = i + 2; j <= i + LOOK_AHEAD && j < gctx.result.size(); j++) {
RouteSegmentResult e = gctx.result.get(j);
if (e.getStartPoint().equals(s.getEndPoint())) {
while ((--j) != i) {
gctx.result.remove(j);
}
break;
}
}
}
RouteResultPreparation preparation = new RouteResultPreparation();
for (RouteSegmentResult r : gctx.result) {
r.setTurnType(null);
r.setDescription("");
}
if (!gctx.ctx.calculationProgress.isCancelled) {
preparation.prepareTurnResults(gctx.ctx, gctx.result);
}
}
private void addStraightLine(GpxRouteApproximation gctx, List<LatLon> lastStraightLine, GpxPoint strPnt, RouteRegion reg) {
RouteDataObject rdo = new RouteDataObject(reg);
if(gctx.SMOOTHEN_POINTS_NO_ROUTE > 0) {
simplifyDouglasPeucker(lastStraightLine, gctx.SMOOTHEN_POINTS_NO_ROUTE, 0, lastStraightLine.size() - 1);
}
int s = lastStraightLine.size();
TIntArrayList x = new TIntArrayList(s);
TIntArrayList y = new TIntArrayList(s);
for (int i = 0; i < s; i++) {
if(lastStraightLine.get(i) != null) {
LatLon l = lastStraightLine.get(i);
int t = x.size() - 1;
x.add(MapUtils.get31TileNumberX(l.getLongitude()));
y.add(MapUtils.get31TileNumberY(l.getLatitude()));
if (t >= 0) {
double dist = MapUtils.squareRootDist31(x.get(t), y.get(t), x.get(t + 1), y.get(t + 1));
gctx.routeDistanceUnmatched += dist;
}
}
}
rdo.pointsX = x.toArray();
rdo.pointsY = y.toArray();
rdo.types = new int[] { 0 } ;
rdo.id = -1;
strPnt.routeToTarget = new ArrayList<>();
strPnt.straightLine = true;
strPnt.routeToTarget.add(new RouteSegmentResult(rdo, 0, rdo.getPointsLength() - 1));
RouteResultPreparation preparation = new RouteResultPreparation();
try {
preparation.prepareResult(gctx.ctx, strPnt.routeToTarget, false);
} catch (IOException e) {
throw new IllegalStateException(e);
}
// VIEW: comment to see road without straight connections
gctx.finalPoints.add(strPnt);
gctx.result.addAll(strPnt.routeToTarget);
}
private void simplifyDouglasPeucker(List<LatLon> l, double eps, int start, int end) {
double dmax = -1;
int index = -1;
LatLon s = l.get(start);
LatLon e = l.get(end);
for (int i = start + 1; i <= end - 1; i++) {
LatLon ip = l.get(i);
double dist = MapUtils.getOrthogonalDistance(ip.getLatitude(), ip.getLongitude(), s.getLatitude(), s.getLongitude(),
e.getLatitude(), e.getLongitude());
if (dist > dmax) {
dmax = dist;
index = i;
}
}
if (dmax >= eps) {
simplifyDouglasPeucker(l, eps, start, index);
simplifyDouglasPeucker(l, eps, index, end);
} else {
for(int i = start + 1; i < end; i++ ) {
l.set(i, null);
}
}
}
private boolean initRoutingPoint(GpxPoint start, GpxRouteApproximation gctx, double distThreshold) throws IOException {
if (start != null && start.pnt == null) {
gctx.routePointsSearched++;
RouteSegmentPoint rsp = findRouteSegment(start.loc.getLatitude(), start.loc.getLongitude(), gctx.ctx, null, false);
if (rsp != null) {
if (MapUtils.getDistance(rsp.getPreciseLatLon(), start.loc) < distThreshold) {
start.pnt = rsp;
}
}
}
if (start != null && start.pnt != null) {
return true;
}
return false;
}
private GpxPoint findNextGpxPointWithin(GpxRouteApproximation gctx, List<GpxPoint> gpxPoints,
GpxPoint start, double dist) {
// returns first point with that has slightly more than dist or last point
int plus = dist > 0 ? 1 : -1;
int targetInd = start.ind + plus;
GpxPoint target = null;
while (targetInd < gpxPoints.size() && targetInd >= 0) {
target = gpxPoints.get(targetInd);
if (Math.abs(target.cumDist - start.cumDist) > Math.abs(dist)) {
break;
}
targetInd = targetInd + plus;
}
return target;
}
private boolean findGpxRouteSegment(GpxRouteApproximation gctx, List<GpxPoint> gpxPoints,
GpxPoint start, GpxPoint target, boolean prevRouteCalculated) throws IOException, InterruptedException {
List<RouteSegmentResult> res = null;
boolean routeIsCorrect = false;
if (start.pnt != null && target.pnt != null) {
start.pnt = new RouteSegmentPoint(start.pnt);
target.pnt = new RouteSegmentPoint(target.pnt);
gctx.routeDistCalculations += (target.cumDist - start.cumDist);
gctx.routeCalculations++;
res = searchRouteInternalPrepare(gctx.ctx, start.pnt, target.pnt, null);
//BinaryRoutePlanner.printDebugMemoryInformation(gctx.ctx);
routeIsCorrect = res != null && !res.isEmpty();
for (int k = start.ind + 1; routeIsCorrect && k < target.ind; k++) {
GpxPoint ipoint = gpxPoints.get(k);
if (!pointCloseEnough(gctx, ipoint, res)) {
routeIsCorrect = false;
}
}
if (routeIsCorrect) {
// correct start point though don't change end point
if (!prevRouteCalculated) {
// make first position precise
makeSegmentPointPrecise(res.get(0), start.loc, true);
} else {
if(res.get(0).getObject().getId() == start.pnt.getRoad().getId()) {
// start point could shift to +-1 due to direction
res.get(0).setStartPointIndex(start.pnt.getSegmentStart());
} else {
// for native routing this is possible when point lies on intersection of 2 lines
// solution here could be to pass to native routing id of the route
// though it should not create any issue
System.out.println("??? not found " + start.pnt.getRoad().getId() + " instead "
+ res.get(0).getObject().getId());
}
}
start.routeToTarget = res;
start.targetInd = target.ind;
}
}
return routeIsCorrect;
}
private boolean pointCloseEnough(GpxRouteApproximation gctx, GpxPoint ipoint, List<RouteSegmentResult> res) {
int px = MapUtils.get31TileNumberX(ipoint.loc.getLongitude());
int py = MapUtils.get31TileNumberY(ipoint.loc.getLatitude());
double SQR = gctx.MINIMUM_POINT_APPROXIMATION;
SQR = SQR * SQR;
for (RouteSegmentResult sr : res) {
int start = sr.getStartPointIndex();
int end = sr.getEndPointIndex();
if (sr.getStartPointIndex() > sr.getEndPointIndex()) {
start = sr.getEndPointIndex();
end = sr.getStartPointIndex();
}
for (int i = start; i < end; i++) {
RouteDataObject r = sr.getObject();
QuadPoint pp = MapUtils.getProjectionPoint31(px, py, r.getPoint31XTile(i), r.getPoint31YTile(i),
r.getPoint31XTile(i + 1), r.getPoint31YTile(i + 1));
double currentsDist = squareDist((int) pp.x, (int) pp.y, px, py);
if (currentsDist <= SQR) {
return true;
}
}
}
return false;
}
private boolean needRequestPrivateAccessRouting(RoutingContext ctx, List<LatLon> points) throws IOException {
boolean res = false;
@ -174,7 +670,7 @@ public class RoutePlannerFrontEnd {
public List<RouteSegmentResult> searchRoute(final RoutingContext ctx, LatLon start, LatLon end, List<LatLon> intermediates,
PrecalculatedRouteDirection routeDirection) throws IOException, InterruptedException {
ctx.timeToCalculate = System.nanoTime();
long timeToCalculate = System.nanoTime();
if (ctx.calculationProgress == null) {
ctx.calculationProgress = new RouteCalculationProgress();
}
@ -206,6 +702,7 @@ public class RoutePlannerFrontEnd {
}
routeDirection = PrecalculatedRouteDirection.build(ls, ctx.config.DEVIATION_RADIUS, ctx.getRouter().getMaxSpeed());
}
List<RouteSegmentResult> res ;
if (intermediatesEmpty && ctx.nativeLib != null) {
ctx.startX = MapUtils.get31TileNumberX(start.getLongitude());
ctx.startY = MapUtils.get31TileNumberY(start.getLatitude());
@ -219,32 +716,32 @@ public class RoutePlannerFrontEnd {
ctx.precalculatedRouteDirection = routeDirection.adopt(ctx);
}
ctx.calculationProgress.nextIteration();
List<RouteSegmentResult> res = runNativeRouting(ctx, recalculationEnd);
if (res != null) {
new RouteResultPreparation().printResults(ctx, start, end, res);
}
res = runNativeRouting(ctx, recalculationEnd);
makeStartEndPointsPrecise(res, start, end, intermediates);
return res;
}
int indexNotFound = 0;
List<RouteSegmentPoint> points = new ArrayList<RouteSegmentPoint>();
if (!addSegment(start, ctx, indexNotFound++, points, ctx.startTransportStop)) {
return null;
}
if (intermediates != null) {
for (LatLon l : intermediates) {
if (!addSegment(l, ctx, indexNotFound++, points, false)) {
return null;
} else {
int indexNotFound = 0;
List<RouteSegmentPoint> points = new ArrayList<RouteSegmentPoint>();
if (!addSegment(start, ctx, indexNotFound++, points, ctx.startTransportStop)) {
return null;
}
if (intermediates != null) {
for (LatLon l : intermediates) {
if (!addSegment(l, ctx, indexNotFound++, points, false)) {
System.out.println(points.get(points.size() - 1).getRoad().toString());
return null;
}
}
}
if (!addSegment(end, ctx, indexNotFound++, points, ctx.targetTransportStop)) {
return null;
}
ctx.calculationProgress.nextIteration();
res = searchRouteImpl(ctx, points, routeDirection);
}
if (!addSegment(end, ctx, indexNotFound++, points, ctx.targetTransportStop)) {
return null;
if (ctx.calculationProgress != null) {
ctx.calculationProgress.timeToCalculate = (System.nanoTime() - timeToCalculate);
}
ctx.calculationProgress.nextIteration();
List<RouteSegmentResult> res = searchRoute(ctx, points, routeDirection);
// make start and end more precise
makeStartEndPointsPrecise(res, start, end, intermediates);
BinaryRoutePlanner.printDebugMemoryInformation(ctx);
if (res != null) {
new RouteResultPreparation().printResults(ctx, start, end, res);
}
@ -253,35 +750,8 @@ public class RoutePlannerFrontEnd {
protected void makeStartEndPointsPrecise(List<RouteSegmentResult> res, LatLon start, LatLon end, List<LatLon> intermediates) {
if (res.size() > 0) {
updateResult(res.get(0), start, true);
updateResult(res.get(res.size() - 1), end, false);
if (intermediates != null) {
int k = 1;
for (int i = 0; i < intermediates.size(); i++) {
LatLon ll = intermediates.get(i);
int px = MapUtils.get31TileNumberX(ll.getLongitude());
int py = MapUtils.get31TileNumberY(ll.getLatitude());
for (; k < res.size(); k++) {
double currentsDist = projectDistance(res, k, px, py);
if (currentsDist < 500 * 500) {
for (int k1 = k + 1; k1 < res.size(); k1++) {
double c2 = projectDistance(res, k1, px, py);
if (c2 < currentsDist) {
k = k1;
currentsDist = c2;
} else if (k1 - k > 15) {
break;
}
}
updateResult(res.get(k), ll, false);
if (k < res.size() - 1) {
updateResult(res.get(k + 1), ll, true);
}
break;
}
}
}
}
makeSegmentPointPrecise(res.get(0), start, true);
makeSegmentPointPrecise(res.get(res.size() - 1), end, false);
}
}
@ -295,12 +765,13 @@ public class RoutePlannerFrontEnd {
return currentsDist;
}
private void updateResult(RouteSegmentResult routeSegmentResult, LatLon point, boolean st) {
private void makeSegmentPointPrecise(RouteSegmentResult routeSegmentResult, LatLon point, boolean st) {
int px = MapUtils.get31TileNumberX(point.getLongitude());
int py = MapUtils.get31TileNumberY(point.getLatitude());
int pind = st ? routeSegmentResult.getStartPointIndex() : routeSegmentResult.getEndPointIndex();
RouteDataObject r = routeSegmentResult.getObject();
RouteDataObject r = new RouteDataObject(routeSegmentResult.getObject());
routeSegmentResult.setObject(r);
QuadPoint before = null;
QuadPoint after = null;
if (pind > 0) {
@ -364,7 +835,7 @@ public class RoutePlannerFrontEnd {
ctx.calculationProgress.segmentNotFound = indexNotFound;
return false;
} else {
log.info("Route segment found " + f.getRoad().id + " " + f.getRoad().getName());
log.info("Route segment found " + f.road);
res.add(f);
return true;
}
@ -383,6 +854,14 @@ public class RoutePlannerFrontEnd {
ctx.precalculatedRouteDirection = routeDirection.adopt(ctx);
}
if (ctx.nativeLib != null) {
ctx.startX = start.preciseX;
ctx.startY = start.preciseY;
ctx.startRoadId = start.road.id;
ctx.startSegmentInd = start.segStart;
ctx.targetX = end.preciseX;
ctx.targetY = end.preciseY;
ctx.targetRoadId = end.road.id;
ctx.targetSegmentInd = end.segStart;
return runNativeRouting(ctx, recalculationEnd);
} else {
refreshProgressDistance(ctx);
@ -446,11 +925,9 @@ public class RoutePlannerFrontEnd {
ctx.checkOldRoutingFiles(ctx.startX, ctx.startY);
ctx.checkOldRoutingFiles(ctx.targetX, ctx.targetY);
long time = System.currentTimeMillis();
RouteSegmentResult[] res = ctx.nativeLib.runNativeRouting(ctx.startX, ctx.startY, ctx.targetX, ctx.targetY,
ctx.config, regions, ctx.calculationProgress, ctx.precalculatedRouteDirection, ctx.calculationMode == RouteCalculationMode.BASE,
ctx.publicTransport, ctx.startTransportStop, ctx.targetTransportStop);
log.info("Native routing took " + (System.currentTimeMillis() - time) / 1000f + " seconds");
// long time = System.currentTimeMillis();
RouteSegmentResult[] res = ctx.nativeLib.runNativeRouting(ctx, regions, ctx.calculationMode == RouteCalculationMode.BASE);
// log.info("Native routing took " + (System.currentTimeMillis() - time) / 1000f + " seconds");
ArrayList<RouteSegmentResult> result = new ArrayList<RouteSegmentResult>(Arrays.asList(res));
if (recalculationEnd != null) {
log.info("Native routing use precalculated route");
@ -461,20 +938,23 @@ public class RoutePlannerFrontEnd {
current = pr;
}
}
ctx.routingTime = ctx.calculationProgress.routingCalculatedTime;
ctx.visitedSegments = ctx.calculationProgress.visitedSegments;
ctx.loadedTiles = ctx.calculationProgress.loadedTiles;
ctx.routingTime += ctx.calculationProgress.routingCalculatedTime;
return new RouteResultPreparation().prepareResult(ctx, result, recalculationEnd != null);
}
private List<RouteSegmentResult> searchRoute(final RoutingContext ctx, List<RouteSegmentPoint> points, PrecalculatedRouteDirection routeDirection)
private List<RouteSegmentResult> searchRouteImpl(final RoutingContext ctx, List<RouteSegmentPoint> points, PrecalculatedRouteDirection routeDirection)
throws IOException, InterruptedException {
if (points.size() <= 2) {
// simple case 2 points only
if (!useSmartRouteRecalculation) {
ctx.previouslyCalculatedRoute = null;
}
return searchRoute(ctx, points.get(0), points.get(1), routeDirection);
pringGC(ctx, true);
List<RouteSegmentResult> res = searchRouteInternalPrepare(ctx, points.get(0), points.get(1), routeDirection);
pringGC(ctx, false);
makeStartEndPointsPrecise(res, points.get(0).getPreciseLatLon(), points.get(1).getPreciseLatLon(), null);
return res;
}
ArrayList<RouteSegmentResult> firstPartRecalculatedRoute = null;
@ -516,19 +996,10 @@ public class RoutePlannerFrontEnd {
local.visitor = ctx.visitor;
local.calculationProgress = ctx.calculationProgress;
List<RouteSegmentResult> res = searchRouteInternalPrepare(local, points.get(i), points.get(i + 1), routeDirection);
makeStartEndPointsPrecise(res, points.get(i).getPreciseLatLon(), points.get(i + 1).getPreciseLatLon(), null);
results.addAll(res);
ctx.distinctLoadedTiles += local.distinctLoadedTiles;
ctx.loadedTiles += local.loadedTiles;
ctx.visitedSegments += local.visitedSegments;
ctx.loadedPrevUnloadedTiles += local.loadedPrevUnloadedTiles;
ctx.timeToCalculate += local.timeToCalculate;
ctx.timeToLoad += local.timeToLoad;
ctx.timeToLoadHeaders += local.timeToLoadHeaders;
ctx.relaxedSegments += local.relaxedSegments;
ctx.routingTime += local.routingTime;
local.unloadAllData(ctx);
// local.unloadAllData(ctx);
if (restPartRecalculatedRoute != null) {
results.addAll(restPartRecalculatedRoute);
break;
@ -539,28 +1010,24 @@ public class RoutePlannerFrontEnd {
}
@SuppressWarnings("static-access")
private List<RouteSegmentResult> searchRoute(final RoutingContext ctx, RouteSegmentPoint start, RouteSegmentPoint end,
PrecalculatedRouteDirection routeDirection) throws IOException, InterruptedException {
if (ctx.SHOW_GC_SIZE) {
long h1 = ctx.runGCUsedMemory();
private void pringGC(final RoutingContext ctx, boolean before) {
if (RoutingContext.SHOW_GC_SIZE && before) {
long h1 = RoutingContext.runGCUsedMemory();
float mb = (1 << 20);
log.warn("Used before routing " + h1 / mb + " actual");
}
List<RouteSegmentResult> result = searchRouteInternalPrepare(ctx, start, end, routeDirection);
if (RoutingContext.SHOW_GC_SIZE) {
} else if (RoutingContext.SHOW_GC_SIZE && !before) {
int sz = ctx.global.size;
log.warn("Subregion size " + ctx.subregionTiles.size() + " " + " tiles " + ctx.indexedSubregions.size());
ctx.runGCUsedMemory();
long h1 = ctx.runGCUsedMemory();
RoutingContext.runGCUsedMemory();
long h1 = RoutingContext.runGCUsedMemory();
ctx.unloadAllData();
ctx.runGCUsedMemory();
long h2 = ctx.runGCUsedMemory();
RoutingContext.runGCUsedMemory();
long h2 = RoutingContext.runGCUsedMemory();
float mb = (1 << 20);
log.warn("Unload context : estimated " + sz / mb + " ?= " + (h1 - h2) / mb + " actual");
}
return result;
}
}

View file

@ -1,5 +1,26 @@
package net.osmand.router;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.set.hash.TIntHashSet;
import net.osmand.PlatformUtil;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule;
@ -18,33 +39,16 @@ import net.osmand.util.Algorithms;
import net.osmand.util.MapAlgorithms;
import net.osmand.util.MapUtils;
import org.apache.commons.logging.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import gnu.trove.iterator.TIntIterator;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.set.hash.TIntHashSet;
public class RouteResultPreparation {
public static boolean PRINT_TO_CONSOLE_ROUTE_INFORMATION_TO_TEST = false;
public static String PRINT_TO_GPX_FILE = null;
private static final float TURN_DEGREE_MIN = 45;
private static final float UNMATCHED_TURN_DEGREE_MINIMUM = 45;
private static final float SPLIT_TURN_DEGREE_NOT_STRAIGHT = 100;
public static final int SHIFT_ID = 6;
private Log log = PlatformUtil.getLog(RouteResultPreparation.class);
public static final String UNMATCHED_HIGHWAY_TYPE = "unmatched";
/**
* Helper method to prepare final result
*/
@ -174,6 +178,11 @@ public class RouteResultPreparation {
splitRoadsAndAttachRoadSegments(ctx, result, recalculation);
calculateTimeSpeed(ctx, result);
prepareTurnResults(ctx, result);
return result;
}
public void prepareTurnResults(RoutingContext ctx, List<RouteSegmentResult> result) {
for (int i = 0; i < result.size(); i ++) {
TurnType turnType = getTurnInfo(result, i, ctx.leftSideNavigation);
result.get(i).setTurnType(turnType);
@ -183,7 +192,6 @@ public class RouteResultPreparation {
ignorePrecedingStraightsOnSameIntersection(ctx.leftSideNavigation, result);
justifyUTurns(ctx.leftSideNavigation, result);
addTurnInfoDescriptions(result);
return result;
}
protected void ignorePrecedingStraightsOnSameIntersection(boolean leftside, List<RouteSegmentResult> result) {
@ -217,7 +225,7 @@ public class RouteResultPreparation {
private void justifyUTurns(boolean leftSide, List<RouteSegmentResult> result) {
int next;
for (int i = 0; i < result.size() - 1; i = next) {
for (int i = 1; i < result.size() - 1; i = next) {
next = i + 1;
TurnType t = result.get(i).getTurnType();
// justify turn
@ -269,7 +277,7 @@ public class RouteResultPreparation {
double d = measuredDist(road.getPoint31XTile(j), road.getPoint31YTile(j), road.getPoint31XTile(next),
road.getPoint31YTile(next));
distance += d;
double obstacle = ctx.getRouter().defineObstacle(road, j);
double obstacle = ctx.getRouter().defineObstacle(road, j, plus);
if (obstacle < 0) {
obstacle = 0;
}
@ -282,11 +290,12 @@ public class RouteResultPreparation {
float height = heightDistanceArray[heightIndex];
if (prevHeight != -99999.0f) {
float heightDiff = height - prevHeight;
if (heightDiff > 0) { //ascent only
distOnRoadToPass += heightDiff * 6.0f; //Naismith's rule: add 1 hour per every 600m of ascent
if (heightDiff > 0) { // ascent only
distOnRoadToPass += heightDiff * 6.0f; // Naismith's rule: add 1 hour per every 600m of
// ascent
}
}
prevHeight = height;
prevHeight = height;
}
}
}
@ -307,6 +316,7 @@ public class RouteResultPreparation {
RouteSegmentResult rr = result.get(i);
boolean plus = rr.getStartPointIndex() < rr.getEndPointIndex();
int next;
boolean unmatched = UNMATCHED_HIGHWAY_TYPE.equals(rr.getObject().getHighway());
for (int j = rr.getStartPointIndex(); j != rr.getEndPointIndex(); j = next) {
next = plus ? j + 1 : j - 1;
if (j == rr.getStartPointIndex()) {
@ -317,27 +327,33 @@ public class RouteResultPreparation {
}
List<RouteSegmentResult> attachedRoutes = rr.getAttachedRoutes(next);
boolean tryToSplit = next != rr.getEndPointIndex() && !rr.getObject().roundabout() && attachedRoutes != null;
if(rr.getDistance(next, plus ) == 0) {
if (rr.getDistance(next, plus) == 0) {
// same point will be processed next step
tryToSplit = false;
}
if (tryToSplit) {
float distBearing = unmatched ? RouteSegmentResult.DIST_BEARING_DETECT_UNMATCHED : RouteSegmentResult.DIST_BEARING_DETECT;
// avoid small zigzags
float before = rr.getBearing(next, !plus);
float after = rr.getBearing(next, plus);
if(rr.getDistance(next, plus ) < 5) {
after = before + 180;
} else if(rr.getDistance(next, !plus ) < 5) {
before = after - 180;
float before = rr.getBearingEnd(next, distBearing);
float after = rr.getBearingBegin(next, distBearing);
if (rr.getDistance(next, plus) < distBearing) {
after = before;
} else if (rr.getDistance(next, !plus) < distBearing) {
before = after;
}
boolean straight = Math.abs(MapUtils.degreesDiff(before + 180, after)) < TURN_DEGREE_MIN;
double contAngle = Math.abs(MapUtils.degreesDiff(before, after));
boolean straight = contAngle < TURN_DEGREE_MIN;
boolean isSplit = false;
if (unmatched && Math.abs(contAngle) >= UNMATCHED_TURN_DEGREE_MINIMUM) {
isSplit = true;
}
// split if needed
for (RouteSegmentResult rs : attachedRoutes) {
double diff = MapUtils.degreesDiff(before + 180, rs.getBearingBegin());
double diff = MapUtils.degreesDiff(before, rs.getBearingBegin());
if (Math.abs(diff) <= TURN_DEGREE_MIN) {
isSplit = true;
} else if (!straight && Math.abs(diff) < 100) {
} else if (!straight && Math.abs(diff) < SPLIT_TURN_DEGREE_NOT_STRAIGHT) {
isSplit = true;
}
}
@ -358,7 +374,7 @@ public class RouteResultPreparation {
private void checkAndInitRouteRegion(RoutingContext ctx, RouteDataObject road) throws IOException {
BinaryMapIndexReader reader = ctx.reverseMap.get(road.region);
if(reader != null) {
if (reader != null) {
reader.initRouteRegion(road.region);
}
}
@ -378,8 +394,8 @@ public class RouteResultPreparation {
private List<RouteSegmentResult> convertFinalSegmentToResults(RoutingContext ctx, FinalRouteSegment finalSegment) {
List<RouteSegmentResult> result = new ArrayList<RouteSegmentResult>();
if (finalSegment != null) {
ctx.routingTime = finalSegment.distanceFromStart;
println("Routing calculated time distance " + finalSegment.distanceFromStart);
ctx.routingTime += finalSegment.distanceFromStart;
// println("Routing calculated time distance " + finalSegment.distanceFromStart);
// Get results from opposite direction roads
RouteSegment segment = finalSegment.reverseWaySearch ? finalSegment :
finalSegment.opposite.getParentRoute();
@ -395,7 +411,6 @@ public class RouteResultPreparation {
}
// reverse it just to attach good direction roads
Collections.reverse(result);
segment = finalSegment.reverseWaySearch ? finalSegment.opposite.getParentRoute() : finalSegment;
int parentSegmentEnd = finalSegment.reverseWaySearch ? finalSegment.opposite.getParentSegmentEnd() : finalSegment.opposite.getSegmentStart();
parentRoutingTime = -1;
@ -481,8 +496,9 @@ public class RouteResultPreparation {
String msg = String.format("<test regions=\"\" description=\"\" best_percent=\"\" vehicle=\"%s\" \n"
+ " start_lat=\"%.5f\" start_lon=\"%.5f\" target_lat=\"%.5f\" target_lon=\"%.5f\" "
+ " routing_time=\"%.2f\" loadedTiles=\"%d\" visitedSegments=\"%d\" complete_distance=\"%.2f\" complete_time=\"%.2f\" >",
ctx.config.routerName, startLat, startLon, endLat, endLon, ctx.routingTime, ctx.loadedTiles,
ctx.visitedSegments, completeDistance, completeTime);
ctx.config.routerName, startLat, startLon, endLat, endLon, ctx.routingTime,
ctx.getLoadedTiles(),
ctx.getVisitedSegments(), completeDistance, completeTime);
// String msg = MessageFormat.format("<test regions=\"\" description=\"\" best_percent=\"\" vehicle=\"{4}\" \n"
// + " start_lat=\"{0}\" start_lon=\"{1}\" target_lat=\"{2}\" target_lon=\"{3}\" {5} >",
// startLat + "", startLon + "", endLat + "", endLon + "", ctx.config.routerName,
@ -622,7 +638,7 @@ public class RouteResultPreparation {
// calculateStatistics(result);
}
private void calculateStatistics(List<RouteSegmentResult> result) {
protected void calculateStatistics(List<RouteSegmentResult> result) {
InputStream is = RenderingRulesStorage.class.getResourceAsStream("default.render.xml");
final Map<String, String> renderingConstants = new LinkedHashMap<String, String>();
try {
@ -799,13 +815,22 @@ public class RouteResultPreparation {
}
if (dist < mergeDistance) {
mergeTurnLanes(leftside, currentSegment, nextSegment);
inferCommonActiveLane(currentSegment.getTurnType(), nextSegment.getTurnType());
TurnType turnType = currentSegment.getTurnType();
Integer[] possibleTurn = getPossibleTurnsFromActiveLanes(turnType.getLanes(), true);
if (possibleTurn.length == 1) {
TurnType tt = TurnType.valueOf(possibleTurn[0], currentSegment.getTurnType().isLeftSide());
tt.setLanes(turnType.getLanes());
tt.setSkipToSpeak(turnType.isSkipToSpeak());
currentSegment.setTurnType(tt);
turnType = tt;
}
inferCommonActiveLane(turnType, nextSegment.getTurnType());
merged = true;
}
}
if (!merged) {
TurnType tt = currentSegment.getTurnType();
inferActiveTurnLanesFromTurn(tt, TurnType.C);
inferActiveTurnLanesFromTurn(tt, tt.getValue());
}
nextSegment = currentSegment;
dist = 0;
@ -983,11 +1008,9 @@ public class RouteResultPreparation {
if(turnSet.size() == 1) {
singleTurn = turnSet.iterator().next();
} else if(currentTurn.goAhead() && turnSet.contains(nextTurn.getValue())) {
if(currentTurn.isPossibleLeftTurn() &&
TurnType.isLeftTurn(nextTurn.getValue())) {
singleTurn = nextTurn.getValue();
} else if(currentTurn.isPossibleLeftTurn() &&
TurnType.isLeftTurn(nextTurn.getActiveCommonLaneTurn())) {
if (currentTurn.isPossibleLeftTurn() && TurnType.isLeftTurn(nextTurn.getValue())) {
singleTurn = nextTurn.getValue();
} else if (currentTurn.isPossibleLeftTurn() && TurnType.isLeftTurn(nextTurn.getActiveCommonLaneTurn())) {
singleTurn = nextTurn.getActiveCommonLaneTurn();
} else if(currentTurn.isPossibleRightTurn() &&
TurnType.isRightTurn(nextTurn.getValue())) {
@ -1049,18 +1072,17 @@ public class RouteResultPreparation {
}
TurnType t = null;
if (prev != null) {
boolean noAttachedRoads = rr.getAttachedRoutes(rr.getStartPointIndex()).size() == 0;
// add description about turn
double mpi = MapUtils.degreesDiff(prev.getBearingEnd(), rr.getBearingBegin());
if(noAttachedRoads){
// TODO VICTOR : look at the comment inside direction route
// ? avoid small zigzags is covered at (search for "zigzags")
// double begin = rr.getObject().directionRoute(rr.getStartPointIndex(), rr.getStartPointIndex() <
// rr.getEndPointIndex(), 25);
// mpi = MapUtils.degreesDiff(prev.getBearingEnd(), begin);
// avoid small zigzags is covered at (search for "zigzags")
float bearingDist = RouteSegmentResult.DIST_BEARING_DETECT;
// could be || noAttachedRoads, boolean noAttachedRoads = rr.getAttachedRoutes(rr.getStartPointIndex()).size() == 0;
if (UNMATCHED_HIGHWAY_TYPE.equals(rr.getObject().getHighway())) {
bearingDist = RouteSegmentResult.DIST_BEARING_DETECT_UNMATCHED;
}
double mpi = MapUtils.degreesDiff(prev.getBearingEnd(prev.getEndPointIndex(), bearingDist),
rr.getBearingBegin(rr.getStartPointIndex(), bearingDist));
if (mpi >= TURN_DEGREE_MIN) {
if (mpi < 45) {
if (mpi < TURN_DEGREE_MIN) {
// Slight turn detection here causes many false positives where drivers would expect a "normal" TL. Best use limit-angle=TURN_DEGREE_MIN, this reduces TSL to the turn-lanes cases.
t = TurnType.valueOf(TurnType.TSLL, leftSide);
} else if (mpi < 120) {
@ -1073,7 +1095,7 @@ public class RouteResultPreparation {
int[] lanes = getTurnLanesInfo(prev, t.getValue());
t.setLanes(lanes);
} else if (mpi < -TURN_DEGREE_MIN) {
if (mpi > -45) {
if (mpi > -TURN_DEGREE_MIN) {
t = TurnType.valueOf(TurnType.TSLR, leftSide);
} else if (mpi > -120) {
t = TurnType.valueOf(TurnType.TR, leftSide);
@ -1262,23 +1284,12 @@ public class RouteResultPreparation {
}
}
t.setPossibleLeftTurn(possiblyLeftTurn);
t.setPossibleRightTurn(possiblyRightTurn);
if (rs.keepLeft || rs.keepRight) {
String[] splitLaneOptions = turnLanes.split("\\|", -1);
int activeBeginIndex = findActiveIndex(rawLanes, splitLaneOptions, rs.leftLanes, true,
rs.leftLanesInfo, rs.roadsOnLeft, rs.addRoadsOnLeft);
if(!rs.keepLeft && activeBeginIndex != -1 &&
splitLaneOptions.length > 0 && !splitLaneOptions[splitLaneOptions.length - 1].contains(";")) {
activeBeginIndex = Math.max(activeBeginIndex, 1);
}
int activeEndIndex = findActiveIndex(rawLanes, splitLaneOptions, rs.rightLanes, false,
rs.rightLanesInfo, rs.roadsOnRight, rs.addRoadsOnRight);
if(!rs.keepRight && activeEndIndex != -1 &&
splitLaneOptions.length > 0 && !splitLaneOptions[0].contains(";") ) {
activeEndIndex = Math.min(activeEndIndex, rawLanes.length - 1);
}
if (activeBeginIndex == -1 || activeEndIndex == -1 || activeBeginIndex > activeEndIndex) {
// something went wrong
return createSimpleKeepLeftRightTurn(leftSide, prevSegm, currentSegm, rs);
@ -1288,36 +1299,65 @@ public class RouteResultPreparation {
rawLanes[k] |= 1;
}
}
int tp = inferSlightTurnFromLanes(rawLanes, rs);
int tp = inferSlightTurnFromActiveLanes(rawLanes, rs.keepLeft, rs.keepRight);
// Checking to see that there is only one unique turn
if (tp != 0) {
// add extra lanes with same turn
for(int i = 0; i < rawLanes.length; i++) {
if(TurnType.getSecondaryTurn(rawLanes[i]) == tp) {
TurnType.setSecondaryToPrimary(rawLanes, i);
rawLanes[i] |= 1;
} else if(TurnType.getPrimaryTurn(rawLanes[i]) == tp) {
rawLanes[i] |= 1;
}
}
}
if (tp != t.getValue() && tp != 0) {
t = TurnType.valueOf(tp, leftSide);
} else {
if (rs.keepRight && TurnType.getSecondaryTurn(rawLanes[activeEndIndex]) == 0) {
t = TurnType.valueOf(TurnType.getPrimaryTurn(rawLanes[activeEndIndex]), leftSide);
} else if (rs.keepLeft && TurnType.getSecondaryTurn(rawLanes[activeBeginIndex]) == 0) {
t = TurnType.valueOf(TurnType.getPrimaryTurn(rawLanes[activeBeginIndex]), leftSide);
}
}
} else {
// case for go straight and identify correct turn:lane to go straight
Integer[] possibleTurns = getPossibleTurns(rawLanes, false, false);
int tp = TurnType.C;
if (possibleTurns.length == 1) {
tp = possibleTurns[0];
} else if (possibleTurns.length == 3) {
if ((!possiblyLeftTurn || !possiblyRightTurn) && TurnType.isSlightTurn(possibleTurns[1])) {
tp = possibleTurns[1];
t = TurnType.valueOf(tp, leftSide);
}
}
for (int k = 0; k < rawLanes.length; k++) {
int turn = TurnType.getPrimaryTurn(rawLanes[k]);
int sturn = TurnType.getSecondaryTurn(rawLanes[k]);
int tturn = TurnType.getTertiaryTurn(rawLanes[k]);
boolean active = false;
// some turns go through many segments (to turn right or left)
// so on one first segment the lane could be available and may be only 1 possible
// all undesired lanes will be disabled through the 2nd pass
if((TurnType.isRightTurn(sturn) && possiblyRightTurn) ||
(TurnType.isLeftTurn(sturn) && possiblyLeftTurn)) {
// we can't predict here whether it will be a left turn or straight on,
if ((TurnType.isRightTurn(sturn) && possiblyRightTurn)
|| (TurnType.isLeftTurn(sturn) && possiblyLeftTurn)) {
// we can't predict here whether it will be a left turn or straight on,
// it could be done during 2nd pass
TurnType.setPrimaryTurn(rawLanes, k, sturn);
TurnType.setSecondaryTurn(rawLanes, k, turn);
TurnType.setSecondaryToPrimary(rawLanes, k);
active = true;
} else if((TurnType.isRightTurn(tturn) && possiblyRightTurn) ||
(TurnType.isLeftTurn(tturn) && possiblyLeftTurn)) {
TurnType.setPrimaryTurn(rawLanes, k, tturn);
TurnType.setTertiaryTurn(rawLanes, k, turn);
} else if ((TurnType.isRightTurn(tturn) && possiblyRightTurn)
|| (TurnType.isLeftTurn(tturn) && possiblyLeftTurn)) {
TurnType.setTertiaryToPrimary(rawLanes, k);
active = true;
} else if((TurnType.isRightTurn(turn) && possiblyRightTurn) ||
(TurnType.isLeftTurn(turn) && possiblyLeftTurn)) {
} else if ((TurnType.isRightTurn(turn) && possiblyRightTurn)
|| (TurnType.isLeftTurn(turn) && possiblyLeftTurn)) {
active = true;
} else if (turn == TurnType.C) {
// } else if (turn == TurnType.C) {
} else if (turn == tp) {
active = true;
}
if (active) {
@ -1327,6 +1367,8 @@ public class RouteResultPreparation {
}
t.setSkipToSpeak(!rs.speak);
t.setLanes(rawLanes);
t.setPossibleLeftTurn(possiblyLeftTurn);
t.setPossibleRightTurn(possiblyRightTurn);
return t;
}
@ -1435,32 +1477,39 @@ public class RouteResultPreparation {
protected TurnType createSimpleKeepLeftRightTurn(boolean leftSide, RouteSegmentResult prevSegm,
RouteSegmentResult currentSegm, RoadSplitStructure rs) {
double devation = Math.abs(MapUtils.degreesDiff(prevSegm.getBearingEnd(), currentSegm.getBearingBegin()));
boolean makeSlightTurn = devation > 5 && (!isMotorway(prevSegm) || !isMotorway(currentSegm));
TurnType t = null;
int laneType = TurnType.C;
if (rs.keepLeft && rs.keepRight) {
t = TurnType.valueOf(TurnType.C, leftSide);
} else if (rs.keepLeft) {
t = TurnType.valueOf(makeSlightTurn ? TurnType.TSLL : TurnType.KL, leftSide);
if (makeSlightTurn) {
laneType = TurnType.TSLL;
}
} else if (rs.keepRight) {
t = TurnType.valueOf(makeSlightTurn ? TurnType.TSLR : TurnType.KR, leftSide);
if (makeSlightTurn) {
laneType = TurnType.TSLR;
}
} else {
return null;
}
int current = countLanesMinOne(currentSegm);
int ls = current + rs.leftLanes + rs.rightLanes;
int[] lanes = new int[ls];
for (int it = 0; it < ls; it++) {
if (it < rs.leftLanes || it >= rs.leftLanes + current) {
lanes[it] = 0;
lanes[it] = TurnType.C << 1;
} else {
lanes[it] = 1;
lanes[it] = (laneType << 1) + 1;
}
}
// sometimes links are
if ((current <= rs.leftLanes + rs.rightLanes) && (rs.leftLanes > 1 || rs.rightLanes > 1)) {
rs.speak = true;
}
double devation = Math.abs(MapUtils.degreesDiff(prevSegm.getBearingEnd(), currentSegm.getBearingBegin()));
boolean makeSlightTurn = devation > 5 && (!isMotorway(prevSegm) || !isMotorway(currentSegm));
TurnType t = null;
if (rs.keepLeft && rs.keepRight) {
t = TurnType.valueOf(TurnType.C, leftSide);
} else if (rs.keepLeft) {
t = TurnType.valueOf(makeSlightTurn ? TurnType.TSLL : TurnType.KL, leftSide);
} else if (rs.keepRight) {
t = TurnType.valueOf(makeSlightTurn ? TurnType.TSLR : TurnType.KR, leftSide);
} else {
return t;
}
t.setSkipToSpeak(!rs.speak);
t.setLanes(lanes);
return t;
@ -1569,10 +1618,8 @@ public class RouteResultPreparation {
int[] lanes = new int[splitLaneOptions.length];
for (int i = 0; i < splitLaneOptions.length; i++) {
String[] laneOptions = splitLaneOptions[i].split(";");
boolean isTertiaryTurn = false;
for (int j = 0; j < laneOptions.length; j++) {
int turn = TurnType.convertType(laneOptions[j]);
final int primary = TurnType.getPrimaryTurn(lanes[i]);
if (primary == 0) {
TurnType.setPrimaryTurnAndReset(lanes, i, turn);
@ -1582,12 +1629,12 @@ public class RouteResultPreparation {
(TurnType.isLeftTurn(calcTurnType) && TurnType.isLeftTurn(turn))
) {
TurnType.setPrimaryTurnShiftOthers(lanes, i, turn);
} else if (!isTertiaryTurn) {
TurnType.setSecondaryTurnShiftOthers(lanes, i, turn);
isTertiaryTurn = true;
} else {
} else if (TurnType.getSecondaryTurn(lanes[i]) == 0) {
TurnType.setSecondaryTurn(lanes, i, turn);
} else if (TurnType.getTertiaryTurn(lanes[i]) == 0) {
TurnType.setTertiaryTurn(lanes, i, turn);
break;
} else {
// ignore
}
}
}
@ -1595,96 +1642,90 @@ public class RouteResultPreparation {
return lanes;
}
private int inferSlightTurnFromLanes(int[] oLanes, RoadSplitStructure rs) {
TIntHashSet possibleTurns = new TIntHashSet();
for (int i = 0; i < oLanes.length; i++) {
if ((oLanes[i] & 1) == 0) {
continue;
}
if (possibleTurns.isEmpty()) {
// Nothing is in the list to compare to, so add the first elements
possibleTurns.add(TurnType.getPrimaryTurn(oLanes[i]));
if (TurnType.getSecondaryTurn(oLanes[i]) != 0) {
possibleTurns.add(TurnType.getSecondaryTurn(oLanes[i]));
}
if (TurnType.getTertiaryTurn(oLanes[i]) != 0) {
possibleTurns.add(TurnType.getTertiaryTurn(oLanes[i]));
}
} else {
TIntArrayList laneTurns = new TIntArrayList();
laneTurns.add(TurnType.getPrimaryTurn(oLanes[i]));
if (TurnType.getSecondaryTurn(oLanes[i]) != 0) {
laneTurns.add(TurnType.getSecondaryTurn(oLanes[i]));
}
if (TurnType.getTertiaryTurn(oLanes[i]) != 0) {
laneTurns.add(TurnType.getTertiaryTurn(oLanes[i]));
}
possibleTurns.retainAll(laneTurns);
if (possibleTurns.isEmpty()) {
// No common turns, so can't determine anything.
return 0;
}
}
private int inferSlightTurnFromActiveLanes(int[] oLanes, boolean mostLeft, boolean mostRight) {
Integer[] possibleTurns = getPossibleTurns(oLanes, false, true);
if (possibleTurns.length == 0) {
// No common turns, so can't determine anything.
return 0;
}
// Remove all turns from lanes not selected...because those aren't it
for (int i = 0; i < oLanes.length; i++) {
if ((oLanes[i] & 1) == 0 && !possibleTurns.isEmpty()) {
possibleTurns.remove((Integer) TurnType.getPrimaryTurn(oLanes[i]));
if (TurnType.getSecondaryTurn(oLanes[i]) != 0) {
possibleTurns.remove((Integer) TurnType.getSecondaryTurn(oLanes[i]));
}
if (TurnType.getTertiaryTurn(oLanes[i]) != 0) {
possibleTurns.remove((Integer) TurnType.getTertiaryTurn(oLanes[i]));
}
}
}
// remove all non-slight turns // TEST don't pass
// if(possibleTurns.size() > 1) {
// TIntIterator it = possibleTurns.iterator();
// while(it.hasNext()) {
// int nxt = it.next();
// if(!TurnType.isSlightTurn(nxt)) {
// it.remove();
// }
// }
// }
int infer = 0;
if (possibleTurns.size() == 1) {
infer = possibleTurns.iterator().next();
} else if (possibleTurns.size() > 1) {
if (rs.keepLeft && rs.keepRight && possibleTurns.contains(TurnType.C)) {
infer = TurnType.C;
} else if (rs.keepLeft || rs.keepRight) {
TIntIterator it = possibleTurns.iterator();
infer = it.next();
while(it.hasNext()) {
int next = it.next();
int orderInfer = TurnType.orderFromLeftToRight(infer);
int orderNext = TurnType.orderFromLeftToRight(next) ;
if(rs.keepLeft && orderNext < orderInfer) {
infer = next;
} else if(rs.keepRight && orderNext > orderInfer) {
infer = next;
if (possibleTurns.length == 1) {
infer = possibleTurns[0];
} else {
if (mostLeft && !mostRight) {
infer = possibleTurns[0];
} else if(mostRight && !mostLeft) {
infer = possibleTurns[possibleTurns.length - 1];
} else {
infer = possibleTurns[1];
// infer = TurnType.C;
}
}
return infer;
}
private Integer[] getPossibleTurnsFromActiveLanes(int[] oLanes, boolean onlyPrimary) {
return getPossibleTurns(oLanes, onlyPrimary, true);
}
private Integer[] getPossibleTurns(int[] oLanes, boolean onlyPrimary, boolean uniqueFromActive) {
Set<Integer> possibleTurns = new LinkedHashSet<>();
Set<Integer> upossibleTurns = new LinkedHashSet<>();
for (int i = 0; i < oLanes.length; i++) {
// Nothing is in the list to compare to, so add the first elements
upossibleTurns.clear();
upossibleTurns.add(TurnType.getPrimaryTurn(oLanes[i]));
if (!onlyPrimary && TurnType.getSecondaryTurn(oLanes[i]) != 0) {
upossibleTurns.add(TurnType.getSecondaryTurn(oLanes[i]));
}
if (!onlyPrimary && TurnType.getTertiaryTurn(oLanes[i]) != 0) {
upossibleTurns.add(TurnType.getTertiaryTurn(oLanes[i]));
}
if (!uniqueFromActive) {
possibleTurns.addAll(upossibleTurns);
// if (!possibleTurns.isEmpty()) {
// possibleTurns.retainAll(upossibleTurns);
// if(possibleTurns.isEmpty()) {
// break;
// }
// } else {
// possibleTurns.addAll(upossibleTurns);
// }
} else if ((oLanes[i] & 1) == 1) {
if (!possibleTurns.isEmpty()) {
possibleTurns.retainAll(upossibleTurns);
if(possibleTurns.isEmpty()) {
break;
}
} else {
possibleTurns.addAll(upossibleTurns);
}
}
}
// Remove all turns from lanes not selected...because those aren't it
if (uniqueFromActive) {
for (int i = 0; i < oLanes.length; i++) {
if ((oLanes[i] & 1) == 0) {
possibleTurns.remove((Integer) TurnType.getPrimaryTurn(oLanes[i]));
if (TurnType.getSecondaryTurn(oLanes[i]) != 0) {
possibleTurns.remove((Integer) TurnType.getSecondaryTurn(oLanes[i]));
}
if (TurnType.getTertiaryTurn(oLanes[i]) != 0) {
possibleTurns.remove((Integer) TurnType.getTertiaryTurn(oLanes[i]));
}
}
}
}
Integer[] array = possibleTurns.toArray(new Integer[possibleTurns.size()]);
Arrays.sort(array, new Comparator<Integer>() {
// Checking to see that there is only one unique turn
if (infer != 0) {
for(int i = 0; i < oLanes.length; i++) {
if(TurnType.getSecondaryTurn(oLanes[i]) == infer) {
int pt = TurnType.getPrimaryTurn(oLanes[i]);
int en = oLanes[i] & 1;
TurnType.setPrimaryTurnAndReset(oLanes, i, infer);
oLanes[i] |= en;
TurnType.setSecondaryTurn(oLanes, i, pt);
}
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(TurnType.orderFromLeftToRight(o1), TurnType.orderFromLeftToRight(o2));
}
}
return infer;
});
return array;
}
private boolean isMotorway(RouteSegmentResult s){
@ -1721,7 +1762,7 @@ public class RouteResultPreparation {
}
}
Iterator<RouteSegment> it;
if(rr.getPreAttachedRoutes(pointInd) != null) {
if (rr.getPreAttachedRoutes(pointInd) != null) {
final RouteSegmentResult[] list = rr.getPreAttachedRoutes(pointInd);
it = new Iterator<BinaryRoutePlanner.RouteSegment>() {
int i = 0;
@ -1740,7 +1781,7 @@ public class RouteResultPreparation {
public void remove() {
}
};
} else if (recalculation) {
} else if (recalculation || ctx.nativeLib == null) {
RouteSegment rt = ctx.loadRouteSegment(road.getPoint31XTile(pointInd), road.getPoint31YTile(pointInd), ctx.config.memoryLimitation);
it = rt == null ? null : rt.getIterator();
} else {
@ -1753,7 +1794,7 @@ public class RouteResultPreparation {
if (routeSegment.road.getId() != road.getId() && routeSegment.road.getId() != previousRoadId) {
RouteDataObject addRoad = routeSegment.road;
checkAndInitRouteRegion(ctx, addRoad);
// TODO restrictions can be considered as well
// Future: restrictions can be considered as well
int oneWay = ctx.getRouter().isOneWay(addRoad);
if (oneWay >= 0 && routeSegment.getSegmentStart() < addRoad.getPointsLength() - 1) {
long pointL = getPoint(addRoad, routeSegment.getSegmentStart() + 1);

View file

@ -21,7 +21,12 @@ import gnu.trove.map.hash.TIntObjectHashMap;
public class RouteSegmentResult implements StringExternalizable<RouteDataBundle> {
private final RouteDataObject object;
// this should be bigger (50-80m) but tests need to be fixed first
public static final float DIST_BEARING_DETECT = 5;
public static final float DIST_BEARING_DETECT_UNMATCHED = 50;
private RouteDataObject object;
private int startPointIndex;
private int endPointIndex;
private List<RouteSegmentResult>[] attachedRoutes;
@ -266,7 +271,7 @@ public class RouteSegmentResult implements StringExternalizable<RouteDataBundle>
bundle.putString("turnLanes", TurnType.lanesToString(turnLanes));
}
}
bundle.putLong("id", object.id);
bundle.putLong("id", object.id >> 6); // OsmAnd ID to OSM ID
bundle.putArray("types", convertTypes(object.types, rules));
int start = Math.min(startPointIndex, endPointIndex);
@ -322,19 +327,21 @@ public class RouteSegmentResult implements StringExternalizable<RouteDataBundle>
Location prevLocation = null;
for (int i = 0; i < length; i++) {
Location location = resources.getLocation(index);
double dist = 0;
if (prevLocation != null) {
dist = MapUtils.getDistance(prevLocation.getLatitude(), prevLocation.getLongitude(), location.getLatitude(), location.getLongitude());
distance += dist;
}
prevLocation = location;
object.pointsX[i] = MapUtils.get31TileNumberX(location.getLongitude());
object.pointsY[i] = MapUtils.get31TileNumberY(location.getLatitude());
if (location.hasAltitude() && object.heightDistanceArray.length > 0) {
object.heightDistanceArray[i * 2] = (float) dist;
object.heightDistanceArray[i * 2 + 1] = (float) location.getAltitude();
} else {
object.heightDistanceArray = new float[0];
if (location != null) {
double dist = 0;
if (prevLocation != null) {
dist = MapUtils.getDistance(prevLocation.getLatitude(), prevLocation.getLongitude(), location.getLatitude(), location.getLongitude());
distance += dist;
}
prevLocation = location;
object.pointsX[i] = MapUtils.get31TileNumberX(location.getLongitude());
object.pointsY[i] = MapUtils.get31TileNumberY(location.getLatitude());
if (location.hasAltitude() && object.heightDistanceArray.length > 0) {
object.heightDistanceArray[i * 2] = (float) dist;
object.heightDistanceArray[i * 2 + 1] = (float) location.getAltitude();
} else {
object.heightDistanceArray = new float[0];
}
}
if (plus) {
index++;
@ -403,7 +410,7 @@ public class RouteSegmentResult implements StringExternalizable<RouteDataBundle>
}
public void copyPreattachedRoutes(RouteSegmentResult toCopy, int shift) {
if(toCopy.preAttachedRoutes != null) {
if (toCopy.preAttachedRoutes != null) {
int l = toCopy.preAttachedRoutes.length - shift;
preAttachedRoutes = new RouteSegmentResult[l][];
System.arraycopy(toCopy.preAttachedRoutes, shift, preAttachedRoutes, 0, l);
@ -412,7 +419,7 @@ public class RouteSegmentResult implements StringExternalizable<RouteDataBundle>
public RouteSegmentResult[] getPreAttachedRoutes(int routeInd) {
int st = Math.abs(routeInd - startPointIndex);
if(preAttachedRoutes != null && st < preAttachedRoutes.length) {
if (preAttachedRoutes != null && st < preAttachedRoutes.length) {
return preAttachedRoutes[st];
}
return null;
@ -421,7 +428,7 @@ public class RouteSegmentResult implements StringExternalizable<RouteDataBundle>
public List<RouteSegmentResult> getAttachedRoutes(int routeInd) {
int st = Math.abs(routeInd - startPointIndex);
List<RouteSegmentResult> list = attachedRoutes[st];
if(list == null) {
if (list == null) {
return Collections.emptyList();
}
return list;
@ -444,19 +451,32 @@ public class RouteSegmentResult implements StringExternalizable<RouteDataBundle>
}
public float getBearingBegin() {
return (float) (object.directionRoute(startPointIndex, startPointIndex < endPointIndex) / Math.PI * 180);
return getBearingBegin(startPointIndex, DIST_BEARING_DETECT);
}
public float getBearing(int point, boolean plus) {
return (float) (object.directionRoute(point, plus) / Math.PI * 180);
}
public float getDistance(int point, boolean plus) {
return (float) (plus? object.distance(point, endPointIndex): object.distance(startPointIndex, point));
public float getBearingBegin(int point, float dist) {
return getBearing(point, true, dist);
}
public float getBearingEnd() {
return (float) (MapUtils.alignAngleDifference(object.directionRoute(endPointIndex, startPointIndex > endPointIndex) - Math.PI) / Math.PI * 180);
return getBearingEnd(endPointIndex, DIST_BEARING_DETECT);
}
public float getBearingEnd(int point, float dist) {
return getBearing(point, false, dist);
}
public float getBearing(int point, boolean begin, float dist) {
if (begin) {
return (float) (object.directionRoute(point, startPointIndex < endPointIndex, dist) / Math.PI * 180);
} else {
double dr = object.directionRoute(point, startPointIndex > endPointIndex, dist);
return (float) (MapUtils.alignAngleDifference(dr - Math.PI) / Math.PI * 180);
}
}
public float getDistance(int point, boolean plus) {
return (float) (plus ? object.distance(point, endPointIndex) : object.distance(startPointIndex, point));
}
public void setSegmentTime(float segmentTime) {
@ -495,7 +515,7 @@ public class RouteSegmentResult implements StringExternalizable<RouteDataBundle>
return endPointIndex - startPointIndex > 0;
}
private LatLon convertPoint(RouteDataObject o, int ind){
return new LatLon(MapUtils.get31LatitudeY(o.getPoint31YTile(ind)), MapUtils.get31LongitudeX(o.getPoint31XTile(ind)));
}
@ -533,10 +553,16 @@ public class RouteSegmentResult implements StringExternalizable<RouteDataBundle>
public void setDescription(String description) {
this.description = description;
}
public void setObject(RouteDataObject r) {
this.object = r;
}
@Override
public String toString() {
return object.toString() + ": " + startPointIndex + "-" + endPointIndex;
}
}

View file

@ -36,12 +36,10 @@ import net.osmand.router.RoutePlannerFrontEnd.RouteCalculationMode;
public class RoutingContext {
public static final boolean SHOW_GC_SIZE = false;
public static boolean SHOW_GC_SIZE = false;
private final static Log log = PlatformUtil.getLog(RoutingContext.class);
// Final context variables
public final RoutingConfiguration config;
@ -50,23 +48,31 @@ public class RoutingContext {
public final Map<BinaryMapIndexReader, List<RouteSubregion>> map = new LinkedHashMap<BinaryMapIndexReader, List<RouteSubregion>>();
public final Map<RouteRegion, BinaryMapIndexReader> reverseMap = new LinkedHashMap<RouteRegion, BinaryMapIndexReader>();
// 0. Reference to native routingcontext for multiple routes
public long nativeRoutingContext;
public boolean keepNativeRoutingContext;
// 1. Initial variables
public int startX;
public int startY;
public long startRoadId;
public int startSegmentInd;
public boolean startTransportStop;
public int targetX;
public int targetY;
public long targetRoadId;
public int targetSegmentInd;
public boolean targetTransportStop;
public boolean publicTransport;
// deprecated
public long firstRoadId;
public int firstRoadDirection;
public RouteCalculationProgress calculationProgress;
public boolean leftSideNavigation;
public List<RouteSegmentResult> previouslyCalculatedRoute;
public PrecalculatedRouteDirection precalculatedRouteDirection;
// 2. Routing memory cache (big objects)
TLongObjectHashMap<List<RoutingSubregionTile>> indexedSubregions = new TLongObjectHashMap<List<RoutingSubregionTile>>();
@ -82,27 +88,15 @@ public class RoutingContext {
public TileStatistics global = new TileStatistics();
// updated by route planner in bytes
public int memoryOverhead = 0;
long timeNanoToCalcDeviation = 0;
long timeToLoad = 0;
long timeToLoadHeaders = 0;
long timeToFindInitialSegments = 0;
public long timeToCalculate = 0;
int distinctLoadedTiles = 0;
int maxLoadedTiles = 0;
int loadedPrevUnloadedTiles = 0;
int unloadedTiles = 0;
public float routingTime = 0;
public int loadedTiles = 0;
public int visitedSegments = 0;
public int relaxedSegments = 0;
// callback of processing segments
RouteSegmentVisitor visitor = null;
// old planner
public FinalRouteSegment finalRouteSegment;
RoutingContext(RoutingContext cp) {
this.config = cp.config;
@ -201,12 +195,16 @@ public class RoutingContext {
public void initStartAndTargetPoints(RouteSegment start, RouteSegment end) {
initTargetPoint(end);
startX = start.road.getPoint31XTile(start.getSegmentStart());
startY = start.road.getPoint31YTile(start.getSegmentStart());
startY = start.road.getPoint31YTile(start.getSegmentStart());
startRoadId = start.road.getId();
startSegmentInd = start.getSegmentStart();
}
public void initTargetPoint(RouteSegment end) {
targetX = end.road.getPoint31XTile(end.getSegmentStart());
targetY = end.road.getPoint31YTile(end.getSegmentStart());
targetRoadId = end.road.getId();
targetSegmentInd = end.getSegmentStart();
}
public void unloadAllData() {
@ -218,7 +216,9 @@ public class RoutingContext {
if (tl.isLoaded()) {
if(except == null || except.searchSubregionTile(tl.subregion) < 0){
tl.unload();
unloadedTiles ++;
if(calculationProgress != null) {
calculationProgress.unloadedTiles ++;
}
global.size -= tl.tileStatistics.size;
}
}
@ -252,18 +252,6 @@ public class RoutingContext {
return ind;
}
public void newRoutingPoints() {
int middleX = startX / 2 + targetX / 2;
int middleY = startY / 2 + targetY;
List<RouteDataObject> dataObjects = new ArrayList<RouteDataObject>();
loadTileData(middleX, middleY, 17, dataObjects);
System.out.println("Size of data objects " + dataObjects.size());
}
public RouteSegment loadRouteSegment(int x31, int y31, long memoryLimit) {
long tileId = getRoutingTile(x31, y31, memoryLimit);
@ -301,7 +289,8 @@ public class RoutingContext {
if(excludeNotAllowed != null && !excludeNotAllowed.contains(ro.getId())) {
ts.add(ro);
}
} else if(excludeNotAllowed != null && ro.getId() > 0){
}
if(excludeNotAllowed != null && ro.getId() > 0){
excludeNotAllowed.add(ro.getId());
if(ts.excludedIds == null ){
ts.excludedIds = new TLongHashSet();
@ -314,27 +303,37 @@ public class RoutingContext {
} catch (IOException e) {
throw new RuntimeException("Loading data exception", e);
}
timeToLoad += (System.nanoTime() - now);
if (calculationProgress != null) {
calculationProgress.timeToLoad += (System.nanoTime() - now);
}
} else {
long now = System.nanoTime();
NativeRouteSearchResult ns = nativeLib.loadRouteRegion(ts.subregion, loadObjectsInMemory);
// System.out.println(ts.subregion.shiftToData + " " + Arrays.toString(ns.objects));
ts.setLoadedNative(ns, this);
timeToLoad += (System.nanoTime() - now);
if (calculationProgress != null) {
calculationProgress.timeToLoad += (System.nanoTime() - now);
}
}
loadedTiles++;
if (calculationProgress != null) {
calculationProgress.loadedTiles++;
}
if (wasUnloaded) {
if(ucount == 1) {
loadedPrevUnloadedTiles++;
if(calculationProgress != null) {
calculationProgress.loadedPrevUnloadedTiles++;
}
}
} else {
if(global != null) {
global.allRoutes += ts.tileStatistics.allRoutes;
global.coordinates += ts.tileStatistics.coordinates;
}
distinctLoadedTiles++;
if (calculationProgress != null) {
calculationProgress.distinctLoadedTiles++;
}
}
global.size += ts.tileStatistics.size;
}
@ -408,7 +407,9 @@ public class RoutingContext {
}
collection.add(found);
}
timeToLoadHeaders += (System.nanoTime() - now);
if (calculationProgress != null) {
calculationProgress.timeToLoadHeaders += (System.nanoTime() - now);
}
}
} catch (IOException e) {
throw new RuntimeException("Loading data exception", e);
@ -418,6 +419,10 @@ public class RoutingContext {
}
public void loadTileData(int x31, int y31, int zoomAround, final List<RouteDataObject> toFillIn) {
loadTileData(x31, y31, zoomAround, toFillIn, false);
}
public void loadTileData(int x31, int y31, int zoomAround, final List<RouteDataObject> toFillIn, boolean allowDuplications) {
int t = config.ZOOM_TO_LOAD_TILES - zoomAround;
int coordinatesShift = (1 << (31 - config.ZOOM_TO_LOAD_TILES));
if(t <= 0) {
@ -428,7 +433,6 @@ public class RoutingContext {
}
TLongHashSet ts = new TLongHashSet();
long now = System.nanoTime();
for(int i = -t; i <= t; i++) {
for(int j = -t; j <= t; j++) {
ts.add(getRoutingTile(x31 +i*coordinatesShift, y31 + j*coordinatesShift, 0));
@ -436,10 +440,12 @@ public class RoutingContext {
}
TLongIterator it = ts.iterator();
TLongObjectHashMap<RouteDataObject> excludeDuplications = new TLongObjectHashMap<RouteDataObject>();
while(it.hasNext()){
while (it.hasNext()) {
getAllObjects(it.next(), toFillIn, excludeDuplications);
if (allowDuplications) {
excludeDuplications.clear();
}
}
timeToFindInitialSegments += (System.nanoTime() - now);
}
@SuppressWarnings("unused")
@ -467,15 +473,14 @@ public class RoutingContext {
long h2 = runGCUsedMemory();
float mb = (1 << 20);
log.warn("Unload tiles : estimated " + (sz1 - sz2) / mb + " ?= " + (h1 - h2) / mb + " actual");
log.warn("Used after " + h2 / mb + " of " + Runtime.getRuntime().totalMemory() / mb + " max "
+ maxMemory() / mb);
log.warn("Used after " + h2 / mb + " of " + Runtime.getRuntime().totalMemory() / mb );
} else {
float mb = (1 << 20);
int sz2 = getCurrentEstimatedSize();
log.warn("Unload tiles : occupied before " + sz1 / mb + " Mb - now " + sz2 / mb + "MB "
+ memoryLimit / mb + " limit MB " + config.memoryLimitation / mb);
long us2 = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
log.warn("Used memory before " + us1 / mb + "after " + us1 / mb + " of max " + maxMemory() / mb);
log.warn("Used memory before " + us1 / mb + "after " + us1 / mb );
}
}
if (!indexedSubregions.containsKey(tileId)) {
@ -507,12 +512,6 @@ public class RoutingContext {
return tileId;
}
private long maxMemory() {
// AVIAN FIXME
// return Runtime.getRuntime().maxMemory();
return 0;
}
public boolean checkIfMemoryLimitCritical(long memoryLimit) {
@ -529,7 +528,9 @@ public class RoutingContext {
loaded++;
}
}
maxLoadedTiles = Math.max(maxLoadedTiles, getCurrentlyLoadedTiles());
if(calculationProgress != null) {
calculationProgress.maxLoadedTiles = Math.max(calculationProgress.maxLoadedTiles, getCurrentlyLoadedTiles());
}
Collections.sort(list, new Comparator<RoutingSubregionTile>() {
private int pow(int base, int pw) {
int r = 1;
@ -551,7 +552,9 @@ public class RoutingContext {
i++;
// System.out.println("Unload " + unload);
unload.unload();
unloadedTiles ++;
if(calculationProgress != null) {
calculationProgress.unloadedTiles ++;
}
global.size -= unload.tileStatistics.size;
// tile could be cleaned from routing tiles and deleted from whole list
@ -802,7 +805,31 @@ public class RoutingContext {
return map.keySet().toArray(new BinaryMapIndexReader[map.size()]);
}
public int getVisitedSegments() {
if(calculationProgress != null) {
return calculationProgress.visitedSegments;
}
return 0;
}
public int getLoadedTiles() {
if (calculationProgress != null) {
return calculationProgress.loadedTiles;
}
return 0;
}
public synchronized void deleteNativeRoutingContext() {
if (nativeRoutingContext != 0) {
NativeLibrary.deleteNativeRoutingContext(nativeRoutingContext);
}
nativeRoutingContext = 0;
}
@Override
protected void finalize() throws Throwable {
deleteNativeRoutingContext();
super.finalize();
}
}

View file

@ -236,13 +236,13 @@ public class TestRouting {
throw new IllegalArgumentException(MessageFormat.format("Complete routing time (expected) {0} != {1} (original) : {2}", routing_time, calcRoutingTime, testDescription));
}
if (visitedSegments > 0 && !isInOrLess(visitedSegments, ctx.visitedSegments, percent)) {
if (visitedSegments > 0 && !isInOrLess(visitedSegments, ctx.getVisitedSegments(), percent)) {
throw new IllegalArgumentException(MessageFormat.format("Visited segments (expected) {0} != {1} (original) : {2}", visitedSegments,
ctx.visitedSegments, testDescription));
ctx.getVisitedSegments(), testDescription));
}
if (loadedTiles > 0 && !isInOrLess(loadedTiles, ctx.loadedTiles, percent)) {
if (loadedTiles > 0 && !isInOrLess(loadedTiles, ctx.getLoadedTiles(), percent)) {
throw new IllegalArgumentException(MessageFormat.format("Loaded tiles (expected) {0} != {1} (original) : {2}", loadedTiles,
ctx.loadedTiles, testDescription));
ctx.getLoadedTiles(), testDescription));
}
if(TEST_BOTH_DIRECTION){

View file

@ -2,26 +2,16 @@ package net.osmand.router;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Locale;
import java.util.PriorityQueue;
import gnu.trove.iterator.TIntIterator;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import net.osmand.NativeLibrary;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.data.IncompleteTransportRoute;
import net.osmand.data.LatLon;
import net.osmand.data.QuadRect;
import net.osmand.data.TransportRoute;
@ -35,7 +25,7 @@ import net.osmand.util.MapUtils;
public class TransportRoutePlanner {
private static final boolean MEASURE_TIME = false;
private static final int MISSING_STOP_SEARCH_RADIUS = 15000;
private static final int MIN_DIST_STOP_TO_GEOMETRY = 150;
public static final long GEOMETRY_WAY_ID = -1;
public static final long STOPS_WAY_ID = -2;
@ -220,11 +210,10 @@ public class TransportRoutePlanner {
}
}
private List<TransportRouteResult> prepareResults(TransportRoutingContext ctx, List<TransportRouteSegment> results) {
Collections.sort(results, new SegmentsComparator(ctx));
List<TransportRouteResult> lst = new ArrayList<TransportRouteResult>();
System.out.println(String.format("Calculated %.1f seconds, found %d results, visited %d routes / %d stops, loaded %d tiles (%d ms read, %d ms total), loaded ways %d (%d wrong)",
System.out.println(String.format(Locale.US, "Calculated %.1f seconds, found %d results, visited %d routes / %d stops, loaded %d tiles (%d ms read, %d ms total), loaded ways %d (%d wrong)",
(System.currentTimeMillis() - ctx.startCalcTime) / 1000.0, results.size(),
ctx.visitedRoutesCount, ctx.visitedStops,
ctx.quadTree.size(), ctx.readTime / (1000 * 1000), ctx.loadTime / (1000 * 1000),
@ -310,7 +299,6 @@ public class TransportRoutePlanner {
}
}
public static class TransportRouteResultSegment {
private static final boolean DISPLAY_FULL_SEGMENT_ROUTE = false;
@ -440,7 +428,7 @@ public class TransportRoutePlanner {
Node ln = startInd.way.getLastNode();
Node fn = endInd.way.getFirstNode();
// HERE we need to check other ways for continuation
if (ln != null && fn != null && MapUtils.getDistance(ln.getLatLon(), fn.getLatLon()) < MISSING_STOP_SEARCH_RADIUS) {
if (ln != null && fn != null && MapUtils.getDistance(ln.getLatLon(), fn.getLatLon()) < TransportStopsRouteReader.MISSING_STOP_SEARCH_RADIUS) {
validContinuation = true;
} else {
validContinuation = false;
@ -482,157 +470,13 @@ public class TransportRoutePlanner {
return route.getForwardStops().get(i);
}
}
public static class TransportRouteResult {
List<TransportRouteResultSegment> segments = new ArrayList<TransportRouteResultSegment>(4);
double finishWalkDist;
double routeTime;
private final TransportRoutingConfiguration cfg;
public TransportRouteResult(TransportRoutingContext ctx) {
cfg = ctx.cfg;
}
public TransportRouteResult(TransportRoutingConfiguration cfg) {
this.cfg = cfg;
}
public List<TransportRouteResultSegment> getSegments() {
return segments;
}
public void setFinishWalkDist(double finishWalkDist) {
this.finishWalkDist = finishWalkDist;
}
public void setRouteTime(double routeTime) {
this.routeTime = routeTime;
}
public void addSegment(TransportRouteResultSegment seg) {
segments.add(seg);
}
public double getWalkDist() {
double d = finishWalkDist;
for (TransportRouteResultSegment s : segments) {
d += s.walkDist;
}
return d;
}
public double getFinishWalkDist() {
return finishWalkDist;
}
public double getWalkSpeed() {
return cfg.walkSpeed;
}
public double getRouteTime() {
return routeTime;
}
public int getStops() {
int stops = 0;
for(TransportRouteResultSegment s : segments) {
stops += (s.end - s.start);
}
return stops;
}
public boolean isRouteStop(TransportStop stop) {
for(TransportRouteResultSegment s : segments) {
if (s.getTravelStops().contains(stop)) {
return true;
}
}
return false;
}
public TransportRouteResultSegment getRouteStopSegment(TransportStop stop) {
for(TransportRouteResultSegment s : segments) {
if (s.getTravelStops().contains(stop)) {
return s;
}
}
return null;
}
public double getTravelDist() {
double d = 0;
for (TransportRouteResultSegment s : segments) {
d += s.getTravelDist();
}
return d;
}
public double getTravelTime() {
double t = 0;
for (TransportRouteResultSegment s : segments) {
if (cfg.useSchedule) {
TransportSchedule sts = s.route.getSchedule();
for (int k = s.start; k < s.end; k++) {
t += sts.getAvgStopIntervals()[k] * 10;
}
} else {
t += cfg.getBoardingTime();
t += s.getTravelTime();
}
}
return t;
}
public double getWalkTime() {
return getWalkDist() / cfg.walkSpeed;
}
public double getChangeTime() {
return cfg.getChangeTime();
}
public double getBoardingTime() {
return cfg.getBoardingTime();
}
public int getChanges() {
return segments.size() - 1;
}
@Override
public String toString() {
StringBuilder bld = new StringBuilder();
bld.append(String.format("Route %d stops, %d changes, %.2f min: %.2f m (%.1f min) to walk, %.2f m (%.1f min) to travel\n",
getStops(), getChanges(), routeTime / 60, getWalkDist(), getWalkTime() / 60.0,
getTravelDist(), getTravelTime() / 60.0));
for(int i = 0; i < segments.size(); i++) {
TransportRouteResultSegment s = segments.get(i);
String time = "";
String arriveTime = "";
if(s.depTime != -1) {
time = String.format("at %s", formatTransporTime(s.depTime));
}
int aTime = s.getArrivalTime();
if(aTime != -1) {
arriveTime = String.format("and arrive at %s", formatTransporTime(aTime));
}
bld.append(String.format(" %d. %s [%d]: walk %.1f m to '%s' and travel %s to '%s' by %s %d stops %s\n",
i + 1, s.route.getRef(), s.route.getId() / 2, s.walkDist, s.getStart().getName(),
time, s.getEnd().getName(),s.route.getName(), (s.end - s.start), arriveTime));
}
bld.append(String.format(" F. Walk %.1f m to reach your destination", finishWalkDist));
return bld.toString();
}
}
public static String formatTransporTime(int i) {
public static String formatTransportTime(int i) {
int h = i / 60 / 6;
int mh = i - h * 60 * 6;
int m = mh / 6;
int s = (mh - m * 6) * 10;
String tm = String.format("%02d:%02d:%02d ", h, m, s);
return tm;
return String.format(Locale.US, "%02d:%02d:%02d ", h, m, s);
}
public static class TransportRouteSegment {
@ -649,13 +493,9 @@ public class TransportRoutePlanner {
double parentTravelDist; // travel distance for parent route (inaccurate)
// walk distance to start route location (or finish in case last segment)
double walkDist = 0;
// main field accumulated all time spent from beginning of journey
double distFromStart = 0;
public TransportRouteSegment(TransportRoute road, int stopIndex) {
this.road = road;
this.segStart = (short) stopIndex;
@ -673,8 +513,7 @@ public class TransportRoutePlanner {
this.segStart = c.segStart;
this.departureTime = c.departureTime;
}
public boolean wasVisited(TransportRouteSegment rrs) {
if (rrs.road.getId().longValue() == road.getId().longValue() &&
rrs.departureTime == departureTime) {
@ -686,22 +525,18 @@ public class TransportRoutePlanner {
return false;
}
public TransportStop getStop(int i) {
return road.getForwardStops().get(i);
}
public LatLon getLocation() {
return road.getForwardStops().get(segStart).getLocation();
}
public int getLength() {
return road.getForwardStops().size();
}
public long getId() {
long l = road.getId();
@ -723,7 +558,6 @@ public class TransportRoutePlanner {
return l ;
}
public int getDepth() {
if(parentRoute != null) {
return parentRoute.getDepth() + 1;
@ -734,529 +568,13 @@ public class TransportRoutePlanner {
@Override
public String toString() {
return String.format("Route: %s, stop: %s %s", road.getName(), road.getForwardStops().get(segStart).getName(),
departureTime == -1 ? "" : formatTransporTime(departureTime) );
}
}
public static class TransportRoutingContext {
public NativeLibrary library;
public RouteCalculationProgress calculationProgress;
public TLongObjectHashMap<TransportRouteSegment> visitedSegments = new TLongObjectHashMap<TransportRouteSegment>();
public TransportRoutingConfiguration cfg;
public TLongObjectHashMap<TransportRoute> combinedRoutesCache = new TLongObjectHashMap<TransportRoute>();
public Map<TransportStop, List<TransportRoute>> missingStopsCache = new HashMap<TransportStop, List<TransportRoute>>();
public TLongObjectHashMap<List<TransportRouteSegment>> quadTree;
// Here we don't limit files by bbox, so it could be an issue while searching for multiple unused files
// Incomplete routes usually don't need more files than around Max-BBOX of start/end,
// so here an improvement could be introduced
public final Map<BinaryMapIndexReader, TIntObjectHashMap<TransportRoute>> routeMap =
new LinkedHashMap<BinaryMapIndexReader, TIntObjectHashMap<TransportRoute>>();
public int finishTimeSeconds;
// stats
public long startCalcTime;
public int visitedRoutesCount;
public int visitedStops;
public int wrongLoadedWays;
public int loadedWays;
public long loadTime;
public long readTime;
private final int walkRadiusIn31;
private final int walkChangeRadiusIn31;
public TransportRoutingContext(TransportRoutingConfiguration cfg, NativeLibrary library, BinaryMapIndexReader... readers) {
this.cfg = cfg;
walkRadiusIn31 = (int) (cfg.walkRadius / MapUtils.getTileDistanceWidth(31));
walkChangeRadiusIn31 = (int) (cfg.walkChangeRadius / MapUtils.getTileDistanceWidth(31));
quadTree = new TLongObjectHashMap<List<TransportRouteSegment>>();
this.library = library;
for (BinaryMapIndexReader r : readers) {
routeMap.put(r, new TIntObjectHashMap<TransportRoute>());
}
}
public List<TransportRouteSegment> getTransportStops(LatLon loc) throws IOException {
int y = MapUtils.get31TileNumberY(loc.getLatitude());
int x = MapUtils.get31TileNumberX(loc.getLongitude());
return getTransportStops(x, y, false, new ArrayList<TransportRouteSegment>());
}
public List<TransportRouteSegment> getTransportStops(int x, int y, boolean change, List<TransportRouteSegment> res) throws IOException {
return loadNativeTransportStops(x, y, change, res);
}
private List<TransportRouteSegment> loadNativeTransportStops(int sx, int sy, boolean change, List<TransportRouteSegment> res) throws IOException {
long nanoTime = System.nanoTime();
int d = change ? walkChangeRadiusIn31 : walkRadiusIn31;
int lx = (sx - d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
int rx = (sx + d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
int ty = (sy - d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
int by = (sy + d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
for(int x = lx; x <= rx; x++) {
for(int y = ty; y <= by; y++) {
long tileId = (((long)x) << (cfg.ZOOM_TO_LOAD_TILES + 1)) + y;
List<TransportRouteSegment> list = quadTree.get(tileId);
if(list == null) {
list = loadTile(x, y);
quadTree.put(tileId, list);
}
for(TransportRouteSegment r : list) {
TransportStop st = r.getStop(r.segStart);
if (Math.abs(st.x31 - sx) > walkRadiusIn31 || Math.abs(st.y31 - sy) > walkRadiusIn31) {
wrongLoadedWays++;
} else {
loadedWays++;
res.add(r);
}
}
}
}
loadTime += System.nanoTime() - nanoTime;
return res;
}
private List<TransportRouteSegment> loadTile(int x, int y) throws IOException {
long nanoTime = System.nanoTime();
List<TransportRouteSegment> lst = new ArrayList<TransportRouteSegment>();
int pz = (31 - cfg.ZOOM_TO_LOAD_TILES);
SearchRequest<TransportStop> sr = BinaryMapIndexReader.buildSearchTransportRequest(x << pz, (x + 1) << pz,
y << pz, (y + 1) << pz, -1, null);
// could be global ?
TLongObjectHashMap<TransportStop> loadedTransportStops = new TLongObjectHashMap<TransportStop>();
TIntObjectHashMap<TransportRoute> localFileRoutes = new TIntObjectHashMap<>(); //reference, route
for (BinaryMapIndexReader r : routeMap.keySet()) {
sr.clearSearchResults();
List<TransportStop> stops = r.searchTransportIndex(sr);
localFileRoutes.clear();
mergeTransportStops(r, loadedTransportStops, stops, localFileRoutes, routeMap.get(r));
for (TransportStop stop : stops) {
// skip missing stops
if (stop.isMissingStop()) {
continue;
}
long stopId = stop.getId();
TransportStop multifileStop = loadedTransportStops.get(stopId);
int[] rrs = stop.getReferencesToRoutes();
// clear up so it won't be used as it is multi file stop
stop.setReferencesToRoutes(null);
if (rrs != null && !multifileStop.isDeleted()) {
for (int rr : rrs) {
TransportRoute route = localFileRoutes.get(rr);
if (route == null) {
System.err.println(
String.format("Something went wrong by loading combined route %d for stop %s",
rr, stop));
} else {
TransportRoute combinedRoute = getCombinedRoute(route);
if (multifileStop == stop || (!multifileStop.hasRoute(combinedRoute.getId()) &&
!multifileStop.isRouteDeleted(combinedRoute.getId()))) {
// duplicates won't be added
multifileStop.addRouteId(combinedRoute.getId());
multifileStop.addRoute(combinedRoute);
}
}
}
}
}
}
// There should go stops with complete routes:
loadTransportSegments(loadedTransportStops.valueCollection(), lst);
readTime += System.nanoTime() - nanoTime;
return lst;
}
public static List<TransportStop> mergeTransportStops(BinaryMapIndexReader reader,
TLongObjectHashMap<TransportStop> loadedTransportStops,
List<TransportStop> stops,
TIntObjectHashMap<TransportRoute> localFileRoutes,
TIntObjectHashMap<TransportRoute> loadedRoutes
) throws IOException {
TIntArrayList routesToLoad = new TIntArrayList();
TIntArrayList localRoutesToLoad = new TIntArrayList();
Iterator<TransportStop> it = stops.iterator();
while (it.hasNext()) {
TransportStop stop = it.next();
long stopId = stop.getId();
localRoutesToLoad.clear();
TransportStop multifileStop = loadedTransportStops.get(stopId);
long[] routesIds = stop.getRoutesIds();
long[] delRIds = stop.getDeletedRoutesIds();
if (multifileStop == null) {
loadedTransportStops.put(stopId, stop);
multifileStop = stop;
if(!stop.isDeleted()) {
localRoutesToLoad.addAll(stop.getReferencesToRoutes());
}
} else if (multifileStop.isDeleted()){
// stop has nothing to load, so not needed
it.remove();
} else {
if (delRIds != null) {
for (long deletedRouteId : delRIds) {
multifileStop.addDeletedRouteId(deletedRouteId);
}
}
if (routesIds != null && routesIds.length > 0) {
int[] refs = stop.getReferencesToRoutes();
for (int i = 0; i < routesIds.length; i++) {
long routeId = routesIds[i];
if (!multifileStop.hasRoute(routeId) && !multifileStop.isRouteDeleted(routeId)) {
localRoutesToLoad.add(refs[i]);
}
}
} else {
if (stop.hasReferencesToRoutes()) {
// old format
localRoutesToLoad.addAll(stop.getReferencesToRoutes());
} else {
// stop has noting to load, so not needed
it.remove();
}
}
}
routesToLoad.addAll(localRoutesToLoad);
multifileStop.putReferencesToRoutes(reader.getFile().getName(), localRoutesToLoad.toArray()); //add valid stop and references to routes
}
// load/combine routes
if (routesToLoad.size() > 0) {
routesToLoad.sort();
TIntArrayList referencesToLoad = new TIntArrayList();
TIntIterator itr = routesToLoad.iterator();
int p = routesToLoad.get(0) + 1; // different
while (itr.hasNext()) {
int nxt = itr.next();
if (p != nxt) {
if (localFileRoutes != null && loadedRoutes != null && loadedRoutes.contains(nxt)) { //check if
localFileRoutes.put(nxt, loadedRoutes.get(nxt));
} else {
referencesToLoad.add(nxt);
}
}
}
if (localFileRoutes != null && loadedRoutes != null) {
reader.loadTransportRoutes(referencesToLoad.toArray(), localFileRoutes);
loadedRoutes.putAll(localFileRoutes);
}
}
return stops;
}
private TransportRoute getCombinedRoute(TransportRoute route) throws IOException {
if (!route.isIncomplete()) {
return route;
}
TransportRoute c = combinedRoutesCache.get(route.getId());
if (c == null) {
c = combineRoute(route);
combinedRoutesCache.put(route.getId(), c);
}
return c;
}
private TransportRoute combineRoute(TransportRoute route) throws IOException {
// 1. Get all available route parts;
List<TransportRoute> incompleteRoutes = findIncompleteRouteParts(route);
if (incompleteRoutes == null) {
return route;
}
// here could be multiple overlays between same points
// It's better to remove them especially identical segments
List<Way> allWays = getAllWays(incompleteRoutes);
// 2. Get array of segments (each array size > 1):
LinkedList<List<TransportStop>> stopSegments = parseRoutePartsToSegments(incompleteRoutes);
// 3. Merge segments and remove excess missingStops (when they are closer then MISSING_STOP_SEARCH_RADIUS):
// + Check for missingStops. If they present in the middle/there more then one segment - we have a hole in the map data
List<List<TransportStop>> mergedSegments = combineSegmentsOfSameRoute(stopSegments);
// 4. Now we need to properly sort segments, proper sorting is minimizing distance between stops
// So it is salesman problem, we have this solution at TspAnt, but if we know last or first segment we can solve it straightforward
List<TransportStop> firstSegment = null;
List<TransportStop> lastSegment = null;
for(List<TransportStop> l : mergedSegments) {
if(!l.get(0).isMissingStop()) {
firstSegment = l;
}
if(!l.get(l.size() - 1).isMissingStop()) {
lastSegment = l;
}
}
List<List<TransportStop>> sortedSegments = new ArrayList<List<TransportStop>>();
if(firstSegment != null) {
sortedSegments.add(firstSegment);
mergedSegments.remove(firstSegment);
while(!mergedSegments.isEmpty()) {
List<TransportStop> last = sortedSegments.get(sortedSegments.size() - 1);
List<TransportStop> add = findAndDeleteMinDistance(last.get(last.size() - 1).getLocation(), mergedSegments, true);
sortedSegments.add(add);
}
} else if(lastSegment != null) {
sortedSegments.add(lastSegment);
mergedSegments.remove(lastSegment);
while(!mergedSegments.isEmpty()) {
List<TransportStop> first = sortedSegments.get(0);
List<TransportStop> add = findAndDeleteMinDistance(first.get(0).getLocation(), mergedSegments, false);
sortedSegments.add(0, add);
}
} else {
sortedSegments = mergedSegments;
}
List<TransportStop> finalList = new ArrayList<TransportStop>();
for(List<TransportStop> s : sortedSegments) {
finalList.addAll(s);
}
// 5. Create combined TransportRoute and return it
return new TransportRoute(route, finalList, allWays);
}
private List<TransportStop> findAndDeleteMinDistance(LatLon location, List<List<TransportStop>> mergedSegments,
boolean attachToBegin) {
int ind = attachToBegin ? 0 : mergedSegments.get(0).size() - 1;
double minDist = MapUtils.getDistance(mergedSegments.get(0).get(ind).getLocation(), location);
int minInd = 0;
for(int i = 1; i < mergedSegments.size(); i++) {
ind = attachToBegin ? 0 : mergedSegments.get(i).size() - 1;
double dist = MapUtils.getDistance(mergedSegments.get(i).get(ind).getLocation(), location);
if(dist < minDist) {
minInd = i;
}
}
return mergedSegments.remove(minInd);
}
private List<Way> getAllWays(List<TransportRoute> parts) {
List<Way> w = new ArrayList<Way>();
for (TransportRoute t : parts) {
w.addAll(t.getForwardWays());
}
return w;
}
private List<List<TransportStop>> combineSegmentsOfSameRoute(LinkedList<List<TransportStop>> segments) {
List<List<TransportStop>> resultSegments = new ArrayList<List<TransportStop>>();
while (!segments.isEmpty()) {
List<TransportStop> firstSegment = segments.poll();
boolean merged = true;
while (merged) {
merged = false;
Iterator<List<TransportStop>> it = segments.iterator();
while (it.hasNext()) {
List<TransportStop> segmentToMerge = it.next();
merged = tryToMerge(firstSegment, segmentToMerge);
if (merged) {
it.remove();
break;
}
}
}
resultSegments.add(firstSegment);
}
return resultSegments;
}
private boolean tryToMerge(List<TransportStop> firstSegment, List<TransportStop> segmentToMerge) {
if(firstSegment.size() < 2 || segmentToMerge.size() < 2) {
return false;
}
// 1st we check that segments overlap by stop
int commonStopFirst = 0;
int commonStopSecond = 0;
boolean found = false;
for(;commonStopFirst < firstSegment.size(); commonStopFirst++) {
for(commonStopSecond = 0; commonStopSecond < segmentToMerge.size() && !found; commonStopSecond++) {
long lid1 = firstSegment.get(commonStopFirst).getId();
long lid2 = segmentToMerge.get(commonStopSecond).getId();
if(lid1 > 0 && lid2 == lid1) {
found = true;
break;
}
}
if(found) {
// important to increment break inside loop
break;
}
}
if(found && commonStopFirst < firstSegment.size()) {
// we've found common stop so we can merge based on stops
// merge last part first
int leftPartFirst = firstSegment.size() - commonStopFirst;
int leftPartSecond = segmentToMerge.size() - commonStopSecond;
if(leftPartFirst < leftPartSecond || (leftPartFirst == leftPartSecond &&
firstSegment.get(firstSegment.size() - 1).isMissingStop())) {
while(firstSegment.size() > commonStopFirst) {
firstSegment.remove(firstSegment.size() - 1);
}
for(int i = commonStopSecond; i < segmentToMerge.size(); i++) {
firstSegment.add(segmentToMerge.get(i));
}
}
// merge first part
if(commonStopFirst < commonStopSecond || (commonStopFirst == commonStopSecond &&
firstSegment.get(0).isMissingStop())) {
for(int i = 0; i < commonStopFirst; i++) {
firstSegment.remove(0);
}
for(int i = commonStopSecond; i >= 0; i--) {
firstSegment.add(0, segmentToMerge.get(i));
}
}
return true;
}
// no common stops, so try to connect to the end or beginning
// beginning
boolean merged = false;
if (MapUtils.getDistance(firstSegment.get(0).getLocation(),
segmentToMerge.get(segmentToMerge.size() - 1).getLocation()) < MISSING_STOP_SEARCH_RADIUS) {
firstSegment.remove(0);
for(int i = segmentToMerge.size() - 2; i >= 0; i--) {
firstSegment.add(0, segmentToMerge.get(i));
}
merged = true;
} else if(MapUtils.getDistance(firstSegment.get(firstSegment.size() - 1).getLocation(),
segmentToMerge.get(0).getLocation()) < MISSING_STOP_SEARCH_RADIUS) {
firstSegment.remove(firstSegment.size() - 1);
for(int i = 1; i < segmentToMerge.size(); i++) {
firstSegment.add(segmentToMerge.get(i));
}
merged = true;
}
return merged;
}
private LinkedList<List<TransportStop>> parseRoutePartsToSegments(List<TransportRoute> routeParts) {
LinkedList<List<TransportStop>> segs = new LinkedList<List<TransportStop>>();
// here we assume that missing stops come in pairs <A, B, C, MISSING, MISSING, D, E...>
// we don't add segments with 1 stop cause they are irrelevant further
for (TransportRoute part : routeParts) {
List<TransportStop> newSeg = new ArrayList<TransportStop>();
for (TransportStop s : part.getForwardStops()) {
newSeg.add(s);
if (s.isMissingStop()) {
if (newSeg.size() > 1) {
segs.add(newSeg);
newSeg = new ArrayList<TransportStop>();
}
}
}
if (newSeg.size() > 1) {
segs.add(newSeg);
}
}
return segs;
}
private List<TransportRoute> findIncompleteRouteParts(TransportRoute baseRoute) throws IOException {
List<TransportRoute> allRoutes = null;
for (BinaryMapIndexReader bmir : routeMap.keySet()) {
// here we could limit routeMap indexes by only certain bbox around start / end (check comment on field)
IncompleteTransportRoute ptr = bmir.getIncompleteTransportRoutes().get(baseRoute.getId());
if (ptr != null) {
TIntArrayList lst = new TIntArrayList();
while(ptr != null) {
lst.add(ptr.getRouteOffset());
ptr = ptr.getNextLinkedRoute();
}
if(lst.size() > 0) {
if(allRoutes == null) {
allRoutes = new ArrayList<TransportRoute>();
}
allRoutes.addAll(bmir.getTransportRoutes(lst.toArray()).valueCollection());
}
}
}
return allRoutes;
}
private void loadTransportSegments(Collection<TransportStop> stops, List<TransportRouteSegment> lst) throws IOException {
for(TransportStop s : stops) {
if (s.isDeleted() || s.getRoutes() == null) {
continue;
}
for (TransportRoute route : s.getRoutes()) {
int stopIndex = -1;
double dist = TransportRoute.SAME_STOP;
for (int k = 0; k < route.getForwardStops().size(); k++) {
TransportStop st = route.getForwardStops().get(k);
if(st.getId().longValue() == s.getId().longValue() ) {
stopIndex = k;
break;
}
double d = MapUtils.getDistance(st.getLocation(), s.getLocation());
if (d < dist) {
stopIndex = k;
dist = d;
}
}
if (stopIndex != -1) {
if (cfg.useSchedule) {
loadScheduleRouteSegment(lst, route, stopIndex);
} else {
TransportRouteSegment segment = new TransportRouteSegment(route, stopIndex);
lst.add(segment);
}
} else {
// MapUtils.getDistance(s.getLocation(), route.getForwardStops().get(158).getLocation());
System.err.println(String.format("Routing error: missing stop '%s' in route '%s' id: %d",
s.toString(), route.getRef(), route.getId() / 2));
}
}
}
}
private void loadScheduleRouteSegment(List<TransportRouteSegment> lst, TransportRoute route, int stopIndex) {
if(route.getSchedule() != null) {
TIntArrayList ti = route.getSchedule().tripIntervals;
int cnt = ti.size();
int t = 0;
// improve by using exact data
int stopTravelTime = 0;
TIntArrayList avgStopIntervals = route.getSchedule().avgStopIntervals;
for (int i = 0; i < stopIndex; i++) {
if (avgStopIntervals.size() > i) {
stopTravelTime += avgStopIntervals.getQuick(i);
}
}
for(int i = 0; i < cnt; i++) {
t += ti.getQuick(i);
int startTime = t + stopTravelTime;
if(startTime >= cfg.scheduleTimeOfDay && startTime <= cfg.scheduleTimeOfDay + cfg.scheduleMaxTime ) {
TransportRouteSegment segment = new TransportRouteSegment(route, stopIndex, startTime);
lst.add(segment);
}
}
}
departureTime == -1 ? "" : formatTransportTime(departureTime) );
}
}
public static List<TransportRouteResult> convertToTransportRoutingResult(NativeTransportRoutingResult[] res,
TransportRoutingConfiguration cfg) {
//cache for converted TransportRoutes:
TransportRoutingConfiguration cfg) {
// cache for converted TransportRoutes:
TLongObjectHashMap<TransportRoute> convertedRoutesCache = new TLongObjectHashMap<>();
TLongObjectHashMap<TransportStop> convertedStopsCache = new TLongObjectHashMap<>();
@ -1290,8 +608,8 @@ public class TransportRoutePlanner {
}
private static TransportRoute convertTransportRoute(NativeTransportRoute nr,
TLongObjectHashMap<TransportRoute> convertedRoutesCache,
TLongObjectHashMap<TransportStop> convertedStopsCache) {
TLongObjectHashMap<TransportRoute> convertedRoutesCache,
TLongObjectHashMap<TransportStop> convertedStopsCache) {
TransportRoute r = new TransportRoute();
r.setId(nr.id);
r.setLocation(nr.routeLat, nr.routeLon);
@ -1310,10 +628,10 @@ public class TransportRoutePlanner {
r.setDist(nr.dist);
r.setColor(nr.color);
if (nr.intervals != null && nr.intervals.length > 0 && nr.avgStopIntervals !=null
if (nr.intervals != null && nr.intervals.length > 0 && nr.avgStopIntervals != null
&& nr.avgStopIntervals.length > 0 && nr.avgWaitIntervals != null && nr.avgWaitIntervals.length > 0) {
r.setSchedule(new TransportSchedule(new TIntArrayList(nr.intervals),
new TIntArrayList(nr.avgStopIntervals), new TIntArrayList(nr.avgWaitIntervals)));
r.setSchedule(new TransportSchedule(new TIntArrayList(nr.intervals), new TIntArrayList(nr.avgStopIntervals),
new TIntArrayList(nr.avgWaitIntervals)));
}
for (int i = 0; i < nr.waysIds.length; i++) {
@ -1331,7 +649,7 @@ public class TransportRoutePlanner {
}
private static List<TransportStop> convertTransportStops(NativeTransportStop[] nstops,
TLongObjectHashMap<TransportStop> convertedStopsCache) {
TLongObjectHashMap<TransportStop> convertedStopsCache) {
List<TransportStop> stops = new ArrayList<>();
for (NativeTransportStop ns : nstops) {
if (convertedStopsCache != null && convertedStopsCache.get(ns.id) != null) {
@ -1358,16 +676,11 @@ public class TransportRoutePlanner {
if (ns.pTStopExit_refs != null && ns.pTStopExit_refs.length > 0) {
for (int i = 0; i < ns.pTStopExit_refs.length; i++) {
s.addExit(new TransportStopExit(ns.pTStopExit_x31s[i],
ns.pTStopExit_y31s[i], ns.pTStopExit_refs[i]));
s.addExit(
new TransportStopExit(ns.pTStopExit_x31s[i], ns.pTStopExit_y31s[i], ns.pTStopExit_refs[i]));
}
}
if (ns.referenceToRoutesKeys != null && ns.referenceToRoutesKeys.length > 0) {
for (int i = 0; i < ns.referenceToRoutesKeys.length; i++) {
s.putReferencesToRoutes(ns.referenceToRoutesKeys[i], ns.referenceToRoutesVals[i]);
}
}
if (convertedStopsCache == null) {
convertedStopsCache = new TLongObjectHashMap<>();
}
@ -1378,4 +691,7 @@ public class TransportRoutePlanner {
}
return stops;
}
}

View file

@ -0,0 +1,151 @@
package net.osmand.router;
import net.osmand.data.TransportSchedule;
import net.osmand.data.TransportStop;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class TransportRouteResult {
List<TransportRoutePlanner.TransportRouteResultSegment> segments = new ArrayList<TransportRoutePlanner.TransportRouteResultSegment>(4);
double finishWalkDist;
double routeTime;
private final TransportRoutingConfiguration cfg;
public TransportRouteResult(TransportRoutingContext ctx) {
cfg = ctx.cfg;
}
public TransportRouteResult(TransportRoutingConfiguration cfg) {
this.cfg = cfg;
}
public List<TransportRoutePlanner.TransportRouteResultSegment> getSegments() {
return segments;
}
public void setFinishWalkDist(double finishWalkDist) {
this.finishWalkDist = finishWalkDist;
}
public void setRouteTime(double routeTime) {
this.routeTime = routeTime;
}
public void addSegment(TransportRoutePlanner.TransportRouteResultSegment seg) {
segments.add(seg);
}
public double getWalkDist() {
double d = finishWalkDist;
for (TransportRoutePlanner.TransportRouteResultSegment s : segments) {
d += s.walkDist;
}
return d;
}
public double getFinishWalkDist() {
return finishWalkDist;
}
public double getWalkSpeed() {
return cfg.walkSpeed;
}
public double getRouteTime() {
return routeTime;
}
public int getStops() {
int stops = 0;
for(TransportRoutePlanner.TransportRouteResultSegment s : segments) {
stops += (s.end - s.start);
}
return stops;
}
public boolean isRouteStop(TransportStop stop) {
for(TransportRoutePlanner.TransportRouteResultSegment s : segments) {
if (s.getTravelStops().contains(stop)) {
return true;
}
}
return false;
}
public TransportRoutePlanner.TransportRouteResultSegment getRouteStopSegment(TransportStop stop) {
for(TransportRoutePlanner.TransportRouteResultSegment s : segments) {
if (s.getTravelStops().contains(stop)) {
return s;
}
}
return null;
}
public double getTravelDist() {
double d = 0;
for (TransportRoutePlanner.TransportRouteResultSegment s : segments) {
d += s.getTravelDist();
}
return d;
}
public double getTravelTime() {
double t = 0;
for (TransportRoutePlanner.TransportRouteResultSegment s : segments) {
if (cfg.useSchedule) {
TransportSchedule sts = s.route.getSchedule();
for (int k = s.start; k < s.end; k++) {
t += sts.getAvgStopIntervals()[k] * 10;
}
} else {
t += cfg.getBoardingTime();
t += s.getTravelTime();
}
}
return t;
}
public double getWalkTime() {
return getWalkDist() / cfg.walkSpeed;
}
public double getChangeTime() {
return cfg.getChangeTime();
}
public double getBoardingTime() {
return cfg.getBoardingTime();
}
public int getChanges() {
return segments.size() - 1;
}
@Override
public String toString() {
StringBuilder bld = new StringBuilder();
bld.append(String.format(Locale.US, "Route %d stops, %d changes, %.2f min: %.2f m (%.1f min) to walk, %.2f m (%.1f min) to travel\n",
getStops(), getChanges(), routeTime / 60, getWalkDist(), getWalkTime() / 60.0,
getTravelDist(), getTravelTime() / 60.0));
for(int i = 0; i < segments.size(); i++) {
TransportRoutePlanner.TransportRouteResultSegment s = segments.get(i);
String time = "";
String arriveTime = "";
if(s.depTime != -1) {
time = String.format("at %s", TransportRoutePlanner.formatTransportTime(s.depTime));
}
int aTime = s.getArrivalTime();
if(aTime != -1) {
arriveTime = String.format("and arrive at %s", TransportRoutePlanner.formatTransportTime(aTime));
}
bld.append(String.format(Locale.US, " %d. %s [%d]: walk %.1f m to '%s' and travel %s to '%s' by %s %d stops %s\n",
i + 1, s.route.getRef(), s.route.getId() / 2, s.walkDist, s.getStart().getName(),
time, s.getEnd().getName(),s.route.getName(), (s.end - s.start), arriveTime));
}
bld.append(String.format(" F. Walk %.1f m to reach your destination", finishWalkDist));
return bld.toString();
}
}

View file

@ -0,0 +1,171 @@
package net.osmand.router;
import net.osmand.NativeLibrary;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.data.LatLon;
import net.osmand.data.TransportRoute;
import net.osmand.data.TransportStop;
import net.osmand.router.TransportRoutePlanner.TransportRouteSegment;
import net.osmand.util.MapUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.map.hash.TLongObjectHashMap;
public class TransportRoutingContext {
public NativeLibrary library;
public RouteCalculationProgress calculationProgress;
public TLongObjectHashMap<TransportRouteSegment> visitedSegments = new TLongObjectHashMap<TransportRouteSegment>();
public TransportRoutingConfiguration cfg;
public TLongObjectHashMap<TransportRoute> combinedRoutesCache = new TLongObjectHashMap<TransportRoute>();
public Map<TransportStop, List<TransportRoute>> missingStopsCache = new HashMap<TransportStop, List<TransportRoute>>();
public TLongObjectHashMap<List<TransportRouteSegment>> quadTree;
// Here we don't limit files by bbox, so it could be an issue while searching for multiple unused files
// Incomplete routes usually don't need more files than around Max-BBOX of start/end,
// so here an improvement could be introduced
final TransportStopsRouteReader transportStopsReader;
public int finishTimeSeconds;
// stats
public long startCalcTime;
public int visitedRoutesCount;
public int visitedStops;
public int wrongLoadedWays;
public int loadedWays;
public long loadTime;
public long readTime;
private final int walkRadiusIn31;
private final int walkChangeRadiusIn31;
public TransportRoutingContext(TransportRoutingConfiguration cfg, NativeLibrary library, BinaryMapIndexReader... readers) {
this.cfg = cfg;
walkRadiusIn31 = (int) (cfg.walkRadius / MapUtils.getTileDistanceWidth(31));
walkChangeRadiusIn31 = (int) (cfg.walkChangeRadius / MapUtils.getTileDistanceWidth(31));
quadTree = new TLongObjectHashMap<List<TransportRouteSegment>>();
this.library = library;
transportStopsReader = new TransportStopsRouteReader(Arrays.asList(readers));
}
public List<TransportRouteSegment> getTransportStops(LatLon loc) throws IOException {
int y = MapUtils.get31TileNumberY(loc.getLatitude());
int x = MapUtils.get31TileNumberX(loc.getLongitude());
return getTransportStops(x, y, false, new ArrayList<TransportRouteSegment>());
}
public List<TransportRouteSegment> getTransportStops(int x, int y, boolean change, List<TransportRouteSegment> res) throws IOException {
return loadNativeTransportStops(x, y, change, res);
}
private List<TransportRouteSegment> loadNativeTransportStops(int sx, int sy, boolean change, List<TransportRouteSegment> res) throws IOException {
long nanoTime = System.nanoTime();
int d = change ? walkChangeRadiusIn31 : walkRadiusIn31;
int lx = (sx - d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
int rx = (sx + d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
int ty = (sy - d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
int by = (sy + d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
for(int x = lx; x <= rx; x++) {
for(int y = ty; y <= by; y++) {
long tileId = (((long)x) << (cfg.ZOOM_TO_LOAD_TILES + 1)) + y;
List<TransportRouteSegment> list = quadTree.get(tileId);
if(list == null) {
list = loadTile(x, y);
quadTree.put(tileId, list);
}
for(TransportRouteSegment r : list) {
TransportStop st = r.getStop(r.segStart);
if (Math.abs(st.x31 - sx) > walkRadiusIn31 || Math.abs(st.y31 - sy) > walkRadiusIn31) {
wrongLoadedWays++;
} else {
loadedWays++;
res.add(r);
}
}
}
}
loadTime += System.nanoTime() - nanoTime;
return res;
}
private List<TransportRouteSegment> loadTile(int x, int y) throws IOException {
long nanoTime = System.nanoTime();
List<TransportRouteSegment> lst = new ArrayList<TransportRouteSegment>();
int pz = (31 - cfg.ZOOM_TO_LOAD_TILES);
BinaryMapIndexReader.SearchRequest<TransportStop> sr = BinaryMapIndexReader.buildSearchTransportRequest(x << pz, (x + 1) << pz,
y << pz, (y + 1) << pz, -1, null);
Collection<TransportStop> stops = transportStopsReader.readMergedTransportStops(sr);
loadTransportSegments(stops, lst);
readTime += System.nanoTime() - nanoTime;
return lst;
}
private void loadTransportSegments(Collection<TransportStop> stops, List<TransportRouteSegment> lst) throws IOException {
for(TransportStop s : stops) {
if (s.isDeleted() || s.getRoutes() == null) {
continue;
}
for (TransportRoute route : s.getRoutes()) {
int stopIndex = -1;
double dist = TransportRoute.SAME_STOP;
for (int k = 0; k < route.getForwardStops().size(); k++) {
TransportStop st = route.getForwardStops().get(k);
if(st.getId().longValue() == s.getId().longValue() ) {
stopIndex = k;
break;
}
double d = MapUtils.getDistance(st.getLocation(), s.getLocation());
if (d < dist) {
stopIndex = k;
dist = d;
}
}
if (stopIndex != -1) {
if (cfg != null && cfg.useSchedule) {
loadScheduleRouteSegment(lst, route, stopIndex);
} else {
TransportRouteSegment segment = new TransportRouteSegment(route, stopIndex);
lst.add(segment);
}
} else {
System.err.println(String.format(Locale.US, "Routing error: missing stop '%s' in route '%s' id: %d",
s.toString(), route.getRef(), route.getId() / 2));
}
}
}
}
private void loadScheduleRouteSegment(List<TransportRouteSegment> lst, TransportRoute route, int stopIndex) {
if(route.getSchedule() != null) {
TIntArrayList ti = route.getSchedule().tripIntervals;
int cnt = ti.size();
int t = 0;
// improve by using exact data
int stopTravelTime = 0;
TIntArrayList avgStopIntervals = route.getSchedule().avgStopIntervals;
for (int i = 0; i < stopIndex; i++) {
if (avgStopIntervals.size() > i) {
stopTravelTime += avgStopIntervals.getQuick(i);
}
}
for(int i = 0; i < cnt; i++) {
t += ti.getQuick(i);
int startTime = t + stopTravelTime;
if(startTime >= cfg.scheduleTimeOfDay && startTime <= cfg.scheduleTimeOfDay + cfg.scheduleMaxTime ) {
TransportRouteSegment segment = new TransportRouteSegment(route, stopIndex, startTime);
lst.add(segment);
}
}
}
}
}

View file

@ -0,0 +1,409 @@
package net.osmand.router;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import gnu.trove.iterator.TIntObjectIterator;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.BinaryMapTransportReaderAdapter.TransportIndex;
import net.osmand.data.IncompleteTransportRoute;
import net.osmand.data.LatLon;
import net.osmand.data.TransportRoute;
import net.osmand.data.TransportStop;
import net.osmand.osm.edit.Way;
import net.osmand.util.MapUtils;
public class TransportStopsRouteReader {
public static final int MISSING_STOP_SEARCH_RADIUS = 50000;
TLongObjectHashMap<TransportRoute> combinedRoutesCache = new TLongObjectHashMap<TransportRoute>();
Map<BinaryMapIndexReader, TIntObjectHashMap<TransportRoute>> routesFilesCache = new LinkedHashMap<BinaryMapIndexReader,
TIntObjectHashMap<TransportRoute>>();
public TransportStopsRouteReader(Collection<BinaryMapIndexReader> fls) {
for(BinaryMapIndexReader r : fls) {
routesFilesCache.put(r, new TIntObjectHashMap<TransportRoute>());
}
}
public Collection<TransportStop> readMergedTransportStops(SearchRequest<TransportStop> sr) throws IOException {
TLongObjectHashMap<TransportStop> loadedTransportStops = new TLongObjectHashMap<TransportStop>();
for (BinaryMapIndexReader r : routesFilesCache.keySet()) {
sr.clearSearchResults();
List<TransportStop> stops = r.searchTransportIndex(sr);
TIntObjectHashMap<TransportRoute> routesToLoad = mergeTransportStops(r, loadedTransportStops, stops);
loadRoutes(r, routesToLoad);
for (TransportStop stop : stops) {
// skip missing stops
if (stop.isMissingStop()) {
continue;
}
long stopId = stop.getId();
TransportStop multifileStop = loadedTransportStops.get(stopId);
int[] rrs = stop.getReferencesToRoutes();
// clear up so it won't be used because there is multi file stop
stop.setReferencesToRoutes(null);
if (rrs != null && !multifileStop.isDeleted()) {
for (int rr : rrs) {
TransportRoute route = routesToLoad.get(rr);
if (route == null) {
if (routesToLoad.containsKey(rr)) {
System.err.println(String.format(
"Something went wrong by loading combined route %d for stop %s", rr, stop));
}
} else {
TransportRoute combinedRoute = getCombinedRoute(route);
if (multifileStop == stop || (!multifileStop.hasRoute(combinedRoute.getId())
&& !multifileStop.isRouteDeleted(combinedRoute.getId()))) {
// duplicates won't be added
multifileStop.addRouteId(combinedRoute.getId());
multifileStop.addRoute(combinedRoute);
}
}
}
}
}
}
// There should go stops with complete routes:
return loadedTransportStops.valueCollection();
}
public TIntObjectHashMap<TransportRoute> mergeTransportStops(BinaryMapIndexReader reader,
TLongObjectHashMap<TransportStop> loadedTransportStops, List<TransportStop> stops) throws IOException {
Iterator<TransportStop> it = stops.iterator();
TIntObjectHashMap<TransportRoute> routesToLoad = routesFilesCache.get(reader);
while (it.hasNext()) {
TransportStop stop = it.next();
long stopId = stop.getId();
TransportStop multifileStop = loadedTransportStops.get(stopId);
long[] routesIds = stop.getRoutesIds();
long[] delRIds = stop.getDeletedRoutesIds();
if (multifileStop == null) {
loadedTransportStops.put(stopId, stop);
multifileStop = stop;
if (!stop.isDeleted()) {
putAll(routesToLoad, stop.getReferencesToRoutes());
}
} else if (multifileStop.isDeleted()) {
// stop has nothing to load, so not needed
it.remove();
} else {
if (delRIds != null) {
for (long deletedRouteId : delRIds) {
multifileStop.addDeletedRouteId(deletedRouteId);
}
}
if (routesIds != null && routesIds.length > 0) {
int[] refs = stop.getReferencesToRoutes();
for (int i = 0; i < routesIds.length; i++) {
long routeId = routesIds[i];
if (!multifileStop.hasRoute(routeId) && !multifileStop.isRouteDeleted(routeId)) {
if(!routesToLoad.containsKey(refs[i])) {
routesToLoad.put(refs[i], null);
}
}
}
} else {
if (stop.hasReferencesToRoutes()) {
// old format
putAll(routesToLoad, stop.getReferencesToRoutes());
} else {
// stop has noting to load, so not needed
it.remove();
}
}
}
}
return routesToLoad;
}
private void putAll(TIntObjectHashMap<TransportRoute> routesToLoad, int[] referencesToRoutes) {
for(int k = 0 ; k < referencesToRoutes.length; k++) {
if(!routesToLoad.containsKey(referencesToRoutes[k])) {
routesToLoad.put(referencesToRoutes[k], null);
}
}
}
public void loadRoutes(BinaryMapIndexReader reader, TIntObjectHashMap<TransportRoute> localFileRoutes) throws IOException {
// load/combine routes
if (localFileRoutes.size() > 0) {
TIntArrayList routesToLoad = new TIntArrayList(localFileRoutes.size());
TIntObjectIterator<TransportRoute> it = localFileRoutes.iterator();
while(it.hasNext()) {
it.advance();
if(it.value() == null) {
routesToLoad.add(it.key());
}
}
routesToLoad.sort();
reader.loadTransportRoutes(routesToLoad.toArray(), localFileRoutes);
}
}
private TransportRoute getCombinedRoute(TransportRoute route) throws IOException {
if (!route.isIncomplete()) {
return route;
}
TransportRoute c = combinedRoutesCache.get(route.getId());
if (c == null) {
c = combineRoute(route);
combinedRoutesCache.put(route.getId(), c);
}
return c;
}
private TransportRoute combineRoute(TransportRoute route) throws IOException {
// 1. Get all available route parts;
List<TransportRoute> incompleteRoutes = findIncompleteRouteParts(route);
if (incompleteRoutes == null) {
return route;
}
// here could be multiple overlays between same points
// It's better to remove them especially identical segments
List<Way> allWays = getAllWays(incompleteRoutes);
// 2. Get array of segments (each array size > 1):
LinkedList<List<TransportStop>> stopSegments = parseRoutePartsToSegments(incompleteRoutes);
// 3. Merge segments and remove excess missingStops (when they are closer then MISSING_STOP_SEARCH_RADIUS):
// + Check for missingStops. If they present in the middle/there more then one segment - we have a hole in the
// map data
List<List<TransportStop>> mergedSegments = combineSegmentsOfSameRoute(stopSegments);
// 4. Now we need to properly sort segments, proper sorting is minimizing distance between stops
// So it is salesman problem, we have this solution at TspAnt, but if we know last or first segment we can solve
// it straightforward
List<TransportStop> firstSegment = null;
List<TransportStop> lastSegment = null;
for (List<TransportStop> l : mergedSegments) {
if (!l.get(0).isMissingStop()) {
firstSegment = l;
}
if (!l.get(l.size() - 1).isMissingStop()) {
lastSegment = l;
}
}
List<List<TransportStop>> sortedSegments = new ArrayList<List<TransportStop>>();
if (firstSegment != null) {
sortedSegments.add(firstSegment);
mergedSegments.remove(firstSegment);
while (!mergedSegments.isEmpty()) {
List<TransportStop> last = sortedSegments.get(sortedSegments.size() - 1);
List<TransportStop> add = findAndDeleteMinDistance(last.get(last.size() - 1).getLocation(),
mergedSegments, true);
sortedSegments.add(add);
}
} else if (lastSegment != null) {
sortedSegments.add(lastSegment);
mergedSegments.remove(lastSegment);
while (!mergedSegments.isEmpty()) {
List<TransportStop> first = sortedSegments.get(0);
List<TransportStop> add = findAndDeleteMinDistance(first.get(0).getLocation(), mergedSegments, false);
sortedSegments.add(0, add);
}
} else {
sortedSegments = mergedSegments;
}
List<TransportStop> finalList = new ArrayList<TransportStop>();
for (List<TransportStop> s : sortedSegments) {
finalList.addAll(s);
}
// 5. Create combined TransportRoute and return it
return new TransportRoute(route, finalList, allWays);
}
private List<TransportStop> findAndDeleteMinDistance(LatLon location, List<List<TransportStop>> mergedSegments,
boolean attachToBegin) {
int ind = attachToBegin ? 0 : mergedSegments.get(0).size() - 1;
double minDist = MapUtils.getDistance(mergedSegments.get(0).get(ind).getLocation(), location);
int minInd = 0;
for (int i = 1; i < mergedSegments.size(); i++) {
ind = attachToBegin ? 0 : mergedSegments.get(i).size() - 1;
double dist = MapUtils.getDistance(mergedSegments.get(i).get(ind).getLocation(), location);
if (dist < minDist) {
minInd = i;
}
}
return mergedSegments.remove(minInd);
}
private List<Way> getAllWays(List<TransportRoute> parts) {
List<Way> w = new ArrayList<Way>();
for (TransportRoute t : parts) {
w.addAll(t.getForwardWays());
}
return w;
}
private List<List<TransportStop>> combineSegmentsOfSameRoute(LinkedList<List<TransportStop>> segments) {
LinkedList<List<TransportStop>> tempResultSegments = mergeSegments(segments, new LinkedList<List<TransportStop>>(), false);
return mergeSegments(tempResultSegments, new ArrayList<List<TransportStop>>(), true);
}
private <T extends List<List<TransportStop>>> T mergeSegments(LinkedList<List<TransportStop>> segments, T resultSegments,
boolean mergeMissingSegs) {
while (!segments.isEmpty()) {
List<TransportStop> firstSegment = segments.poll();
boolean merged = true;
while (merged) {
merged = false;
Iterator<List<TransportStop>> it = segments.iterator();
while (it.hasNext()) {
List<TransportStop> segmentToMerge = it.next();
if (mergeMissingSegs) {
merged = tryToMergeMissingStops(firstSegment, segmentToMerge);
} else {
merged = tryToMerge(firstSegment, segmentToMerge);
}
if (merged) {
it.remove();
break;
}
}
}
resultSegments.add(firstSegment);
}
return resultSegments;
}
private boolean tryToMerge(List<TransportStop> firstSegment, List<TransportStop> segmentToMerge) {
if (firstSegment.size() < 2 || segmentToMerge.size() < 2) {
return false;
}
// 1st we check that segments overlap by stop
int commonStopFirst = 0;
int commonStopSecond = 0;
boolean found = false;
for (; commonStopFirst < firstSegment.size(); commonStopFirst++) {
for (commonStopSecond = 0; commonStopSecond < segmentToMerge.size() && !found; commonStopSecond++) {
long lid1 = firstSegment.get(commonStopFirst).getId();
long lid2 = segmentToMerge.get(commonStopSecond).getId();
if (lid1 > 0 && lid2 == lid1) {
found = true;
break;
}
}
if (found) {
// important to increment break inside loop
break;
}
}
if (found && commonStopFirst < firstSegment.size()) {
// we've found common stop so we can merge based on stops
// merge last part first
int leftPartFirst = firstSegment.size() - commonStopFirst;
int leftPartSecond = segmentToMerge.size() - commonStopSecond;
if (leftPartFirst < leftPartSecond
|| (leftPartFirst == leftPartSecond && firstSegment.get(firstSegment.size() - 1).isMissingStop())) {
while (firstSegment.size() > commonStopFirst) {
firstSegment.remove(firstSegment.size() - 1);
}
for (int i = commonStopSecond; i < segmentToMerge.size(); i++) {
firstSegment.add(segmentToMerge.get(i));
}
}
// merge first part
if (commonStopFirst < commonStopSecond
|| (commonStopFirst == commonStopSecond && firstSegment.get(0).isMissingStop())) {
for (int i = 0; i <= commonStopFirst; i++) {
firstSegment.remove(0);
}
for (int i = commonStopSecond; i >= 0; i--) {
firstSegment.add(0, segmentToMerge.get(i));
}
}
return true;
}
return false;
}
private boolean tryToMergeMissingStops(List<TransportStop> firstSegment, List<TransportStop> segmentToMerge) {
// no common stops, so try to connect to the end or beginning
// beginning
boolean merged = false;
if (MapUtils.getDistance(firstSegment.get(0).getLocation(),
segmentToMerge.get(segmentToMerge.size() - 1).getLocation()) < MISSING_STOP_SEARCH_RADIUS
&& firstSegment.get(0).isMissingStop() && segmentToMerge.get(segmentToMerge.size() - 1).isMissingStop()) {
firstSegment.remove(0);
for (int i = segmentToMerge.size() - 2; i >= 0; i--) {
firstSegment.add(0, segmentToMerge.get(i));
}
merged = true;
} else if (MapUtils.getDistance(firstSegment.get(firstSegment.size() - 1).getLocation(),
segmentToMerge.get(0).getLocation()) < MISSING_STOP_SEARCH_RADIUS
&& segmentToMerge.get(0).isMissingStop() && firstSegment.get(firstSegment.size() - 1).isMissingStop()) {
firstSegment.remove(firstSegment.size() - 1);
for (int i = 1; i < segmentToMerge.size(); i++) {
firstSegment.add(segmentToMerge.get(i));
}
merged = true;
}
return merged;
}
private LinkedList<List<TransportStop>> parseRoutePartsToSegments(List<TransportRoute> routeParts) {
LinkedList<List<TransportStop>> segs = new LinkedList<List<TransportStop>>();
// here we assume that missing stops come in pairs <A, B, C, MISSING, MISSING, D, E...>
// we don't add segments with 1 stop cause they are irrelevant further
for (TransportRoute part : routeParts) {
List<TransportStop> newSeg = new ArrayList<TransportStop>();
for (TransportStop s : part.getForwardStops()) {
newSeg.add(s);
if (s.isMissingStop()) {
if (newSeg.size() > 1) {
segs.add(newSeg);
newSeg = new ArrayList<TransportStop>();
}
}
}
if (newSeg.size() > 1) {
segs.add(newSeg);
}
}
return segs;
}
private List<TransportRoute> findIncompleteRouteParts(TransportRoute baseRoute) throws IOException {
List<TransportRoute> allRoutes = null;
for (BinaryMapIndexReader bmir : routesFilesCache.keySet()) {
// here we could limit routeMap indexes by only certain bbox around start / end (check comment on field)
IncompleteTransportRoute ptr = bmir.getIncompleteTransportRoutes().get(baseRoute.getId());
if (ptr != null) {
TIntArrayList lst = new TIntArrayList();
while (ptr != null) {
lst.add(ptr.getRouteOffset());
ptr = ptr.getNextLinkedRoute();
}
if (lst.size() > 0) {
if (allRoutes == null) {
allRoutes = new ArrayList<TransportRoute>();
}
allRoutes.addAll(bmir.getTransportRoutes(lst.toArray()).valueCollection());
}
}
}
return allRoutes;
}
}

View file

@ -210,16 +210,25 @@ public class TurnType {
public static void setPrimaryTurnShiftOthers(int[] lanes, int lane, int turnType) {
int pt = getPrimaryTurn(lanes[lane]);
int st = getSecondaryTurn(lanes[lane]);
//int tt = getTertiaryTurn(lanes[lane]);
//int tt = getTertiaryTurn(lanes[lane]); is lost here
setPrimaryTurnAndReset(lanes, lane, turnType);
setSecondaryTurn(lanes, lane, pt);
setTertiaryTurn(lanes, lane, st);
}
public static void setSecondaryTurnShiftOthers(int[] lanes, int lane, int turnType) {
public static void setSecondaryToPrimary(int[] lanes, int lane) {
int st = getSecondaryTurn(lanes[lane]);
//int tt = getTertiaryTurn(lanes[lane]);
setSecondaryTurn(lanes, lane, turnType);
int pt = getPrimaryTurn(lanes[lane]);
setPrimaryTurn(lanes, lane, st);
setSecondaryTurn(lanes, lane, pt);
}
public static void setTertiaryToPrimary(int[] lanes, int lane) {
int st = getSecondaryTurn(lanes[lane]);
int pt = getPrimaryTurn(lanes[lane]);
int tt = getTertiaryTurn(lanes[lane]);
setPrimaryTurn(lanes, lane, tt);
setSecondaryTurn(lanes, lane, pt);
setTertiaryTurn(lanes, lane, st);
}

View file

@ -30,7 +30,7 @@ public interface VehicleRouter {
/**
* return delay in seconds (0 no obstacles)
*/
public float defineObstacle(RouteDataObject road, int point);
public float defineObstacle(RouteDataObject road, int point, boolean dir);
/**
* return delay in seconds for height obstacles
@ -40,7 +40,7 @@ public interface VehicleRouter {
/**
* return delay in seconds (0 no obstacles)
*/
public float defineRoutingObstacle(RouteDataObject road, int point);
public float defineRoutingObstacle(RouteDataObject road, int point, boolean dir);
/**
* return routing speed in m/s for vehicle for specified road

View file

@ -1,7 +1,6 @@
package net.osmand.search;
import net.osmand.Collator;
import net.osmand.OsmAndCollator;
import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapIndexReader;
@ -10,11 +9,13 @@ import net.osmand.data.City;
import net.osmand.data.LatLon;
import net.osmand.data.MapObject;
import net.osmand.data.Street;
import net.osmand.osm.AbstractPoiType;
import net.osmand.osm.MapPoiTypes;
import net.osmand.search.core.CustomSearchPoiFilter;
import net.osmand.search.core.ObjectType;
import net.osmand.search.core.SearchCoreAPI;
import net.osmand.search.core.SearchCoreFactory;
import net.osmand.search.core.SearchCoreFactory.SearchAmenityByTypeAPI;
import net.osmand.search.core.SearchCoreFactory.SearchAmenityTypesAPI;
import net.osmand.search.core.SearchCoreFactory.SearchBuildingAndIntersectionsByStreetAPI;
import net.osmand.search.core.SearchCoreFactory.SearchStreetByCityAPI;
@ -72,7 +73,7 @@ public class SearchUICore {
taskQueue = new LinkedBlockingQueue<Runnable>();
searchSettings = new SearchSettings(new ArrayList<BinaryMapIndexReader>());
searchSettings = searchSettings.setLang(locale, transliterate);
phrase = new SearchPhrase(searchSettings, OsmAndCollator.primaryCollator());
phrase = SearchPhrase.emptyPhrase(searchSettings);
currentSearchResult = new SearchResultCollection(phrase);
singleThreadedExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, taskQueue);
}
@ -325,7 +326,7 @@ public class SearchUICore {
apis.add(new SearchCoreFactory.SearchLocationAndUrlAPI());
SearchAmenityTypesAPI searchAmenityTypesAPI = new SearchAmenityTypesAPI(poiTypes);
apis.add(searchAmenityTypesAPI);
apis.add(new SearchCoreFactory.SearchAmenityByTypeAPI(poiTypes, searchAmenityTypesAPI));
apis.add(new SearchAmenityByTypeAPI(poiTypes, searchAmenityTypesAPI));
apis.add(new SearchCoreFactory.SearchAmenityByNameAPI());
SearchBuildingAndIntersectionsByStreetAPI streetsApi =
new SearchCoreFactory.SearchBuildingAndIntersectionsByStreetAPI();
@ -351,10 +352,10 @@ public class SearchUICore {
}
}
public void setFilterOrders(List<String> filterOrders) {
public void setActivePoiFiltersByOrder(List<String> filterOrders) {
for (SearchCoreAPI capi : apis) {
if (capi instanceof SearchAmenityTypesAPI) {
((SearchAmenityTypesAPI) capi).setFilterOrders(filterOrders);
((SearchAmenityTypesAPI) capi).setActivePoiFiltersByOrder(filterOrders);
}
}
}
@ -404,7 +405,7 @@ public class SearchUICore {
}
private boolean filterOneResult(SearchResult object, SearchPhrase phrase) {
NameStringMatcher nameStringMatcher = phrase.getNameStringMatcher();
NameStringMatcher nameStringMatcher = phrase.getFirstUnknownNameStringMatcher();
return nameStringMatcher.matches(object.localeName) || nameStringMatcher.matches(object.otherNames);
}
@ -515,7 +516,7 @@ public class SearchUICore {
}
currentSearchResult = collection;
if (phrase.getSettings().isExportObjects()) {
//rm.createTestJSON(collection);
rm.createTestJSON(collection);
}
rm.searchFinished(phrase);
if (onResultsComplete != null) {
@ -573,6 +574,24 @@ public class SearchUICore {
return radius;
}
public AbstractPoiType getUnselectedPoiType() {
for (SearchCoreAPI capi : apis) {
if (capi instanceof SearchAmenityByTypeAPI) {
return ((SearchAmenityByTypeAPI) capi).getUnselectedPoiType();
}
}
return null;
}
public String getCustomNameFilter() {
for (SearchCoreAPI capi : apis) {
if (capi instanceof SearchAmenityByTypeAPI) {
return ((SearchAmenityByTypeAPI) capi).getNameFilter();
}
}
return null;
}
void searchInternal(final SearchPhrase phrase, SearchResultMatcher matcher) {
preparePhrase(phrase);
ArrayList<SearchCoreAPI> lst = new ArrayList<>(apis);
@ -651,6 +670,10 @@ public class SearchUICore {
this.parentSearchResult = parentSearchResult;
return prev;
}
public SearchResult getParentSearchResult() {
return parentSearchResult;
}
public List<SearchResult> getRequestResults() {
return requestResults;
@ -710,16 +733,16 @@ public class SearchUICore {
@Override
public boolean publish(SearchResult object) {
if (phrase != null && object.otherNames != null && !phrase.getNameStringMatcher().matches(object.localeName)) {
if (phrase != null && object.otherNames != null && !phrase.getFirstUnknownNameStringMatcher().matches(object.localeName)) {
for (String s : object.otherNames) {
if (phrase.getNameStringMatcher().matches(s)) {
if (phrase.getFirstUnknownNameStringMatcher().matches(s)) {
object.alternateName = s;
break;
}
}
if (Algorithms.isEmpty(object.alternateName) && object.object instanceof Amenity) {
for (String value : ((Amenity) object.object).getAdditionalInfo().values()) {
if (phrase.getNameStringMatcher().matches(value)) {
if (phrase.getFirstUnknownNameStringMatcher().matches(value)) {
object.alternateName = value;
break;
}
@ -730,9 +753,9 @@ public class SearchUICore {
object.localeName = object.alternateName;
object.alternateName = null;
}
object.parentSearchResult = parentSearchResult;
if (matcher == null || matcher.publish(object)) {
count++;
object.parentSearchResult = parentSearchResult;
if (totalLimit == -1 || count < totalLimit) {
requestResults.add(object);
}
@ -740,6 +763,7 @@ public class SearchUICore {
}
return false;
}
@Override
public boolean isCancelled() {
boolean cancelled = request != requestNumber.get();
@ -754,14 +778,28 @@ public class SearchUICore {
return exportedCities;
}
public void exportObject(MapObject object) {
public void exportObject(SearchPhrase phrase, MapObject object) {
double maxDistance = phrase.getSettings().getExportSettings().getMaxDistance();
if (maxDistance > 0) {
double distance = MapUtils.getDistance(phrase.getSettings().getOriginalLocation(), object.getLocation());
if (distance > maxDistance) {
return;
}
}
if (exportedObjects == null) {
exportedObjects = new ArrayList<>();
}
exportedObjects.add(object);
}
public void exportCity(City city) {
public void exportCity(SearchPhrase phrase, City city) {
double maxDistance = phrase.getSettings().getExportSettings().getMaxDistance();
if (maxDistance > 0) {
double distance = MapUtils.getDistance(phrase.getSettings().getOriginalLocation(), city.getLocation());
if (distance > maxDistance) {
return;
}
}
if (exportedCities == null) {
exportedCities = new ArrayList<>();
}
@ -781,22 +819,23 @@ public class SearchUICore {
cities = new HashSet<>();
}
Set<Street> streets = new HashSet<>();
for (MapObject obj : exportedObjects) {
if (obj instanceof Amenity) {
amenities.add((Amenity) obj);
} else if (obj instanceof Street) {
Street street = (Street) obj;
streets.add(street);
if (street.getCity() != null) {
final City city = street.getCity();
if (exportedObjects != null) {
for (MapObject obj : exportedObjects) {
if (obj instanceof Amenity) {
amenities.add((Amenity) obj);
} else if (obj instanceof Street) {
Street street = (Street) obj;
streets.add(street);
if (street.getCity() != null) {
final City city = street.getCity();
cities.add(city);
streetCities.add(city);
}
} else if (obj instanceof City) {
City city = (City) obj;
cities.add(city);
streetCities.add(city);
matchedCities.add(city);
}
} else if (obj instanceof City) {
City city = (City) obj;
cities.add(city);
matchedCities.add(city);
}
}
for (City city : cities) {
@ -810,7 +849,7 @@ public class SearchUICore {
SearchExportSettings exportSettings = phrase.getSettings().getExportSettings();
json.put("settings", phrase.getSettings().toJSON());
json.put("phrase", phrase.getRawUnknownSearchPhrase());
json.put("phrase", phrase.getFullSearchPhrase());
if (searchResult.searchResults != null && searchResult.searchResults.size() > 0) {
JSONArray resultsArr = new JSONArray();
for (SearchResult r : searchResult.searchResults) {
@ -829,7 +868,7 @@ public class SearchUICore {
JSONArray citiesArr = new JSONArray();
for (City city : cities) {
final JSONObject cityObj = city.toJSON(exportSettings.isExportBuildings());
if (exportedCities.contains(city)) {
if (exportedCities != null && exportedCities.contains(city)) {
if (!exportSettings.isExportEmptyCities()) {
continue;
}
@ -848,15 +887,134 @@ public class SearchUICore {
return json;
}
}
private enum ResultCompareStep {
TOP_VISIBLE,
FOUND_WORD_COUNT, // more is better (top)
UNKNOWN_PHRASE_MATCH_WEIGHT, // more is better (top)
COMPARE_AMENITY_TYPE_ADDITIONAL,
SEARCH_DISTANCE_IF_NOT_BY_NAME,
COMPARE_FIRST_NUMBER_IN_NAME,
COMPARE_DISTANCE_TO_PARENT_SEARCH_RESULT, // makes sense only for inner subqueries
COMPARE_BY_NAME,
COMPARE_BY_DISTANCE,
AMENITY_LAST_AND_SORT_BY_SUBTYPE
;
// -1 - means 1st is less (higher) than 2nd
public int compare(SearchResult o1, SearchResult o2, SearchResultComparator c) {
switch(this) {
case TOP_VISIBLE:
boolean topVisible1 = ObjectType.isTopVisible(o1.objectType);
boolean topVisible2 = ObjectType.isTopVisible(o2.objectType);
if (topVisible1 != topVisible2) {
// -1 - means 1st is less than 2nd
return topVisible1 ? -1 : 1;
}
break;
case FOUND_WORD_COUNT:
if (o1.getFoundWordCount() != o2.getFoundWordCount()) {
return -Algorithms.compare(o1.getFoundWordCount(), o2.getFoundWordCount());
}
break;
case UNKNOWN_PHRASE_MATCH_WEIGHT:
// here we check how much each sub search result matches the phrase
// also we sort it by type house -> street/poi -> city/postcode/village/other
if (o1.getUnknownPhraseMatchWeight() != o2.getUnknownPhraseMatchWeight()) {
return -Double.compare(o1.getUnknownPhraseMatchWeight(), o2.getUnknownPhraseMatchWeight());
}
break;
case SEARCH_DISTANCE_IF_NOT_BY_NAME:
if (!c.sortByName) {
double s1 = o1.getSearchDistance(c.loc);
double s2 = o2.getSearchDistance(c.loc);
if (s1 != s2) {
return Double.compare(s1, s2);
}
}
break;
case COMPARE_FIRST_NUMBER_IN_NAME: {
String localeName1 = o1.localeName == null ? "" : o1.localeName;
String localeName2 = o2.localeName == null ? "" : o2.localeName;
int st1 = Algorithms.extractFirstIntegerNumber(localeName1);
int st2 = Algorithms.extractFirstIntegerNumber(localeName2);
if (st1 != st2) {
return Algorithms.compare(st1, st2);
}
break;
}
case COMPARE_AMENITY_TYPE_ADDITIONAL: {
if(o1.object instanceof AbstractPoiType && o2.object instanceof AbstractPoiType ) {
boolean additional1 = ((AbstractPoiType) o1.object).isAdditional();
boolean additional2 = ((AbstractPoiType) o2.object).isAdditional();
if (additional1 != additional2) {
// -1 - means 1st is less than 2nd
return additional1 ? 1 : -1;
}
}
break;
}
case COMPARE_DISTANCE_TO_PARENT_SEARCH_RESULT:
double ps1 = o1.parentSearchResult == null ? 0 : o1.parentSearchResult.getSearchDistance(c.loc);
double ps2 = o2.parentSearchResult == null ? 0 : o2.parentSearchResult.getSearchDistance(c.loc);
if (ps1 != ps2) {
return Double.compare(ps1, ps2);
}
break;
case COMPARE_BY_NAME: {
String localeName1 = o1.localeName == null ? "" : o1.localeName;
String localeName2 = o2.localeName == null ? "" : o2.localeName;
int cmp = c.collator.compare(localeName1, localeName2);
if (cmp != 0) {
return cmp;
}
break;
}
case COMPARE_BY_DISTANCE:
double s1 = o1.getSearchDistance(c.loc, 1);
double s2 = o2.getSearchDistance(c.loc, 1);
if (s1 != s2) {
return Double.compare(s1, s2);
}
break;
case AMENITY_LAST_AND_SORT_BY_SUBTYPE: {
boolean am1 = o1.object instanceof Amenity;
boolean am2 = o2.object instanceof Amenity;
if (am1 != am2) {
// amenity second
return am1 ? 1 : -1;
} else if (am1 && am2) {
// here 2 points are amenity
Amenity a1 = (Amenity) o1.object;
Amenity a2 = (Amenity) o2.object;
String type1 = a1.getType().getKeyName();
String type2 = a2.getType().getKeyName();
int cmp = c.collator.compare(type1, type2);
if (cmp != 0) {
return cmp;
}
String subType1 = a1.getSubType() == null ? "" : a1.getSubType();
String subType2 = a2.getSubType() == null ? "" : a2.getSubType();
cmp = c.collator.compare(subType1, subType2);
if (cmp != 0) {
return cmp;
}
}
break;
}
}
return 0;
}
}
public static class SearchResultComparator implements Comparator<SearchResult> {
private SearchPhrase sp;
private Collator collator;
private LatLon loc;
private boolean sortByName;
public SearchResultComparator(SearchPhrase sp) {
this.sp = sp;
this.collator = sp.getCollator();
loc = sp.getLastTokenLocation();
sortByName = sp.isSortByName();
@ -865,66 +1023,10 @@ public class SearchUICore {
@Override
public int compare(SearchResult o1, SearchResult o2) {
boolean topVisible1 = ObjectType.isTopVisible(o1.objectType);
boolean topVisible2 = ObjectType.isTopVisible(o2.objectType);
if (topVisible1 != topVisible2) {
// -1 - means 1st is less than 2nd
return topVisible1 ? -1 : 1;
}
if (o1.getUnknownPhraseMatchWeight() != o2.getUnknownPhraseMatchWeight()) {
return -Double.compare(o1.getUnknownPhraseMatchWeight(), o2.getUnknownPhraseMatchWeight());
}
if (o1.getFoundWordCount() != o2.getFoundWordCount()) {
return -Algorithms.compare(o1.getFoundWordCount(), o2.getFoundWordCount());
}
if (!sortByName) {
double s1 = o1.getSearchDistance(loc);
double s2 = o2.getSearchDistance(loc);
if (s1 != s2) {
return Double.compare(s1, s2);
}
}
String localeName1 = o1.localeName == null ? "" : o1.localeName;
String localeName2 = o2.localeName == null ? "" : o2.localeName;
int st1 = Algorithms.extractFirstIntegerNumber(localeName1);
int st2 = Algorithms.extractFirstIntegerNumber(localeName2);
if (st1 != st2) {
return Algorithms.compare(st1, st2);
}
double s1 = o1.getSearchDistance(loc, 1);
double s2 = o2.getSearchDistance(loc, 1);
double ps1 = o1.parentSearchResult == null ? 0 : o1.parentSearchResult.getSearchDistance(loc);
double ps2 = o2.parentSearchResult == null ? 0 : o2.parentSearchResult.getSearchDistance(loc);
if (ps1 != ps2) {
return Double.compare(ps1, ps2);
}
int cmp = collator.compare(localeName1, localeName2);
if (cmp != 0) {
return cmp;
}
if (s1 != s2) {
return Double.compare(s1, s2);
}
boolean am1 = o1.object instanceof Amenity;
boolean am2 = o2.object instanceof Amenity;
if (am1 != am2) {
return Boolean.compare(am1, am2);
} else if (am1 && am2) {
// here 2 points are amenity
Amenity a1 = (Amenity) o1.object;
Amenity a2 = (Amenity) o2.object;
String type1 = a1.getType().getKeyName();
String type2 = a2.getType().getKeyName();
cmp = collator.compare(type1, type2);
if (cmp != 0) {
return cmp;
}
String subType1 = a1.getSubType() == null ? "" : a1.getSubType();
String subType2 = a2.getSubType() == null ? "" : a2.getSubType();
cmp = collator.compare(subType1, subType2);
if (cmp != 0) {
return cmp;
for(ResultCompareStep step : ResultCompareStep.values()) {
int r = step.compare(o1, o2, this);
if(r != 0) {
return r;
}
}
return 0;

View file

@ -45,27 +45,24 @@ public enum ObjectType {
return null;
}
public static double getTypeWeight(ObjectType t) {
public static int getTypeWeight(ObjectType t) {
if (t == null) {
return 1.0;
return 1;
}
switch (t) {
case CITY:
return 1.0;
case VILLAGE:
return 1.0;
case POSTCODE:
return 1.0;
case STREET:
return 2.0;
case HOUSE:
return 3.0;
case STREET_INTERSECTION:
return 3.0;
return 4;
case STREET:
return 3;
case CITY:
case VILLAGE:
case POSTCODE:
return 2;
case POI:
return 2.0;
return 1;
default:
return 1.0;
return 1;
}
}
}

View file

@ -3,30 +3,29 @@ package net.osmand.search.core;
public class SearchExportSettings {
private boolean exportEmptyCities;
private boolean exportBuildings;
private double maxDistance;
public SearchExportSettings() {
exportEmptyCities = true;
exportBuildings = true;
maxDistance = -1;
}
public SearchExportSettings(boolean exportEmptyCities, boolean exportBuildings) {
public SearchExportSettings(boolean exportEmptyCities, boolean exportBuildings, double maxDistance) {
this.exportEmptyCities = exportEmptyCities;
this.exportBuildings = exportBuildings;
this.maxDistance = maxDistance;
}
public boolean isExportEmptyCities() {
return exportEmptyCities;
}
public void setExportEmptyCities(boolean exportEmptyCities) {
this.exportEmptyCities = exportEmptyCities;
}
public boolean isExportBuildings() {
return exportBuildings;
}
public void setExportBuildings(boolean exportBuildings) {
this.exportBuildings = exportBuildings;
public double getMaxDistance() {
return maxDistance;
}
}

View file

@ -1,7 +1,7 @@
package net.osmand.search.core;
import net.osmand.Collator;
import net.osmand.CollatorStringMatcher;
import net.osmand.CollatorStringMatcher;import net.osmand.OsmAndCollator;
import net.osmand.CollatorStringMatcher.StringMatcherMode;
import net.osmand.StringMatcher;
import net.osmand.binary.BinaryMapIndexReader;
@ -9,7 +9,6 @@ import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.CommonWords;
import net.osmand.data.LatLon;
import net.osmand.data.QuadRect;
import net.osmand.osm.AbstractPoiType;
import net.osmand.util.Algorithms;
import net.osmand.util.LocationParser;
import net.osmand.util.MapUtils;
@ -27,31 +26,41 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;
//immutable object
// Immutable object !
public class SearchPhrase {
private List<SearchWord> words = new ArrayList<>();
private List<String> unknownWords = new ArrayList<>();
private List<NameStringMatcher> unknownWordsMatcher = new ArrayList<>();
private String unknownSearchWordTrim;
private String rawUnknownSearchPhrase = "";
private String unknownSearchPhrase = "";
private AbstractPoiType unknownSearchWordPoiType;
private List<AbstractPoiType> unknownSearchWordPoiTypes = null;
private NameStringMatcher sm;
private SearchSettings settings;
private List<BinaryMapIndexReader> indexes;
private QuadRect cache1kmRect;
private boolean lastUnknownSearchWordComplete;
private static final String DELIMITER = " ";
public static final String DELIMITER = " ";
private static final String ALLDELIMITERS = "\\s|,";
private static final Pattern reg = Pattern.compile(ALLDELIMITERS);
private Collator clt;
private static Comparator<String> commonWordsComparator;
private static Set<String> conjunctions = new TreeSet<>();
private final Collator clt;
private final SearchSettings settings;
private List<BinaryMapIndexReader> indexes;
// Object consists of 2 part [known + unknown]
private String fullTextSearchPhrase = "";
private String unknownSearchPhrase = "";
// words to be used for words span
private List<SearchWord> words = new ArrayList<>();
// Words of 2 parts
private String firstUnknownSearchWord = "";
private List<String> otherUnknownWords = new ArrayList<>();
private boolean lastUnknownSearchWordComplete;
// Main unknown word used for search
private String mainUnknownWordToSearch = null;
private boolean mainUnknownSearchWordComplete;
// Name Searchers
private NameStringMatcher firstUnknownNameStringMatcher;
private NameStringMatcher mainUnknownNameStringMatcher;
private List<NameStringMatcher> unknownWordsMatcher = new ArrayList<>();
private QuadRect cache1kmRect;
static {
// the
conjunctions.add("the");
@ -121,7 +130,12 @@ public class SearchPhrase {
int i1 = CommonWords.getCommonSearch(o1.toLowerCase());
int i2 = CommonWords.getCommonSearch(o2.toLowerCase());
if (i1 != i2) {
return icompare(i1, i2);
if(i1 == -1) {
return -1;
} else if(i2 == -1) {
return 1;
}
return -icompare(i1, i2);
}
// compare length without numbers to not include house numbers
return -icompare(lengthWithoutNumbers(o1), lengthWithoutNumbers(o2));
@ -135,7 +149,7 @@ public class SearchPhrase {
}
public SearchPhrase(SearchSettings settings, Collator clt) {
private SearchPhrase(SearchSettings settings, Collator clt) {
this.settings = settings;
this.clt = clt;
}
@ -144,97 +158,181 @@ public class SearchPhrase {
return clt;
}
public SearchPhrase generateNewPhrase(String text, SearchSettings settings) {
SearchPhrase sp = new SearchPhrase(settings, this.clt);
String restText = text;
String textToSearch = Algorithms.normalizeSearchText(text);
List<SearchWord> leftWords = this.words;
String thisTxt = getText(true);
List<SearchWord> foundWords = new ArrayList<>();
if (text.startsWith(thisTxt)) {
// string is longer
restText = text.substring(getText(false).length());
sp.words = new ArrayList<>(this.words);
textToSearch = text.substring(getText(false).length());
foundWords.addAll(this.words);
leftWords = leftWords.subList(leftWords.size(), leftWords.size());
}
for(SearchWord w : leftWords) {
if(restText.startsWith(w.getWord() + DELIMITER)) {
sp.words.add(w);
restText = restText.substring(w.getWord().length() + DELIMITER.length());
for (SearchWord w : leftWords) {
if (textToSearch.startsWith(w.getWord() + DELIMITER)) {
foundWords.add(w);
textToSearch = textToSearch.substring(w.getWord().length() + DELIMITER.length());
} else {
break;
}
}
sp.rawUnknownSearchPhrase = text;
sp.unknownSearchPhrase = restText;
sp.unknownWords.clear();
sp.unknownWordsMatcher.clear();
if (!reg.matcher(restText).find()) {
sp.unknownSearchWordTrim = sp.unknownSearchPhrase.trim();
SearchPhrase sp = createNewSearchPhrase(settings, text, foundWords, textToSearch);
return sp;
}
public static SearchPhrase emptyPhrase() {
return emptyPhrase(null);
}
public static SearchPhrase emptyPhrase(SearchSettings settings) {
return emptyPhrase(settings, OsmAndCollator.primaryCollator());
}
public static SearchPhrase emptyPhrase(SearchSettings settings, Collator clt) {
return new SearchPhrase(settings, clt);
}
// init search phrase
private SearchPhrase createNewSearchPhrase(SearchSettings settings, String fullText, List<SearchWord> foundWords,
String textToSearch) {
SearchPhrase sp = new SearchPhrase(settings, this.clt);
sp.words = foundWords;
sp.fullTextSearchPhrase = fullText;
sp.unknownSearchPhrase = textToSearch;
sp.lastUnknownSearchWordComplete = isTextComplete(fullText) ;
if (!reg.matcher(textToSearch).find()) {
sp.firstUnknownSearchWord = sp.unknownSearchPhrase.trim();
} else {
sp.unknownSearchWordTrim = "";
String[] ws = restText.split(ALLDELIMITERS);
sp.firstUnknownSearchWord = "";
String[] ws = textToSearch.split(ALLDELIMITERS);
boolean first = true;
for (int i = 0; i < ws.length ; i++) {
String wd = ws[i].trim();
if (wd.length() > 0 && !conjunctions.contains(wd.toLowerCase())) {
boolean conjunction = conjunctions.contains(wd.toLowerCase());
boolean lastAndIncomplete = i == ws.length - 1 && !sp.lastUnknownSearchWordComplete;
if (wd.length() > 0 && (!conjunction || lastAndIncomplete)) {
if (first) {
sp.unknownSearchWordTrim = wd;
sp.firstUnknownSearchWord = wd;
first = false;
} else {
sp.unknownWords.add(wd);
sp.otherUnknownWords.add(wd);
}
}
}
}
sp.lastUnknownSearchWordComplete = false;
if (text.length() > 0 ) {
char ch = text.charAt(text.length() - 1);
sp.lastUnknownSearchWordComplete = ch == ' ' || ch == ',' || ch == '\r' || ch == '\n'
|| ch == ';';
}
return sp;
}
public int countWords(String w) {
int cnt = 0;
if (!Algorithms.isEmpty(w)) {
String[] ws = w.split(ALLDELIMITERS);
for (int i = 0; i < ws.length; i++) {
String wd = ws[i].trim();
if (wd.length() > 0) {
cnt++;
}
}
}
return cnt;
}
public SearchPhrase selectWord(SearchResult res, List<String> unknownWords, boolean lastComplete) {
SearchPhrase sp = new SearchPhrase(this.settings, this.clt);
addResult(res, sp);
SearchResult prnt = res.parentSearchResult;
while (prnt != null) {
addResult(prnt, sp);
prnt = prnt.parentSearchResult;
}
sp.words.addAll(0, this.words);
if (unknownWords != null) {
sp.lastUnknownSearchWordComplete = lastComplete;
StringBuilder genUnknownSearchPhrase = new StringBuilder();
for (int i = 0; i < unknownWords.size(); i++) {
if (i == 0) {
sp.firstUnknownSearchWord = unknownWords.get(0);
} else {
sp.otherUnknownWords.add(unknownWords.get(i));
}
genUnknownSearchPhrase.append(unknownWords.get(i)).append(" ");
}
sp.fullTextSearchPhrase = fullTextSearchPhrase;
sp.unknownSearchPhrase = genUnknownSearchPhrase.toString().trim();
}
return sp;
}
private void calcMainUnknownWordToSearch() {
if (mainUnknownWordToSearch != null) {
return;
}
List<String> unknownSearchWords = otherUnknownWords;
mainUnknownWordToSearch = firstUnknownSearchWord;
mainUnknownSearchWordComplete = lastUnknownSearchWordComplete;
if (unknownSearchWords.size() > 0) {
mainUnknownSearchWordComplete = true;
List<String> searchWords = new ArrayList<>(unknownSearchWords);
searchWords.add(0, getFirstUnknownSearchWord());
Collections.sort(searchWords, commonWordsComparator);
for (String s : searchWords) {
if (s.length() > 0 && !Character.isDigit(s.charAt(0)) && !LocationParser.isValidOLC(s)) {
mainUnknownWordToSearch = s.trim();
if (mainUnknownWordToSearch.endsWith(".")) {
mainUnknownWordToSearch = mainUnknownWordToSearch.substring(0,
mainUnknownWordToSearch.length() - 1);
mainUnknownSearchWordComplete = false;
}
int unknownInd = unknownSearchWords.indexOf(s);
if (!lastUnknownSearchWordComplete && unknownSearchWords.size() - 1 == unknownInd) {
mainUnknownSearchWordComplete = false;
}
break;
}
}
}
}
public List<SearchWord> getWords() {
return words;
}
public boolean isUnknownSearchWordComplete() {
return lastUnknownSearchWordComplete || unknownWords.size() > 0 || unknownSearchWordPoiType != null;
public boolean isMainUnknownSearchWordComplete() {
// return lastUnknownSearchWordComplete || otherUnknownWords.size() > 0 || unknownSearchWordPoiType != null;
return mainUnknownSearchWordComplete;
}
public boolean isLastUnknownSearchWordComplete() {
return lastUnknownSearchWordComplete;
}
public boolean hasMoreThanOneUnknownSearchWord() {
return otherUnknownWords.size() > 0;
}
public List<String> getUnknownSearchWords() {
return unknownWords;
}
public List<String> getUnknownSearchWords(Collection<String> exclude) {
if(exclude == null || unknownWords.size() == 0 || exclude.size() == 0) {
return unknownWords;
}
List<String> l = new ArrayList<>();
for(String uw : unknownWords) {
if(exclude == null || !exclude.contains(uw)) {
l.add(uw);
}
}
return l;
return otherUnknownWords;
}
public String getUnknownSearchWord() {
return unknownSearchWordTrim;
public String getFirstUnknownSearchWord() {
return firstUnknownSearchWord;
}
public boolean isFirstUnknownSearchWordComplete() {
return hasMoreThanOneUnknownSearchWord() || isLastUnknownSearchWordComplete();
}
public String getRawUnknownSearchPhrase() {
return rawUnknownSearchPhrase;
public String getFullSearchPhrase() {
return fullTextSearchPhrase;
}
public String getUnknownSearchPhrase() {
@ -242,66 +340,9 @@ public class SearchPhrase {
}
public boolean isUnknownSearchWordPresent() {
return unknownSearchWordTrim.length() > 0;
return firstUnknownSearchWord.length() > 0;
}
public int getUnknownSearchWordLength() {
return unknownSearchWordTrim.length() ;
}
public AbstractPoiType getUnknownSearchWordPoiType() {
return unknownSearchWordPoiType;
}
public void setUnknownSearchWordPoiType(AbstractPoiType unknownSearchWordPoiType) {
this.unknownSearchWordPoiType = unknownSearchWordPoiType;
}
public boolean hasUnknownSearchWordPoiType() {
return unknownSearchWordPoiType != null;
}
public String getPoiNameFilter() {
return getPoiNameFilter(unknownSearchWordPoiType);
}
public boolean hasUnknownSearchWordPoiTypes() {
return unknownSearchWordPoiTypes != null && unknownSearchWordPoiTypes.size() > 0;
}
public List<AbstractPoiType> getUnknownSearchWordPoiTypes() {
return unknownSearchWordPoiTypes;
}
public void setUnknownSearchWordPoiTypes(List<AbstractPoiType> unknownSearchWordPoiTypes) {
this.unknownSearchWordPoiTypes = unknownSearchWordPoiTypes;
for (AbstractPoiType pt : unknownSearchWordPoiTypes) {
if (getPoiNameFilter(pt) != null) {
setUnknownSearchWordPoiType(pt);
break;
}
}
}
public String getPoiNameFilter(AbstractPoiType pt) {
String nameFilter = null;
if (pt != null) {
NameStringMatcher nm = getNameStringMatcher(getUnknownSearchWord(), true);
String unknownSearchPhrase = getUnknownSearchPhrase();
String enTranslation = pt.getEnTranslation();
String translation = pt.getTranslation();
String synonyms = pt.getSynonyms();
if (unknownSearchPhrase.length() >= enTranslation.length() && nm.matches(enTranslation)) {
nameFilter = unknownSearchPhrase.substring(enTranslation.length()).trim();
} else if (unknownSearchPhrase.length() >= translation.length() && nm.matches(translation)) {
nameFilter = unknownSearchPhrase.substring(translation.length()).trim();
} else if (unknownSearchPhrase.length() >= synonyms.length() && nm.matches(synonyms)) {
nameFilter = unknownSearchPhrase.substring(synonyms.length()).trim();
}
}
return nameFilter;
}
public QuadRect getRadiusBBoxToSearch(int radius) {
int radiusInMeters = getRadiusSearch(radius);
QuadRect cache1kmRect = get1km31Rect();
@ -454,27 +495,6 @@ public class SearchPhrase {
return selectWord(res, null, false);
}
public SearchPhrase selectWord(SearchResult res, List<String> unknownWords, boolean lastComplete) {
SearchPhrase sp = new SearchPhrase(this.settings, this.clt);
addResult(res, sp);
SearchResult prnt = res.parentSearchResult;
while(prnt != null) {
addResult(prnt, sp);
prnt = prnt.parentSearchResult;
}
sp.words.addAll(0, this.words);
if(unknownWords != null) {
sp.lastUnknownSearchWordComplete = lastComplete;
for (int i = 0; i < unknownWords.size(); i++) {
if (i == 0) {
sp.unknownSearchWordTrim = unknownWords.get(0);
} else {
sp.unknownWords.add(unknownWords.get(i));
}
}
}
return sp;
}
private void addResult(SearchResult res, SearchPhrase sp) {
SearchWord sw = new SearchWord(res.wordsSpan != null ? res.wordsSpan : res.localeName.trim(), res);
@ -504,16 +524,32 @@ public class SearchPhrase {
return null;
}
public NameStringMatcher getNameStringMatcher() {
if(sm != null) {
return sm;
public NameStringMatcher getMainUnknownNameStringMatcher() {
calcMainUnknownWordToSearch();
if (mainUnknownNameStringMatcher == null) {
mainUnknownNameStringMatcher = getNameStringMatcher(mainUnknownWordToSearch, mainUnknownSearchWordComplete);
}
sm = getNameStringMatcher(unknownSearchWordTrim, lastUnknownSearchWordComplete);
return sm;
return mainUnknownNameStringMatcher;
}
public NameStringMatcher getFirstUnknownNameStringMatcher() {
if (firstUnknownNameStringMatcher == null) {
firstUnknownNameStringMatcher = getNameStringMatcher(firstUnknownSearchWord, isFirstUnknownSearchWordComplete());
}
return firstUnknownNameStringMatcher;
}
public NameStringMatcher getUnknownNameStringMatcher(int i) {
while (unknownWordsMatcher.size() <= i) {
int ind = unknownWordsMatcher.size();
boolean completeMatch = ind < otherUnknownWords.size() - 1 || isLastUnknownSearchWordComplete();
unknownWordsMatcher.add(getNameStringMatcher(otherUnknownWords.get(ind), completeMatch));
}
return unknownWordsMatcher.get(i);
}
public NameStringMatcher getNameStringMatcher(String word, boolean complete) {
private NameStringMatcher getNameStringMatcher(String word, boolean complete) {
return new NameStringMatcher(word,
(complete ?
StringMatcherMode.CHECK_EQUALS_FROM_SPACE :
@ -535,12 +571,12 @@ public class SearchPhrase {
}
}
public String getText(boolean includeLastWord) {
public String getText(boolean includeUnknownPart) {
StringBuilder sb = new StringBuilder();
for(SearchWord s : words) {
sb.append(s.getWord()).append(DELIMITER.trim() + " ");
sb.append(s.getWord()).append(DELIMITER);
}
if(includeLastWord) {
if(includeUnknownPart) {
sb.append(unknownSearchPhrase);
}
return sb.toString();
@ -549,11 +585,11 @@ public class SearchPhrase {
public String getTextWithoutLastWord() {
StringBuilder sb = new StringBuilder();
List<SearchWord> words = new ArrayList<>(this.words);
if(Algorithms.isEmpty(unknownSearchWordTrim) && words.size() > 0) {
if (Algorithms.isEmpty(unknownSearchPhrase.trim()) && words.size() > 0) {
words.remove(words.size() - 1);
}
for(SearchWord s : words) {
sb.append(s.getWord()).append(DELIMITER.trim() + " ");
sb.append(s.getWord()).append(DELIMITER);
}
return sb.toString();
}
@ -696,8 +732,8 @@ public class SearchPhrase {
private CollatorStringMatcher sm;
public NameStringMatcher(String lastWordTrim, StringMatcherMode mode) {
sm = new CollatorStringMatcher(lastWordTrim, mode);
public NameStringMatcher(String namePart, StringMatcherMode mode) {
sm = new CollatorStringMatcher(namePart, mode);
}
public boolean matches(Collection<String> map) {
@ -719,34 +755,60 @@ public class SearchPhrase {
}
public void countUnknownWordsMatch(SearchResult sr) {
countUnknownWordsMatch(sr, sr.localeName, sr.otherNames);
public int countUnknownWordsMatchMainResult(SearchResult sr) {
return countUnknownWordsMatch(sr, sr.localeName, sr.otherNames, 0);
}
public void countUnknownWordsMatch(SearchResult sr, String localeName, Collection<String> otherNames) {
if(unknownWords.size() > 0) {
for(int i = 0; i < unknownWords.size(); i++) {
if(unknownWordsMatcher.size() == i) {
unknownWordsMatcher.add(new NameStringMatcher(unknownWords.get(i),
i < unknownWords.size() - 1 || isLastUnknownSearchWordComplete() ? StringMatcherMode.CHECK_EQUALS_FROM_SPACE :
StringMatcherMode.CHECK_STARTS_FROM_SPACE));
public int countUnknownWordsMatchMainResult(SearchResult sr, int amountMatchingWords) {
return countUnknownWordsMatch(sr, sr.localeName, sr.otherNames, amountMatchingWords);
}
public int countUnknownWordsMatch(SearchResult sr, String localeName, Collection<String> otherNames, int amountMatchingWords) {
int r = 0;
if (otherUnknownWords.size() > 0) {
for (int i = 0; i < otherUnknownWords.size(); i++) {
boolean match = false;
if (i < amountMatchingWords - 1) {
match = true;
} else {
NameStringMatcher ms = getUnknownNameStringMatcher(i);
if (ms.matches(localeName) || ms.matches(otherNames)) {
match = true;
}
}
NameStringMatcher ms = unknownWordsMatcher.get(i);
if(ms.matches(localeName) || ms.matches(otherNames)) {
if(sr.otherWordsMatch == null) {
if (match) {
if (sr.otherWordsMatch == null) {
sr.otherWordsMatch = new TreeSet<>();
}
sr.otherWordsMatch.add(unknownWords.get(i));
sr.otherWordsMatch.add(otherUnknownWords.get(i));
r++;
}
}
}
if(!sr.firstUnknownWordMatches) {
sr.firstUnknownWordMatches = localeName.equals(getUnknownSearchWord()) ||
getNameStringMatcher().matches(localeName) ||
getNameStringMatcher().matches(otherNames);
if (amountMatchingWords > 0) {
sr.firstUnknownWordMatches = true;
r++;
} else {
boolean match = localeName.equals(getFirstUnknownSearchWord())
|| getFirstUnknownNameStringMatcher().matches(localeName)
|| getFirstUnknownNameStringMatcher().matches(otherNames);
if(match) {
r++;
}
sr.firstUnknownWordMatches = match || sr.firstUnknownWordMatches;
}
return r;
}
public String getLastUnknownSearchWord() {
if(otherUnknownWords.size() > 0) {
return otherUnknownWords.get(otherUnknownWords.size() - 1);
}
return firstUnknownSearchWord;
}
public int getRadiusSearch(int meters, int radiusLevel) {
int res = meters;
for(int k = 0; k < radiusLevel; k++) {
@ -767,17 +829,38 @@ public class SearchPhrase {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
public String getUnknownWordToSearchBuilding() {
List<String> unknownSearchWords = getUnknownSearchWords();
if(unknownSearchWords.size() > 0 && Algorithms.extractFirstIntegerNumber(getUnknownSearchWord()) == 0) {
for(String wrd : unknownSearchWords) {
if(Algorithms.extractFirstIntegerNumber(wrd) != 0) {
return wrd;
private int getUnknownWordToSearchBuildingInd() {
if (otherUnknownWords.size() > 0 && Algorithms.extractFirstIntegerNumber(getFirstUnknownSearchWord()) == 0) {
int ind = 0;
for (String wrd : otherUnknownWords) {
ind++;
if (Algorithms.extractFirstIntegerNumber(wrd) != 0) {
return ind;
}
}
}
return getUnknownSearchWord();
}
return 0;
}
public NameStringMatcher getUnknownWordToSearchBuildingNameMatcher() {
int ind = getUnknownWordToSearchBuildingInd();
if(ind > 0) {
return getUnknownNameStringMatcher(ind - 1);
} else {
return getFirstUnknownNameStringMatcher();
}
}
public String getUnknownWordToSearchBuilding() {
int ind = getUnknownWordToSearchBuildingInd();
if(ind > 0) {
return otherUnknownWords.get(ind - 1);
} else {
return firstUnknownSearchWord;
}
}
private static int lengthWithoutNumbers(String s) {
int len = 0;
@ -792,20 +875,23 @@ public class SearchPhrase {
}
public String getUnknownWordToSearch() {
List<String> unknownSearchWords = getUnknownSearchWords();
String wordToSearch = getUnknownSearchWord();
if (unknownSearchWords.size() > 0) {
List<String> searchWords = new ArrayList<>(unknownSearchWords);
searchWords.add(0, getUnknownSearchWord());
Collections.sort(searchWords, commonWordsComparator);
for (String s : searchWords) {
if (s.length() > 0 && !Character.isDigit(s.charAt(0)) && !LocationParser.isValidOLC(s)) {
return s;
}
}
calcMainUnknownWordToSearch();
return mainUnknownWordToSearch;
}
private boolean isTextComplete(String fullText) {
boolean lastUnknownSearchWordComplete = false;
if (fullText.length() > 0 ) {
char ch = fullText.charAt(fullText.length() - 1);
lastUnknownSearchWordComplete = ch == ' ' || ch == ',' || ch == '\r' || ch == '\n'
|| ch == ';';
}
return wordToSearch;
return lastUnknownSearchWordComplete;
}
}

View file

@ -16,40 +16,69 @@ import java.util.Collection;
public class SearchResult {
// search phrase that makes search result valid
public SearchPhrase requiredSearchPhrase;
// internal package fields (used for sorting)
public SearchResult parentSearchResult;
String wordsSpan ;
boolean firstUnknownWordMatches;
Collection<String> otherWordsMatch = null;
public Object object;
public ObjectType objectType;
public BinaryMapIndexReader file;
public double priority;
public double priorityDistance;
public String wordsSpan ;
public SearchResult parentSearchResult;
public Collection<String> otherWordsMatch = null;
public boolean firstUnknownWordMatches = true;
public boolean unknownPhraseMatches = false;
public LatLon location;
public int preferredZoom = 15;
public String localeName;
public String alternateName;
public Collection<String> otherNames;
public String localeRelatedObjectName;
public Object relatedObject;
public double distRelatedObjectName;
public SearchResult(SearchPhrase sp) {
this.requiredSearchPhrase = sp;
}
private static final double MAX_TYPE_WEIGHT = 10;
// maximum corresponds to the top entry
public double getUnknownPhraseMatchWeight() {
return getUnknownPhraseMatchWeight(false);
// if result is a complete match in the search we prioritize it higher
return getSumPhraseMatchWeight() / Math.pow(MAX_TYPE_WEIGHT, getDepth() - 1);
}
private double getUnknownPhraseMatchWeight(boolean isHouse) {
double res = 0;
isHouse = isHouse || objectType == ObjectType.HOUSE;
if (unknownPhraseMatches) {
res = isHouse ? ObjectType.getTypeWeight(ObjectType.HOUSE) : ObjectType.getTypeWeight(objectType);
}
if (res == 0 && parentSearchResult != null) {
return parentSearchResult.getUnknownPhraseMatchWeight(isHouse);
public double getSumPhraseMatchWeight() {
// if result is a complete match in the search we prioritize it higher
boolean match = requiredSearchPhrase.countWords(localeName) <= getSelfWordCount();
double res = ObjectType.getTypeWeight(match ? objectType : null);
if (parentSearchResult != null) {
res = res + parentSearchResult.getSumPhraseMatchWeight() / MAX_TYPE_WEIGHT;
}
return res;
}
public int getDepth() {
if (parentSearchResult != null) {
return 1 + parentSearchResult.getDepth();
}
return 1;
}
public int getFoundWordCount() {
int inc = getSelfWordCount();
if (parentSearchResult != null) {
inc += parentSearchResult.getFoundWordCount();
}
return inc;
}
private int getSelfWordCount() {
int inc = 0;
if (firstUnknownWordMatches) {
inc = 1;
@ -57,9 +86,6 @@ public class SearchResult {
if (otherWordsMatch != null) {
inc += otherWordsMatch.size();
}
if (parentSearchResult != null) {
inc += parentSearchResult.getFoundWordCount();
}
return inc;
}
@ -79,16 +105,6 @@ public class SearchResult {
return priority - 1 / (1 + pd * distance);
}
public LatLon location;
public int preferredZoom = 15;
public String localeName;
public String alternateName;
public Collection<String> otherNames;
public String localeRelatedObjectName;
public Object relatedObject;
public double distRelatedObjectName;
@Override
public String toString() {

View file

@ -23,8 +23,7 @@ public class SearchSettings {
private ObjectType[] searchTypes;
private boolean emptyQueryAllowed;
private boolean sortByName;
private SearchExportSettings exportSettings;
//private SearchExportSettings exportSettings = new SearchExportSettings(false, false);
private SearchExportSettings exportSettings; // = new SearchExportSettings(true, true, -1);
public SearchSettings(SearchSettings s) {
if(s != null) {

View file

@ -26,6 +26,7 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
@ -41,11 +42,34 @@ public class Algorithms {
private static final int BUFFER_SIZE = 1024;
private static final Log log = PlatformUtil.getLog(Algorithms.class);
public static boolean isEmpty(Collection c) {
public static boolean isEmpty(Collection<?> c) {
return c == null || c.size() == 0;
}
private static char[] CHARS_TO_NORMALIZE_KEY = new char[''];
private static char[] CHARS_TO_NORMALIZE_VALUE = new char['\''];
public static boolean isEmpty(Map map) {
public static String normalizeSearchText(String s) {
boolean norm = false;
for (int i = 0; i < s.length() && !norm; i++) {
char ch = s.charAt(i);
for (int j = 0; j < CHARS_TO_NORMALIZE_KEY.length; j++) {
if (ch == CHARS_TO_NORMALIZE_KEY[j]) {
norm = true;
break;
}
}
}
if (!norm) {
return s;
}
for (int k = 0; k < CHARS_TO_NORMALIZE_KEY.length; k++) {
s = s.replace(CHARS_TO_NORMALIZE_KEY[k], CHARS_TO_NORMALIZE_VALUE[k]);
}
return s;
}
public static boolean isEmpty(Map<?, ?> map) {
return map == null || map.size() == 0;
}
@ -90,10 +114,12 @@ public class Algorithms {
return def;
}
public static String getFileNameWithoutExtension(File f) {
String name = f.getName();
int i = name.indexOf('.');
return getFileNameWithoutExtension(f.getName());
}
public static String getFileNameWithoutExtension(String name) {
int i = name.lastIndexOf('.');
if (i >= 0) {
name = name.substring(0, i);
}
@ -114,6 +140,26 @@ public class Algorithms {
return name;
}
public static List<File> collectDirs(File parentDir, List<File> dirs) {
return collectDirs(parentDir, dirs, null);
}
public static List<File> collectDirs(File parentDir, List<File> dirs, File exclDir) {
File[] listFiles = parentDir.listFiles();
if (listFiles != null) {
Arrays.sort(listFiles);
for (File f : listFiles) {
if (f.isDirectory()) {
if (!f.equals(exclDir)) {
dirs.add(f);
}
Algorithms.collectDirs(f, dirs);
}
}
}
return dirs;
}
public static File[] getSortedFilesVersions(File dir) {
File[] listFiles = dir.listFiles();
if (listFiles != null) {
@ -250,6 +296,32 @@ public class Algorithms {
return test == 0x504b0304;
}
/**
* Checks, whether the child directory is a subdirectory of the parent
* directory.
*
* @param parent the parent directory.
* @param child the suspected child directory.
* @return true if the child is a subdirectory of the parent directory.
*/
public static boolean isSubDirectory(File parent, File child) {
try {
parent = parent.getCanonicalFile();
child = child.getCanonicalFile();
File dir = child;
while (dir != null) {
if (parent.equals(dir)) {
return true;
}
dir = dir.getParentFile();
}
} catch (IOException e) {
return false;
}
return false;
}
private static int readInt(InputStream in) throws IOException {
int ch1 = in.read();
int ch2 = in.read();
@ -292,10 +364,8 @@ public class Algorithms {
* exception. Supported formats are:
* #RRGGBB
* #AARRGGBB
* 'red', 'blue', 'green', 'black', 'white', 'gray', 'cyan', 'magenta',
* 'yellow', 'lightgray', 'darkgray'
*/
public static int parseColor(String colorString) {
public static int parseColor(String colorString) throws IllegalArgumentException {
if (colorString.charAt(0) == '#') {
// Use a long to avoid rollovers on #ffXXXXXX
if (colorString.length() == 4) {
@ -456,6 +526,10 @@ public class Algorithms {
}
public static StringBuilder readFromInputStream(InputStream i) throws IOException {
return readFromInputStream(i, true);
}
public static StringBuilder readFromInputStream(InputStream i, boolean autoclose) throws IOException {
StringBuilder responseBody = new StringBuilder();
responseBody.setLength(0);
if (i != null) {
@ -470,6 +544,9 @@ public class Algorithms {
}
responseBody.append(s);
}
if (autoclose) {
i.close();
}
}
return responseBody;
}
@ -679,13 +756,9 @@ public class Algorithms {
}
public static String formatMinutesDuration(int minutes) {
if (minutes < 60) {
return String.valueOf(minutes);
} else {
int min = minutes % 60;
int hours = minutes / 60;
return String.format(Locale.UK, "%02d:%02d", hours, min);
}
int min = minutes % 60;
int hours = minutes / 60;
return String.format(Locale.UK, "%02d:%02d", hours, min);
}
public static <T extends Enum<T>> T parseEnumValue(T[] cl, String val, T defaultValue) {

View file

@ -3,6 +3,8 @@ package net.osmand.util;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Locale;
@ -11,9 +13,12 @@ import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.osmand.data.LatLon;
import net.osmand.util.GeoPointParserUtil.GeoParsedPoint;
public class GeoPointParserUtil {
private static String getQueryParameter(final String param, URI uri) {
final String query = uri.getQuery();
String value = null;
@ -69,6 +74,31 @@ public class GeoPointParserUtil {
}
return map;
}
private static int kMaxPointBytes = 10;
private static int kMaxCoordBits = kMaxPointBytes * 3;
public static LatLon decodeMapsMeLatLonToInt(String s) {
// 44TvlEGXf-
int lat = 0, lon = 0;
int shift = kMaxCoordBits - 3;
for (int i = 0; i < s.length(); ++i, shift -= 3) {
int a = net.osmand.osm.io.Base64.indexOf(s.charAt(i));
if (a < 0)
return null;
int lat1 = (((a >> 5) & 1) << 2 | ((a >> 3) & 1) << 1 | ((a >> 1) & 1));
int lon1 = (((a >> 4) & 1) << 2 | ((a >> 2) & 1) << 1 | (a & 1));
lat |= lat1 << shift;
lon |= lon1 << shift;
}
double middleOfSquare = 1 << (3 * (kMaxPointBytes - s.length()) - 1);
lat += middleOfSquare;
lon += middleOfSquare;
double dlat = ((double) lat) / ((1 << kMaxCoordBits) - 1) * 180 - 90;
double dlon = ((double) lon) / ((1 << kMaxCoordBits) - 1 + 1) * 360.0 - 180;
return new LatLon(dlat, dlon);
}
/**
* Parses geo and map intents:
@ -103,7 +133,7 @@ public class GeoPointParserUtil {
if(uri.getSchemeSpecificPart() == null) {
return null;
} else if(!uri.getSchemeSpecificPart().contains("=")) {
params = getQueryParameters("q="+uri.getSchemeSpecificPart());
params = getQueryParameters("q="+uri.getSchemeSpecificPart());
} else {
params = getQueryParameters(uri.getSchemeSpecificPart());
}
@ -181,7 +211,7 @@ public class GeoPointParserUtil {
}
} else if (host.startsWith("map.baidu.")) { // .com and .cn both work
/* Baidu Map uses a custom format for lat/lon., it is basically standard lat/lon
* multiplied by 100,000, then rounded to an integer */
* multiplied by 100,000, then rounded to an integer */
String zm = params.get("l");
String[] vls = silentSplit(params.get("c"), ",");
if (vls != null && vls.length >= 2) {
@ -190,6 +220,31 @@ public class GeoPointParserUtil {
int zoom = parseZoom(zm);
return new GeoParsedPoint(lat, lon, zoom);
}
} else if (host.equals("ge0.me")) {
// http:///44TvlEGXf-/Kyiv
if (path.startsWith("/")) {
path = path.substring(1);
}
String[] pms = path.split("/");
String label = "";
if (pms.length > 1) {
label = pms[1];
}
String qry = pms[0];
if (qry.length() < 10) {
return null;
}
int indZoom = net.osmand.osm.io.Base64.indexOf(qry.charAt(0));
int zoom = 15;
if (indZoom >= 0) {
zoom = indZoom / 4 + 4;
}
LatLon l = decodeMapsMeLatLonToInt(qry.substring(1).replace('-', '/'));
if (l == null) {
return null;
}
return new GeoParsedPoint(l.getLatitude(), l.getLongitude(), zoom, label);
} else if (simpleDomains.contains(host)) {
if (uri.getQuery() == null && params.size() == 0) {
// DOUBLE check this may be wrong test of openstreetmap.de (looks very weird url and server doesn't respond)
@ -219,9 +274,8 @@ public class GeoPointParserUtil {
String latString = null;
String lonString = null;
String z = String.valueOf(GeoParsedPoint.NO_ZOOM);
if (params.containsKey("q")) {
System.out.println("q=" + params.get("q"));
Matcher matcher = commaSeparatedPairPattern.matcher(params.get("q"));
if (matcher.matches()) {
latString = matcher.group(1);
@ -252,7 +306,6 @@ public class GeoPointParserUtil {
}
final String postf = "\\s\\((\\p{L}|\\p{M}|\\p{Z}|\\p{S}|\\p{N}|\\p{P}|\\p{C})*\\)$";
opath = opath.replaceAll(postf, "");
System.out.println("opath=" + opath);
return parseGoogleMapsPath(opath, params);
}
if (fragment != null) {
@ -262,13 +315,32 @@ public class GeoPointParserUtil {
return new GeoParsedPoint(m.group(1));
}
}
String DATA_PREFIX = "/data=";
String[] pathPrefixes = new String[]{"/@", "/ll=",
"loc:", "/"};
"loc:", DATA_PREFIX, "/"};
for (String pref : pathPrefixes) {
if (path.contains(pref)) {
path = path.substring(path.lastIndexOf(pref) + pref.length());
return parseGoogleMapsPath(path, params);
if (path.contains("/")) {
path = path.substring(0, path.indexOf('/'));
}
if (pref.equals(DATA_PREFIX)) {
String[] vls = path.split("!");
String lat = null;
String lon = null;
for (String v : vls) {
if (v.startsWith("3d")) {
lat = v.substring(2);
} else if (v.startsWith("4d")) {
lon = v.substring(2);
}
}
if (lat != null && lon != null) {
return new GeoParsedPoint(Double.valueOf(lat), Double.valueOf(lon));
}
} else {
return parseGoogleMapsPath(path, params);
}
}
}
} else if (host.endsWith(".amap.com")) {
@ -279,7 +351,7 @@ public class GeoPointParserUtil {
Pattern p;
Matcher matcher;
final String[] patterns = {
/* though this looks like Query String, it is also used as part of the Fragment */
/* though this looks like Query String, it is also used as part of the Fragment */
".*q=([+-]?\\d+(?:\\.\\d+)?),([+-]?\\d+(?:\\.\\d+)?).*&radius=(\\d+).*",
".*q=([+-]?\\d+(?:\\.\\d+)?),([+-]?\\d+(?:\\.\\d+)?).*",
".*p=(?:[A-Z0-9]+),([+-]?\\d+(?:\\.\\d+)?),([+-]?\\d+(?:\\.\\d+)?).*",};
@ -297,7 +369,7 @@ public class GeoPointParserUtil {
}
}
}
} else if (host.equals("here.com") || host.endsWith(".here.com")) { // www.here.com, share.here.com, here.com
} else if (host.equals("here.com") || host.endsWith(".here.com")) { // www.here.com, share.here.com, here.com
String z = String.valueOf(GeoParsedPoint.NO_ZOOM);
String label = null;
if (params.containsKey("msg")) {
@ -474,7 +546,7 @@ public class GeoPointParserUtil {
}
if ("z".equals(paramName) && paramValue != null) {
zoom = Integer.parseInt(paramValue);
zoom = (int) Float.parseFloat(paramValue);
} else if ("q".equals(paramName) && paramValue != null) {
searchRequest = URLDecoder.decode(paramValue);
}
@ -569,7 +641,7 @@ public class GeoPointParserUtil {
private static int parseZoom(String zoom) {
try {
if (zoom != null) {
return Integer.valueOf(zoom);
return (int) Float.parseFloat(zoom);
}
} catch (NumberFormatException e) {
}
@ -579,7 +651,7 @@ public class GeoPointParserUtil {
private static double parseSilentDouble(String zoom) {
return parseSilentDouble(zoom, 0);
}
private static double parseSilentDouble(String zoom, double vl) {
try {
if (zoom != null) {
@ -754,9 +826,9 @@ public class GeoPointParserUtil {
@Override
public String toString() {
return isGeoPoint() ?
String.format("GeoParsedPoint [lat=%.5f, lon=%.5f, zoom=%d, label=%s]", lat, lon, zoom, label) :
String.format("GeoParsedPoint [query=%s]",query);
return isGeoPoint() ?
String.format("GeoParsedPoint [lat=%.5f, lon=%.5f, zoom=%d, label=%s]", lat, lon, zoom, label) :
String.format("GeoParsedPoint [query=%s]",query);
}
}
}

View file

@ -886,6 +886,12 @@ public class OpeningHoursParser {
return new TIntArrayList(endTimes);
}
public void setDays(boolean[] days) {
if (this.days.length == days.length) {
this.days = days;
}
}
/**
* Check if the weekday of time "cal" is part of this rule
*

View file

@ -0,0 +1,56 @@
package net.osmand;
import java.text.Normalizer;
import org.junit.Test;
import com.ibm.icu.text.ArabicShaping;
import com.ibm.icu.text.ArabicShapingException;
import com.ibm.icu.text.Bidi;
import net.osmand.Reshaper;
public class ReShaperTest {
//
// Source : ه ە ی ب ە
// Expected: ه ە ی ب ە
// Reshaped: ە ە
@Test
public void testArabName() throws ArabicShapingException {
// https://www.compart.com/en/unicode/U+FCD8
// String source = "\uFEEB\u06d5";
// System.out.println(new ArabicShaping(0).shape(s));
// System.out.println("\uFEEB\u06d5");
String source = "هەیبە";
String expected = "ەﺐﯾەﻩ";
String res = Reshaper.reshape(source);
Reshaper.check(source, res, expected);
}
@Test
public void test2() throws ArabicShapingException {
Reshaper.test2();
}
@Test
public void test3() throws ArabicShapingException {
Reshaper.test3();
}
@Test
public void test4() throws ArabicShapingException {
Reshaper.test4();
}
@Test
public void test5() throws ArabicShapingException {
Reshaper.test5();
}
}

View file

@ -38,15 +38,13 @@ public class RouteResultPreparationTest {
private static RoutePlannerFrontEnd fe;
private static RoutingContext ctx;
private String testName;
private LatLon startPoint;
private LatLon endPoint;
private Map<Long, String> expectedResults;
private Log log = PlatformUtil.getLog(RouteResultPreparationTest.class);
protected Log log = PlatformUtil.getLog(RouteResultPreparationTest.class);
public RouteResultPreparationTest(String testName, LatLon startPoint, LatLon endPoint, Map<Long, String> expectedResults) {
this.testName = testName;
this.startPoint = startPoint;
this.endPoint = endPoint;
this.expectedResults = expectedResults;

View file

@ -15,6 +15,7 @@ import java.util.TreeSet;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.data.LatLon;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
@ -29,42 +30,52 @@ public class RouteTestingTest {
private TestEntry te;
public RouteTestingTest(String name, TestEntry te) {
this.te = te;
}
public RouteTestingTest(String name, TestEntry te) {
this.te = te;
}
@BeforeClass
public static void setUp() throws Exception {
RouteResultPreparation.PRINT_TO_CONSOLE_ROUTE_INFORMATION_TO_TEST = true;
}
@BeforeClass
public static void setUp() throws Exception {
RouteResultPreparation.PRINT_TO_CONSOLE_ROUTE_INFORMATION_TO_TEST = true;
}
@Parameterized.Parameters(name = "{index}: {0}")
public static Iterable<Object[]> data() throws IOException {
String fileName = "/test_routing.json";
Reader reader = new InputStreamReader(RouteTestingTest.class.getResourceAsStream(fileName));
Gson gson = new GsonBuilder().setPrettyPrinting().create();
TestEntry[] testEntries = gson.fromJson(reader, TestEntry[].class);
ArrayList<Object[]> arrayList = new ArrayList<>();
for(TestEntry te : testEntries) {
if(te.isIgnore()) {
continue;
}
arrayList.add(new Object[] {te.getTestName(), te});
}
reader.close();
return arrayList;
@Parameterized.Parameters(name = "{index}: {0}")
public static Iterable<Object[]> data() throws IOException {
String fileName = "/test_routing.json";
Reader reader = new InputStreamReader(RouteTestingTest.class.getResourceAsStream(fileName));
Gson gson = new GsonBuilder().setPrettyPrinting().create();
TestEntry[] testEntries = gson.fromJson(reader, TestEntry[].class);
ArrayList<Object[]> arrayList = new ArrayList<>();
for (TestEntry te : testEntries) {
if (te.isIgnore()) {
continue;
}
arrayList.add(new Object[]{te.getTestName(), te});
}
reader.close();
return arrayList;
}
}
@Test
@Test
public void testRouting() throws Exception {
String fl = "src/test/resources/Routing_test.obf";
RandomAccessFile raf = new RandomAccessFile(fl, "r");
RoutePlannerFrontEnd fe = new RoutePlannerFrontEnd();
BinaryMapIndexReader[] binaryMapIndexReaders = { new BinaryMapIndexReader(raf, new File(fl)) };
BinaryMapIndexReader[] binaryMapIndexReaders;// = { new BinaryMapIndexReader(raf, new File(fl)) };
RoutingConfiguration.Builder builder = RoutingConfiguration.getDefault();
Map<String, String> params = te.getParams();
if (params.containsKey("map")) {
String fl1 = "src/test/resources/" + params.get("map");
RandomAccessFile raf1 = new RandomAccessFile(fl1, "r");
binaryMapIndexReaders = new BinaryMapIndexReader[]{
new BinaryMapIndexReader(raf1, new File(fl1)),
new BinaryMapIndexReader(raf, new File(fl))
};
} else {
binaryMapIndexReaders = new BinaryMapIndexReader[]{new BinaryMapIndexReader(raf, new File(fl))};
}
RoutingConfiguration config = builder.build(params.containsKey("vehicle") ? params.get("vehicle") : "car",
RoutingConfiguration.DEFAULT_MEMORY_LIMIT * 3, params);
RoutingContext ctx = fe.buildRoutingContext(config, null, binaryMapIndexReaders,
@ -102,6 +113,4 @@ public class RouteTestingTest {
}
}

View file

@ -17,7 +17,7 @@ public class LocationSearchTest {
private void search(String string, LatLon latLon) throws IOException {
SearchResultMatcher srm = new SearchUICore.SearchResultMatcher(null, null, 0, null, 100);
new SearchCoreFactory.SearchLocationAndUrlAPI().
search(new SearchPhrase(null, OsmAndCollator.primaryCollator()).generateNewPhrase(string, null), srm);
search(SearchPhrase.emptyPhrase().generateNewPhrase(string, null), srm);
Assert.assertEquals(1, srm.getRequestResults().size());
Assert.assertEquals(latLon, srm.getRequestResults().get(0).location);
}

View file

@ -1,430 +0,0 @@
package net.osmand.search;
import net.osmand.OsmAndCollator;
import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.data.Amenity;
import net.osmand.data.Building;
import net.osmand.data.City;
import net.osmand.data.LatLon;
import net.osmand.data.MapObject;
import net.osmand.data.Street;
import net.osmand.osm.AbstractPoiType;
import net.osmand.osm.MapPoiTypes;
import net.osmand.search.SearchUICore.SearchResultCollection;
import net.osmand.search.SearchUICore.SearchResultMatcher;
import net.osmand.search.core.SearchPhrase;
import net.osmand.search.core.SearchResult;
import net.osmand.search.core.SearchSettings;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Assert;
import org.junit.Test;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
public class SearchCoreUITest {
private static final String SEARCH_RESOURCES_PATH = "src/test/resources/search/";
private static Map<String, String> enPhrases = new HashMap<>();
private static Map<String, String> phrases = new HashMap<>();
static {
MapPoiTypes.setDefault(new MapPoiTypes("src/test/resources/poi_types.xml"));
MapPoiTypes poiTypes = MapPoiTypes.getDefault();
try {
enPhrases = Algorithms.parseStringsXml(new File("src/test/resources/phrases/en/phrases.xml"));
//phrases = Algorithms.parseStringsXml(new File("src/test/resources/phrases/ru/phrases.xml"));
phrases = enPhrases;
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
}
poiTypes.setPoiTranslator(new MapPoiTypes.PoiTranslator() {
@Override
public String getTranslation(AbstractPoiType type) {
AbstractPoiType baseLangType = type.getBaseLangType();
if (baseLangType != null) {
return getTranslation(baseLangType) + " (" + type.getLang().toLowerCase() + ")";
}
return getTranslation(type.getIconKeyName());
}
@Override
public String getTranslation(String keyName) {
String val = phrases.get("poi_" + keyName);
if (val != null) {
int ind = val.indexOf(';');
if (ind > 0) {
return val.substring(0, ind);
}
}
return val;
}
@Override
public String getSynonyms(AbstractPoiType type) {
AbstractPoiType baseLangType = type.getBaseLangType();
if (baseLangType != null) {
return getSynonyms(baseLangType);
}
return getSynonyms(type.getIconKeyName());
}
@Override
public String getSynonyms(String keyName) {
String val = phrases.get("poi_" + keyName);
if (val != null) {
int ind = val.indexOf(';');
if (ind > 0) {
return val.substring(ind + 1);
}
return "";
}
return null;
}
@Override
public String getEnTranslation(AbstractPoiType type) {
AbstractPoiType baseLangType = type.getBaseLangType();
if (baseLangType != null) {
return getEnTranslation(baseLangType) + " (" + type.getLang().toLowerCase() + ")";
}
return getEnTranslation(type.getIconKeyName());
}
@Override
public String getEnTranslation(String keyName) {
if (enPhrases.isEmpty()) {
return Algorithms.capitalizeFirstLetter(keyName.replace('_', ' '));
}
String val = enPhrases.get("poi_" + keyName);
if (val != null) {
int ind = val.indexOf(';');
if (ind > 0) {
return val.substring(0, ind);
}
}
return val;
}
});
}
@Test
public void testDuplicates() throws IOException {
SearchSettings ss = new SearchSettings((SearchSettings)null);
ss = ss.setOriginalLocation(new LatLon(0, 0));
SearchPhrase phrase = new SearchPhrase(ss, OsmAndCollator.primaryCollator());
SearchResultCollection cll = new SearchUICore.SearchResultCollection(phrase);
List<SearchResult> rs = new ArrayList<>();
SearchResult a1 = searchResult(rs, phrase, "a", 100);
SearchResult b2 = searchResult(rs, phrase, "b", 200);
SearchResult b1 = searchResult(rs, phrase, "b", 100);
/*SearchResult a3 = */ searchResult(rs, phrase, "a", 100);
cll.addSearchResults(rs, true, true);
Assert.assertEquals(3, cll.getCurrentSearchResults().size());
Assert.assertSame(a1, cll.getCurrentSearchResults().get(0));
Assert.assertSame(b1, cll.getCurrentSearchResults().get(1));
Assert.assertSame(b2, cll.getCurrentSearchResults().get(2));
}
@Test
public void testNoResort() throws IOException {
SearchSettings ss = new SearchSettings((SearchSettings)null);
ss = ss.setOriginalLocation(new LatLon(0, 0));
SearchPhrase phrase = new SearchPhrase(ss, OsmAndCollator.primaryCollator());
SearchResultCollection cll = new SearchUICore.SearchResultCollection(phrase);
List<SearchResult> rs = new ArrayList<>();
SearchResult a1 = searchResult(rs, phrase, "a", 100);
cll.addSearchResults(rs, false, true);
rs.clear();
SearchResult b2 = searchResult(rs, phrase, "b", 200);
cll.addSearchResults(rs, false, true);
rs.clear();
SearchResult b1 = searchResult(rs, phrase, "b", 100);
cll.addSearchResults(rs, false, true);
rs.clear();
/*SearchResult a3 = */ searchResult(rs, phrase, "a", 100);
cll.addSearchResults(rs, false, true);
rs.clear();
Assert.assertEquals(3, cll.getCurrentSearchResults().size());
Assert.assertSame(a1, cll.getCurrentSearchResults().get(0));
Assert.assertSame(b2, cll.getCurrentSearchResults().get(1));
Assert.assertSame(b1, cll.getCurrentSearchResults().get(2));
}
@Test
public void testNoResortDuplicate() throws IOException {
SearchSettings ss = new SearchSettings((SearchSettings)null);
ss = ss.setOriginalLocation(new LatLon(0, 0));
SearchPhrase phrase = new SearchPhrase(ss, OsmAndCollator.primaryCollator());
SearchResultCollection cll = new SearchUICore.SearchResultCollection(phrase);
List<SearchResult> rs = new ArrayList<>();
SearchResult a1 = searchResult(rs, phrase, "a", 100);
SearchResult b2 = searchResult(rs, phrase, "b", 200);
SearchResult b1 = searchResult(rs, phrase, "b", 100);
cll.addSearchResults(rs, false, true);
rs.clear();
/*SearchResult a3 = */ searchResult(rs, phrase, "a", 100);
cll.addSearchResults(rs, false, true);
rs.clear();
Assert.assertEquals(3, cll.getCurrentSearchResults().size());
Assert.assertSame(a1, cll.getCurrentSearchResults().get(0));
Assert.assertSame(b1, cll.getCurrentSearchResults().get(1));
Assert.assertSame(b2, cll.getCurrentSearchResults().get(2));
}
private SearchResult searchResult(List<SearchResult> rs, SearchPhrase phrase, String text, int dist) {
SearchResult res = new SearchResult(phrase);
res.localeName = text;
double d1 = MapUtils.getDistance(0, 0, 0, 1);
res.location = new LatLon(0, dist / d1);
rs.add(res);
return res;
}
@Test
public void testSearchJsons() throws IOException {
final File[] files = new File(SEARCH_RESOURCES_PATH).listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String filename) {
return filename.endsWith(".json");
}
});
if (files != null) {
for (File f : files) {
testSearchImpl(f);
}
}
}
private void testSearchImpl(File jsonFile) throws IOException, JSONException {
String sourceJsonText = Algorithms.getFileAsString(jsonFile);
Assert.assertNotNull(sourceJsonText);
Assert.assertTrue(sourceJsonText.length() > 0);
BinaryMapIndexReaderTest reader = new BinaryMapIndexReaderTest();
JSONObject sourceJson = new JSONObject(sourceJsonText);
String phraseText = sourceJson.getString("phrase");
JSONObject settingsJson = sourceJson.getJSONObject("settings");
if (sourceJson.has("amenities")) {
JSONArray amenitiesArr = sourceJson.getJSONArray("amenities");
List<Amenity> amenities = new ArrayList<>();
for (int i = 0; i < amenitiesArr.length(); i++) {
JSONObject amenityObj = amenitiesArr.getJSONObject(i);
amenities.add(Amenity.parseJSON(amenityObj));
}
reader.amenities = amenities;
}
if (sourceJson.has("cities")) {
JSONArray citiesArr = sourceJson.getJSONArray("cities");
List<City> cities = new ArrayList<>();
List<City> initCities = new ArrayList<>();
List<City> matchedCities = new ArrayList<>();
List<City> streetCities = new ArrayList<>();
for (int i = 0; i < citiesArr.length(); i++) {
JSONObject cityObj = citiesArr.getJSONObject(i);
final City city = City.parseJSON(cityObj);
cities.add(city);
if (cityObj.has("init")) {
initCities.add(city);
}
if (cityObj.has("matchCity")) {
matchedCities.add(city);
}
if (cityObj.has("matchStreet")) {
streetCities.add(city);
}
}
reader.cities = cities;
reader.initCities = initCities;
reader.matchedCities = matchedCities;
reader.streetCities = streetCities;
}
List<String> results = new ArrayList<>();
if (sourceJson.has("results")) {
JSONArray resultsArr = sourceJson.getJSONArray("results");
for (int i = 0; i < resultsArr.length(); i++) {
results.add(resultsArr.getString(i));
}
}
SearchSettings s = SearchSettings.parseJSON(settingsJson);
s.setOfflineIndexes(Collections.singletonList(reader));
SearchPhrase phrase = new SearchPhrase(s, OsmAndCollator.primaryCollator());
phrase = phrase.generateNewPhrase(phraseText, s);
final SearchUICore core = new SearchUICore(MapPoiTypes.getDefault(), "en", false);
core.init();
ResultMatcher<SearchResult> rm = new ResultMatcher<SearchResult>() {
@Override
public boolean publish(SearchResult object) {
return true;
}
@Override
public boolean isCancelled() {
return false;
}
};
SearchResultMatcher matcher = new SearchResultMatcher(rm, phrase, 1, new AtomicInteger(1), -1);
core.searchInternal(phrase, matcher);
SearchResultCollection collection = new SearchResultCollection(phrase);
collection.addSearchResults(matcher.getRequestResults(), true, true);
List<SearchResult> searchResults = collection.getCurrentSearchResults();
int i = 0;
for (SearchResult result : searchResults) {
String expected = results.get(i++);
String present = result.toString();
//System.out.println(present);
Assert.assertEquals(expected, present);
if (i >= results.size()) {
break;
}
}
}
private static class BinaryMapIndexReaderTest extends BinaryMapIndexReader {
List<Amenity> amenities = Collections.emptyList();
List<City> cities = Collections.emptyList();
List<City> initCities = Collections.emptyList();
List<City> matchedCities = Collections.emptyList();
List<City> streetCities = Collections.emptyList();
BinaryMapIndexReaderTest() throws IOException {
super(null, null, false);
}
@Override
public List<Amenity> searchPoiByName(SearchRequest<Amenity> req) throws IOException {
for (Amenity amenity : amenities) {
req.publish(amenity);
}
return req.getSearchResults();
}
@Override
public List<Amenity> searchPoi(SearchRequest<Amenity> req) throws IOException {
for (Amenity amenity : amenities) {
req.publish(amenity);
}
return req.getSearchResults();
}
@Override
public List<City> getCities(SearchRequest<City> resultMatcher, int cityType) throws IOException {
for (City city : initCities) {
if (resultMatcher != null) {
resultMatcher.publish(city);
}
}
return initCities;
}
@Override
public int preloadStreets(City c, SearchRequest<Street> resultMatcher) throws IOException {
return 0;
}
@Override
public void preloadBuildings(Street s, SearchRequest<Building> resultMatcher) throws IOException {
// cities must be filled with streets and buildings
}
@Override
public List<MapObject> searchAddressDataByName(SearchRequest<MapObject> req) throws IOException {
for (City city : streetCities) {
for (Street street : city.getStreets()) {
req.publish(street);
}
}
for (City city : matchedCities) {
req.publish(city);
}
return req.getSearchResults();
}
@Override
public String getRegionName() {
return "Test region";
}
@Override
public boolean containsPoiData(int left31x, int top31y, int right31x, int bottom31y) {
return true;
}
@Override
public boolean containsMapData() {
return true;
}
@Override
public boolean containsPoiData() {
return true;
}
@Override
public boolean containsRouteData() {
return true;
}
@Override
public boolean containsRouteData(int left31x, int top31y, int right31x, int bottom31y, int zoom) {
return true;
}
@Override
public boolean containsAddressData(int left31x, int top31y, int right31x, int bottom31y) {
return true;
}
@Override
public boolean containsMapData(int tile31x, int tile31y, int zoom) {
return true;
}
@Override
public boolean containsMapData(int left31x, int top31y, int right31x, int bottom31y, int zoom) {
return true;
}
@Override
public boolean containsAddressData() {
return true;
}
}
}

View file

@ -0,0 +1,110 @@
package net.osmand.search;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import net.osmand.data.LatLon;
import net.osmand.search.SearchUICore.SearchResultCollection;
import net.osmand.search.core.SearchPhrase;
import net.osmand.search.core.SearchResult;
import net.osmand.search.core.SearchSettings;
import net.osmand.util.MapUtils;
public class SearchUICoreGenericTest {
@BeforeClass
public static void setUp() {
SearchUICoreTest.defaultSetup();
}
@Test
public void testDuplicates() throws IOException {
SearchSettings ss = new SearchSettings((SearchSettings)null);
ss = ss.setOriginalLocation(new LatLon(0, 0));
SearchPhrase phrase = SearchPhrase.emptyPhrase(ss);
SearchResultCollection cll = new SearchUICore.SearchResultCollection(phrase);
List<SearchResult> rs = new ArrayList<>();
SearchResult a1 = searchResult(rs, phrase, "a", 100);
SearchResult b2 = searchResult(rs, phrase, "b", 200);
SearchResult b1 = searchResult(rs, phrase, "b", 100);
/*SearchResult a3 = */ searchResult(rs, phrase, "a", 100);
cll.addSearchResults(rs, true, true);
Assert.assertEquals(3, cll.getCurrentSearchResults().size());
Assert.assertSame(a1, cll.getCurrentSearchResults().get(0));
Assert.assertSame(b1, cll.getCurrentSearchResults().get(1));
Assert.assertSame(b2, cll.getCurrentSearchResults().get(2));
}
@Test
public void testNoResort() throws IOException {
SearchSettings ss = new SearchSettings((SearchSettings)null);
ss = ss.setOriginalLocation(new LatLon(0, 0));
SearchPhrase phrase = SearchPhrase.emptyPhrase(ss);
SearchResultCollection cll = new SearchUICore.SearchResultCollection(phrase);
List<SearchResult> rs = new ArrayList<>();
SearchResult a1 = searchResult(rs, phrase, "a", 100);
cll.addSearchResults(rs, false, true);
rs.clear();
SearchResult b2 = searchResult(rs, phrase, "b", 200);
cll.addSearchResults(rs, false, true);
rs.clear();
SearchResult b1 = searchResult(rs, phrase, "b", 100);
cll.addSearchResults(rs, false, true);
rs.clear();
/*SearchResult a3 = */ searchResult(rs, phrase, "a", 100);
cll.addSearchResults(rs, false, true);
rs.clear();
Assert.assertEquals(3, cll.getCurrentSearchResults().size());
Assert.assertSame(a1, cll.getCurrentSearchResults().get(0));
Assert.assertSame(b2, cll.getCurrentSearchResults().get(1));
Assert.assertSame(b1, cll.getCurrentSearchResults().get(2));
}
@Test
public void testNoResortDuplicate() throws IOException {
SearchSettings ss = new SearchSettings((SearchSettings)null);
ss = ss.setOriginalLocation(new LatLon(0, 0));
SearchPhrase phrase = SearchPhrase.emptyPhrase(ss);
SearchResultCollection cll = new SearchUICore.SearchResultCollection(phrase);
List<SearchResult> rs = new ArrayList<>();
SearchResult a1 = searchResult(rs, phrase, "a", 100);
SearchResult b2 = searchResult(rs, phrase, "b", 200);
SearchResult b1 = searchResult(rs, phrase, "b", 100);
cll.addSearchResults(rs, false, true);
rs.clear();
/*SearchResult a3 = */ searchResult(rs, phrase, "a", 100);
cll.addSearchResults(rs, false, true);
rs.clear();
Assert.assertEquals(3, cll.getCurrentSearchResults().size());
Assert.assertSame(a1, cll.getCurrentSearchResults().get(0));
Assert.assertSame(b1, cll.getCurrentSearchResults().get(1));
Assert.assertSame(b2, cll.getCurrentSearchResults().get(2));
}
private SearchResult searchResult(List<SearchResult> rs, SearchPhrase phrase, String text, int dist) {
SearchResult res = new SearchResult(phrase);
res.localeName = text;
double d1 = MapUtils.getDistance(0, 0, 0, 1);
res.location = new LatLon(0, dist / d1);
rs.add(res);
return res;
}
}

Some files were not shown because too many files have changed in this diff Show more