package net.osmand.core.samples.android.sample1;
import android.os.AsyncTask;
import android.os.Build;
import net.osmand.Location;
import net.osmand.ResultMatcher;
import net.osmand.binary.GeocodingUtilities.GeocodingResult;
import net.osmand.binary.RouteDataObject;
import net.osmand.data.LatLon;
import net.osmand.util.Algorithms;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
public class GeocodingLookupService {
private SampleApplication app;
private ConcurrentLinkedQueue<LatLon> lookupLocations = new ConcurrentLinkedQueue<>();
private ConcurrentHashMap<LatLon, List<AddressLookupRequest>> addressLookupRequestsMap = new ConcurrentHashMap<>();
private LatLon currentRequestedLocation = null;
private boolean searchDone;
private String lastFoundAddress;
public interface OnAddressLookupProgress {
void geocodingInProgress();
}
public interface OnAddressLookupResult {
void geocodingDone(String address);
}
public static class AddressLookupRequest {
private LatLon latLon;
private OnAddressLookupResult uiResultCallback;
private OnAddressLookupProgress uiProgressCallback;
public AddressLookupRequest(LatLon latLon, OnAddressLookupResult uiResultCallback,
OnAddressLookupProgress uiProgressCallback) {
this.latLon = latLon;
this.uiResultCallback = uiResultCallback;
this.uiProgressCallback = uiProgressCallback;
}
public LatLon getLatLon() {
return latLon;
}
}
public GeocodingLookupService(SampleApplication app) {
this.app = app;
}
public void lookupAddress(AddressLookupRequest request) {
synchronized (this) {
LatLon requestedLocation = request.latLon;
LatLon existingLocation = null;
if (requestedLocation.equals(currentRequestedLocation)) {
existingLocation = currentRequestedLocation;
requestedLocation = existingLocation;
request.latLon = existingLocation;
} else if (lookupLocations.contains(requestedLocation)) {
for (LatLon latLon : lookupLocations) {
if (latLon.equals(requestedLocation)) {
existingLocation = latLon;
requestedLocation = latLon;
request.latLon = latLon;
break;
}
}
}
List<AddressLookupRequest> list = addressLookupRequestsMap.get(requestedLocation);
if (list == null) {
list = new ArrayList<>();
addressLookupRequestsMap.put(requestedLocation, list);
}
list.add(request);
if (existingLocation == null) {
lookupLocations.add(requestedLocation);
}
if (currentRequestedLocation == null && !lookupLocations.isEmpty()) {
currentRequestedLocation = lookupLocations.peek();
execute(new AddressLookupRequestsAsyncTask(app));
}
}
}
public void cancel(AddressLookupRequest request) {
synchronized (this) {
List<AddressLookupRequest> requests = addressLookupRequestsMap.get(request.latLon);
if (requests != null && requests.size() > 0) {
requests.remove(request);
}
}
}
public void cancel(LatLon latLon) {
synchronized (this) {
List<AddressLookupRequest> requests = addressLookupRequestsMap.get(latLon);
if (requests != null && requests.size() > 0) {
requests.clear();
}
}
}
private boolean hasAnyRequest(LatLon latLon) {
synchronized (this) {
List<AddressLookupRequest> requests = addressLookupRequestsMap.get(latLon);
return requests != null && requests.size() > 0;
}
}
@SuppressWarnings("unchecked")
private <P> void execute(AsyncTask<P, ?, ?> task, P... requests) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, requests);
} else {
task.execute(requests);
}
}
private boolean geocode(final LatLon latLon) {
Location loc = new Location("");
loc.setLatitude(latLon.getLatitude());
loc.setLongitude(latLon.getLongitude());
return app.getLocationProvider()
.getGeocodingResult(loc, new ResultMatcher<GeocodingResult>() {
@Override
public boolean publish(GeocodingResult object) {
String result = null;
if (object != null) {
String lang = SampleApplication.LANGUAGE;
boolean transliterate = SampleApplication.TRANSLITERATE;
String geocodingResult = "";
double relevantDistance = -1;
if (object.building != null) {
String bldName = object.building.getName(lang, transliterate);
if (!Algorithms.isEmpty(object.buildingInterpolation)) {
bldName = object.buildingInterpolation;
}
geocodingResult = object.street.getName(lang, transliterate) + " " + bldName + ", "
+ object.city.getName(lang, transliterate);
} else if (object.street != null) {
geocodingResult = object.street.getName(lang, transliterate) + ", " + object.city.getName(lang, transliterate);
relevantDistance = object.getDistanceP();
} else if (object.city != null) {
geocodingResult = object.city.getName(lang, transliterate);
} else if (object.point != null) {
RouteDataObject rd = object.point.getRoad();
String sname = rd.getName(lang, transliterate);
if (Algorithms.isEmpty(sname)) {
sname = "";
}
String ref = rd.getRef(lang, transliterate, true);
if (!Algorithms.isEmpty(ref)) {
if (!Algorithms.isEmpty(sname)) {
sname += ", ";
}
sname += ref;
}
geocodingResult = sname;
relevantDistance = object.getDistanceP();
}
result = geocodingResult;
if (relevantDistance == -1) {
relevantDistance = object.getDistance();
}
if (!Algorithms.isEmpty(result) && relevantDistance > 100) {
result = app.getString("shared_string_near") + " " + result;
}
}
lastFoundAddress = result;
searchDone = true;
return true;
}
@Override
public boolean isCancelled() {
return !hasAnyRequest(latLon);
}
});
}
private class AddressLookupRequestsAsyncTask extends AsyncTask<AddressLookupRequest, AddressLookupRequest, Void> {
private SampleApplication app;
public AddressLookupRequestsAsyncTask(SampleApplication app) {
super();
this.app = app;
}
@Override
protected Void doInBackground(AddressLookupRequest... addressLookupRequests) {
for (;;) {
try {
while (!lookupLocations.isEmpty()) {
final LatLon latLon;
synchronized (GeocodingLookupService.this) {
latLon = lookupLocations.poll();
currentRequestedLocation = latLon;
List<AddressLookupRequest> requests = addressLookupRequestsMap.get(latLon);
if (requests == null || requests.size() == 0) {
addressLookupRequestsMap.remove(latLon);
continue;
}
}
// geocode
searchDone = false;
while (!geocode(latLon)) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long counter = 0;
while (!searchDone) {
try {
Thread.sleep(50);
counter++;
// call progress every 500 ms
if (counter == 10) {
counter = 0;
synchronized (GeocodingLookupService.this) {
List<AddressLookupRequest> requests = addressLookupRequestsMap.get(latLon);
for (final AddressLookupRequest request : requests) {
if (request.uiProgressCallback != null) {
app.runInUIThread(new Runnable() {
@Override
public void run() {
request.uiProgressCallback.geocodingInProgress();
}
});
}
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (GeocodingLookupService.this) {
List<AddressLookupRequest> requests = addressLookupRequestsMap.get(latLon);
for (final AddressLookupRequest request : requests) {
if (request.uiResultCallback != null) {
app.runInUIThread(new Runnable() {
@Override
public void run() {
request.uiResultCallback.geocodingDone(lastFoundAddress);
}
});
}
}
addressLookupRequestsMap.remove(latLon);
}
}
} catch (Exception e) {
e.printStackTrace();
}
synchronized (GeocodingLookupService.this) {
currentRequestedLocation = null;
if (lookupLocations.isEmpty()) {
break;
}
}
}
return null;
}
}
}