diff --git a/OsmAndCore-sample/build.gradle b/OsmAndCore-sample/build.gradle index 065602bef8..6bfbe775b0 100644 --- a/OsmAndCore-sample/build.gradle +++ b/OsmAndCore-sample/build.gradle @@ -88,8 +88,14 @@ task collectRegionsInfoResources(type: Copy) { include "regions.ocbf" } +task collectRoutingResources(type: Sync) { + from "../../resources/routing" + into "src/net/osmand/router" + include "routing.xml" +} + tasks.withType(JavaCompile) { - compileTask -> compileTask.dependsOn << [collectMiscResources, collectRegionsInfoResources] + compileTask -> compileTask.dependsOn << [collectMiscResources, collectRegionsInfoResources, collectRoutingResources] } repositories { diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/CurrentPositionHelper.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/CurrentPositionHelper.java new file mode 100644 index 0000000000..c0ce4d406f --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/CurrentPositionHelper.java @@ -0,0 +1,254 @@ +package net.osmand.core.samples.android.sample1; + +import android.os.AsyncTask; + +import net.osmand.IndexConstants; +import net.osmand.Location; +import net.osmand.PlatformUtil; +import net.osmand.ResultMatcher; +import net.osmand.binary.BinaryMapIndexReader; +import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion; +import net.osmand.binary.GeocodingUtilities; +import net.osmand.binary.GeocodingUtilities.GeocodingResult; +import net.osmand.binary.RouteDataObject; +import net.osmand.router.RoutePlannerFrontEnd; +import net.osmand.router.RoutingConfiguration; +import net.osmand.router.RoutingContext; +import net.osmand.util.MapUtils; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +public class CurrentPositionHelper { + + private RouteDataObject lastFound; + private Location lastAskedLocation = null; + private RoutingContext defCtx; + private SampleApplication app; + private List readers = new ArrayList<>(); + private List usedReaders = new ArrayList<>(); + private static final org.apache.commons.logging.Log log = PlatformUtil.getLog(CurrentPositionHelper.class); + + public CurrentPositionHelper(SampleApplication app) { + this.app = app; + setRepositories(); + } + + + public void setRepositories() { + ArrayList files = new ArrayList(); + File appPath = app.getAppPath(null); + SampleUtils.collectFiles(appPath, IndexConstants.BINARY_MAP_INDEX_EXT, files); + + readers.clear(); + for (File f : files) { + try { + RandomAccessFile mf = new RandomAccessFile(f.getPath(), "r"); + BinaryMapIndexReader reader = new BinaryMapIndexReader(mf, f); + readers.add(reader); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public Location getLastAskedLocation() { + return lastAskedLocation; + } + + public boolean getGeocodingResult(Location loc, ResultMatcher result) { + return scheduleRouteSegmentFind(loc, false, result, null); + } + + public RouteDataObject getLastKnownRouteSegment(Location loc) { + Location last = lastAskedLocation; + RouteDataObject r = lastFound; + if (loc == null || loc.getAccuracy() > 50) { + return null; + } + if (last != null && last.distanceTo(loc) < 10) { + return r; + } + if (r == null) { + scheduleRouteSegmentFind(loc, true, null, null); + return null; + } + double d = getOrthogonalDistance(r, loc); + if (d > 15) { + scheduleRouteSegmentFind(loc, true, null, null); + } + if (d < 70) { + return r; + } + return null; + } + + ///////////////////////// PRIVATE IMPLEMENTATION ////////////////////////// + private boolean scheduleRouteSegmentFind(final Location loc, final boolean storeFound, final ResultMatcher geoCoding, final ResultMatcher result) { + boolean res = false; + if (loc != null) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + try { + processGeocoding(loc, geoCoding, storeFound, result); + } catch (Exception e) { + log.error("Error processing geocoding", e); + e.printStackTrace(); + } + return null; + } + }.execute((Void) null); + res = true; + } + return res; + } + + private void initCtx(SampleApplication app, List checkReaders) { + BinaryMapIndexReader[] rs = checkReaders.toArray(new BinaryMapIndexReader[checkReaders.size()]); + if (rs.length > 0) { + RoutingConfiguration defCfg = RoutingConfiguration.getDefault().build("geocoding", 10, + new HashMap()); + defCtx = new RoutePlannerFrontEnd(false).buildRoutingContext(defCfg, null, rs); + } else { + defCtx = null; + } + usedReaders = checkReaders; + } + + // single synchronized method + private synchronized void processGeocoding(Location loc, ResultMatcher geoCoding, boolean storeFound, final ResultMatcher result) throws IOException { + final List gr = runUpdateInThread(loc.getLatitude(), loc.getLongitude()); + if (storeFound) { + lastAskedLocation = loc; + lastFound = gr == null || gr.isEmpty() ? null : gr.get(0).point.getRoad(); + } else if (geoCoding != null) { + justifyResult(gr, geoCoding); + } else if (result != null) { + app.runInUIThread(new Runnable() { + @Override + public void run() { + result.publish(gr == null || gr.isEmpty() ? null : gr.get(0).point.getRoad()); + } + }); + } + } + + private List runUpdateInThread(double lat, double lon) throws IOException { + List checkReaders = checkReaders(lat, lon); + if (defCtx == null || checkReaders != usedReaders) { + initCtx(app, checkReaders); + if (defCtx == null) { + return null; + } + } + try { + return new GeocodingUtilities().reverseGeocodingSearch(defCtx, lat, lon); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + private List checkReaders(double lat, double lon) { + List res = new ArrayList<>(); + int y31 = MapUtils.get31TileNumberY(lat); + int x31 = MapUtils.get31TileNumberX(lon); + for (BinaryMapIndexReader r : readers) { + if (r.containsRouteData(x31, y31, x31, y31, 15)) { + if (!res.contains(r)) { + res = new ArrayList<>(res); + res.add(r); + } + } + } + return res; + } + + private void justifyResult(List res, final ResultMatcher result) { + List complete = new ArrayList<>(); + double minBuildingDistance = 0; + if (res != null) { + for (GeocodingResult r : res) { + BinaryMapIndexReader foundRepo = null; + List rts = usedReaders; + for (BinaryMapIndexReader reader : rts) { + for (RouteRegion rb : reader.getRoutingIndexes()) { + if (r.regionFP == rb.getFilePointer() && r.regionLen == rb.getLength()) { + foundRepo = reader; + break; + } + } + } + if (result.isCancelled()) { + break; + } else if (foundRepo != null) { + List justified = null; + try { + justified = new GeocodingUtilities().justifyReverseGeocodingSearch(r, foundRepo, + minBuildingDistance, result); + } catch (IOException e) { + log.error("Exception happened during reverse geocoding", e); + e.printStackTrace(); + } + if (justified != null && !justified.isEmpty()) { + double md = justified.get(0).getDistance(); + if (minBuildingDistance == 0) { + minBuildingDistance = md; + } else { + minBuildingDistance = Math.min(md, minBuildingDistance); + } + complete.addAll(justified); + } + } else { + complete.add(r); + } + } + } + + if (result.isCancelled()) { + app.runInUIThread(new Runnable() { + public void run() { + result.publish(null); + } + }); + return; + } + Collections.sort(complete, GeocodingUtilities.DISTANCE_COMPARATOR); +// for(GeocodingResult rt : complete) { +// System.out.println(rt.toString()); +// } + final GeocodingResult rts = complete.size() > 0 ? complete.get(0) : new GeocodingResult(); + app.runInUIThread(new Runnable() { + public void run() { + result.publish(rts); + } + }); + } + + public static double getOrthogonalDistance(RouteDataObject r, Location loc) { + double d = 1000; + if (r.getPointsLength() > 0) { + double pLt = MapUtils.get31LatitudeY(r.getPoint31YTile(0)); + double pLn = MapUtils.get31LongitudeX(r.getPoint31XTile(0)); + for (int i = 1; i < r.getPointsLength(); i++) { + double lt = MapUtils.get31LatitudeY(r.getPoint31YTile(i)); + double ln = MapUtils.get31LongitudeX(r.getPoint31XTile(i)); + double od = MapUtils.getOrthogonalDistance(loc.getLatitude(), loc.getLongitude(), pLt, pLn, lt, ln); + if (od < d) { + d = od; + } + pLt = lt; + pLn = ln; + } + } + return d; + } + + +} diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MainActivity.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MainActivity.java index fc59c14cbb..ea30fdebcd 100644 --- a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MainActivity.java +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MainActivity.java @@ -567,7 +567,7 @@ public class MainActivity extends AppCompatActivity { } else { fragment.show(); } - //refreshMap(); + refreshMap(); } else { QuickSearchDialogFragment.showInstance(this, "", null, showCategories, null); } @@ -577,7 +577,7 @@ public class MainActivity extends AppCompatActivity { QuickSearchDialogFragment fragment = getQuickSearchDialogFragment(); if (fragment != null) { fragment.closeSearch(); - //refreshMap(); + refreshMap(); } } @@ -587,13 +587,11 @@ public class MainActivity extends AppCompatActivity { } private void hideContextMenu() { - /* todo - if (mapContextMenu.isVisible()) { - mapContextMenu.hide(); - } else if (mapContextMenu.getMultiSelectionMenu().isVisible()) { - mapContextMenu.getMultiSelectionMenu().hide(); + if (menu.isVisible()) { + menu.hide(); + } else if (menu.getMultiSelectionMenu().isVisible()) { + menu.getMultiSelectionMenu().hide(); } - */ } public enum ShowQuickSearchMode { diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SampleApplication.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SampleApplication.java index 1178caca31..88f5e371cb 100644 --- a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SampleApplication.java +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SampleApplication.java @@ -6,6 +6,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.net.ConnectivityManager; import android.net.NetworkInfo; +import android.os.Build; import android.os.Environment; import android.os.Handler; import android.support.v4.content.ContextCompat; @@ -17,10 +18,14 @@ import net.osmand.core.jni.Logger; import net.osmand.core.samples.android.sample1.SampleFormatter.MetricsConstants; import net.osmand.core.samples.android.sample1.SampleFormatter.SpeedConstants; import net.osmand.core.samples.android.sample1.search.QuickSearchHelper; +import net.osmand.map.OsmandRegions; +import net.osmand.map.WorldRegion; import net.osmand.osm.AbstractPoiType; import net.osmand.osm.MapPoiTypes; +import net.osmand.util.Algorithms; import java.io.File; +import java.io.FileOutputStream; import java.lang.reflect.Field; import java.util.Locale; @@ -42,6 +47,7 @@ public class SampleApplication extends Application { private SampleLocationProvider locationProvider; private QuickSearchHelper searchUICore; private GeocodingLookupService geocodingLookupService; + private OsmandRegions regions; public static String LANGUAGE; public static boolean TRANSLITERATE = false; @@ -61,6 +67,10 @@ public class SampleApplication extends Application { locationProvider = new SampleLocationProvider(this); searchUICore = new QuickSearchHelper(this); geocodingLookupService = new GeocodingLookupService(this); + regions = new OsmandRegions(); + updateRegionVars(); + indexRegionsBoundaries(); + uiHandler = new Handler(); poiTypes = MapPoiTypes.getDefaultNoInit(); @@ -79,6 +89,57 @@ public class SampleApplication extends Application { iconsCache = new IconsCache(assetsCustom, this); } + private void updateRegionVars() { + regions.setTranslator(new OsmandRegions.RegionTranslation() { + + @Override + public String getTranslation(String id) { + if(WorldRegion.AFRICA_REGION_ID.equals(id)){ + return getString("index_name_africa"); + } else if(WorldRegion.AUSTRALIA_AND_OCEANIA_REGION_ID.equals(id)){ + return getString("index_name_oceania"); + } else if(WorldRegion.ASIA_REGION_ID.equals(id)){ + return getString("index_name_asia"); + } else if(WorldRegion.CENTRAL_AMERICA_REGION_ID.equals(id)){ + return getString("index_name_central_america"); + } else if(WorldRegion.EUROPE_REGION_ID.equals(id)){ + return getString("index_name_europe"); + } else if(WorldRegion.RUSSIA_REGION_ID.equals(id)){ + return getString("index_name_russia"); + } else if(WorldRegion.NORTH_AMERICA_REGION_ID.equals(id)){ + return getString("index_name_north_america"); + } else if(WorldRegion.SOUTH_AMERICA_REGION_ID.equals(id)){ + return getString("index_name_south_america"); + } + return null; + } + }); + regions.setLocale(LANGUAGE); + } + + private void indexRegionsBoundaries() { + try { + File file = getAppPath("regions.ocbf"); + if (file != null) { + if (!file.exists()) { + file = new File(getInternalAppPath(), "regions.ocbf"); + if (!file.exists()) { + Algorithms.streamCopy(OsmandRegions.class.getResourceAsStream("regions.ocbf"), + new FileOutputStream(file)); + } + } + regions.prepareFile(file.getAbsolutePath()); + + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public OsmandRegions getRegions() { + return regions; + } + public GeocodingLookupService getGeocodingLookupService() { return geocodingLookupService; } @@ -219,4 +280,14 @@ public class SampleApplication extends Application { public String getString(String osmandId, Object... formatArgs) { return OsmandResources.getString(osmandId, formatArgs); } + + public File getInternalAppPath() { + if (Build.VERSION.SDK_INT >= 21) { + File fl = getNoBackupFilesDir(); + if (fl != null) { + return fl; + } + } + return getFilesDir(); + } } \ No newline at end of file diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SampleLocationProvider.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SampleLocationProvider.java index 73205a131f..1c0eda2642 100644 --- a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SampleLocationProvider.java +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SampleLocationProvider.java @@ -75,7 +75,7 @@ public class SampleLocationProvider implements SensorEventListener { private int currentScreenOrientation; private SampleApplication app; - //private CurrentPositionHelper currentPositionHelper; + private CurrentPositionHelper currentPositionHelper; private net.osmand.Location location = null; @@ -91,7 +91,7 @@ public class SampleLocationProvider implements SensorEventListener { public SampleLocationProvider(SampleApplication app) { this.app = app; - //currentPositionHelper = new CurrentPositionHelper(app); + currentPositionHelper = new CurrentPositionHelper(app); } public void resumeAllUpdates() { @@ -520,7 +520,7 @@ public class SampleLocationProvider implements SensorEventListener { */ public boolean getGeocodingResult(net.osmand.Location loc, ResultMatcher result) { - return false; //currentPositionHelper.getGeocodingResult(loc, result); todo + return currentPositionHelper.getGeocodingResult(loc, result); } diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SampleUtils.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SampleUtils.java index 3e9e6406b6..297047e8cd 100644 --- a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SampleUtils.java +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SampleUtils.java @@ -13,6 +13,9 @@ import android.view.Surface; import net.osmand.PlatformUtil; +import java.io.File; +import java.util.List; + public class SampleUtils { private static final int ORIENTATION_0 = 0; @@ -167,4 +170,18 @@ public class SampleUtils { orientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; } + public static List collectFiles(File dir, String ext, List files) { + if (dir.exists() && dir.canRead()) { + File[] lf = dir.listFiles(); + if (lf == null || lf.length == 0) { + return files; + } + for (File f : lf) { + if (f.getName().endsWith(ext)) { + files.add(f); + } + } + } + return files; + } } diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/QuickSearchDialogFragment.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/QuickSearchDialogFragment.java index e99db901ed..48f8b05faa 100644 --- a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/QuickSearchDialogFragment.java +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/QuickSearchDialogFragment.java @@ -50,7 +50,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import static net.osmand.search.core.ObjectType.POI_TYPE; import static net.osmand.search.core.SearchCoreFactory.SEARCH_AMENITY_TYPE_PRIORITY; public class QuickSearchDialogFragment extends DialogFragment implements SampleCompassListener, SampleLocationListener { @@ -196,14 +195,7 @@ public class QuickSearchDialogFragment extends DialogFragment implements SampleC PointDescription pointDescription = new PointDescription(PointDescription.POINT_TYPE_ADDRESS, typeName, name); mainActivity.showOnMap(searchResult.location, searchResult.preferredZoom); - /* todo - app.getSettings().setMapLocationToShow( - searchResult.location.getLatitude(), searchResult.location.getLongitude(), - searchResult.preferredZoom, pointDescription, true, searchResult.object); - - hideToolbar(); - MainActivity.launchMainActivityMoveToTop(getActivity()); - */ + mainActivity.getContextMenu().show(searchResult.location, pointDescription, searchResult.object); hide(); } } diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/QuickSearchHelper.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/QuickSearchHelper.java index 1c93f4dbd1..fcc3fe644e 100644 --- a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/QuickSearchHelper.java +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/QuickSearchHelper.java @@ -3,6 +3,7 @@ package net.osmand.core.samples.android.sample1.search; import net.osmand.IndexConstants; import net.osmand.binary.BinaryMapIndexReader; import net.osmand.core.samples.android.sample1.SampleApplication; +import net.osmand.core.samples.android.sample1.SampleUtils; import net.osmand.search.SearchUICore; import net.osmand.search.SearchUICore.SearchResultCollection; @@ -47,8 +48,8 @@ public class QuickSearchHelper { public void setRepositoriesForSearchUICore(final SampleApplication app) { ArrayList files = new ArrayList(); File appPath = app.getAppPath(null); - collectFiles(appPath, IndexConstants.BINARY_MAP_INDEX_EXT, files); - collectFiles(app.getAppPath(IndexConstants.WIKI_INDEX_DIR), IndexConstants.BINARY_MAP_INDEX_EXT, files); + SampleUtils.collectFiles(appPath, IndexConstants.BINARY_MAP_INDEX_EXT, files); + SampleUtils.collectFiles(app.getAppPath(IndexConstants.WIKI_INDEX_DIR), IndexConstants.BINARY_MAP_INDEX_EXT, files); List readers = new ArrayList<>(); for (File f : files) { @@ -62,19 +63,4 @@ public class QuickSearchHelper { } core.getSearchSettings().setOfflineIndexes(readers); } - - private List collectFiles(File dir, String ext, List files) { - if (dir.exists() && dir.canRead()) { - File[] lf = dir.listFiles(); - if (lf == null || lf.length == 0) { - return files; - } - for (File f : lf) { - if (f.getName().endsWith(ext)) { - files.add(f); - } - } - } - return files; - } } diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/QuickSearchListItem.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/QuickSearchListItem.java index 3d5762d8ee..f0658d0afd 100644 --- a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/QuickSearchListItem.java +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/QuickSearchListItem.java @@ -182,9 +182,8 @@ public class QuickSearchListItem { case LOCATION: LatLon latLon = (LatLon) searchResult.object; if (searchResult.localeRelatedObjectName == null) { - // todo - //String locationCountry = app.getRegions().getCountryName(latLon); - //searchResult.localeRelatedObjectName = locationCountry == null ? "" : locationCountry; + String locationCountry = app.getRegions().getCountryName(latLon); + searchResult.localeRelatedObjectName = locationCountry == null ? "" : locationCountry; } return searchResult.localeRelatedObjectName; case REGION: