Fixed issue 172

Basic geo:intent search for query and lat,lon query.
This commit is contained in:
pavol.zibrita 2011-01-19 01:20:10 +01:00 committed by Pavol Zibrita
parent 4a93261150
commit ed10e42a39
9 changed files with 386 additions and 11 deletions

View file

@ -26,6 +26,11 @@ public class PostCode extends MapObject {
return streets.values();
}
public void removeAllStreets()
{
streets.clear();
}
public Street registerStreet(Street street, boolean useEnglishNames){
String name = street.getName(useEnglishNames);
streets.put(name, street);

View file

@ -39,6 +39,11 @@
<activity android:name=".activities.search.SearchStreet2ByNameActivity"></activity>
<activity android:name=".activities.search.SearchBuildingByNameActivity"></activity>
<activity android:name=".activities.EditPOIFilterActivity"></activity>
<activity android:name=".activities.search.GeoIntentActivity"><intent-filter><action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<data android:scheme="geo"></data>
</intent-filter>
</activity>
<service android:process="net.osmand.plus" android:label="@string/process_navigation_service" android:name=".NavigationService">
<intent-filter><action android:name="net.osmand.plus.NavigationService"></action></intent-filter>
</service>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" android:orientation="vertical">
<TextView android:id="@+id/TextView" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal"
android:layout_marginTop = "5dp" android:text="@string/search_osm_offline"/>
<ListView android:id="@android:id/list" android:layout_width="fill_parent" android:layout_weight="1" android:layout_height="wrap_content"></ListView>
</LinearLayout>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" android:orientation="horizontal">
<TextView android:id="@+id/distance_label" android:layout_marginLeft="5dp"
android:layout_width="80dp" android:layout_height="wrap_content" android:gravity="left"
android:textSize="20sp"/>
<TextView android:id="@+id/label" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:textSize="20sp" />
</LinearLayout>

View file

@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<resources>
<string name="error_doing_search">Error occurred in offline search</string>
<string name="search_offline_geo_error">Could not parse geo intent:{0}</string>
<string name="search_osm_offline">Search address using offline maps</string>
<string name="system_locale">System</string>
<string name="preferred_locale_descr">Change display language</string>
<string name="preferred_locale">Preferred locale</string>

View file

@ -0,0 +1,337 @@
package net.osmand.activities.search;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import net.osmand.OsmandSettings;
import net.osmand.R;
import net.osmand.RegionAddressRepository;
import net.osmand.ResourceManager;
import net.osmand.activities.MapActivity;
import net.osmand.activities.OsmandApplication;
import net.osmand.data.City;
import net.osmand.data.MapObject;
import net.osmand.data.PostCode;
import net.osmand.data.Street;
import net.osmand.osm.LatLon;
import net.osmand.osm.MapUtils;
import net.osmand.osm.Node;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class GeoIntentActivity extends ListActivity {
private ProgressDialog progressDlg;
private LatLon location;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search_address_offline);
location = OsmandSettings.getLastKnownMapLocation(OsmandSettings
.getPrefs(this));
final Intent intent = getIntent();
if (intent != null) {
progressDlg = ProgressDialog.show(this,
getString(R.string.searching),
getString(R.string.searching_address));
final Thread searcher = new Thread(new Runnable() {
@Override
public void run() {
try {
Collection<MapObject> results = extract(
intent.getData()).execute();
// show the first result on map, and populate the list!
if (!results.isEmpty()) {
showResult(0, new ArrayList<MapObject>(results));
} else {
showResult(R.string.search_nothing_found, null);
}
} catch (Exception e) {
e.printStackTrace();
showResult(R.string.error_doing_search, null);
} finally {
progressDlg.dismiss();
}
}
}, "SearchingAddress");
searcher.start();
progressDlg.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
searcher.interrupt();
}
});
progressDlg.setCancelable(true);
}
// finish();
}
private void showResult(final int warning, final List<MapObject> places) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (places == null) {
Toast.makeText(GeoIntentActivity.this, getString(warning),
Toast.LENGTH_LONG).show();
} else {
setListAdapter(new MapObjectAdapter(places));
if (places.size() == 1) {
onListItemClick(getListView(), getListAdapter()
.getView(0, null, null), 0, getListAdapter()
.getItemId(0));
}
}
}
});
}
class MapObjectAdapter extends ArrayAdapter<MapObject> {
public MapObjectAdapter(List<MapObject> places) {
super(GeoIntentActivity.this,
R.layout.search_address_offline_list_item, places);
}
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
LayoutInflater inflater = getLayoutInflater();
row = inflater.inflate(
R.layout.search_address_offline_list_item, parent,
false);
}
MapObject model = getItem(position);
TextView label = (TextView) row.findViewById(R.id.label);
TextView distanceLabel = (TextView) row
.findViewById(R.id.distance_label);
if (location != null) {
int dist = (int) (MapUtils.getDistance(location, model
.getLocation().getLatitude(), model.getLocation()
.getLongitude()));
distanceLabel.setText(MapUtils.getFormattedDistance(dist));
} else {
distanceLabel.setText(""); //$NON-NLS-1$
}
label.setText(model.toString());
return row;
}
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
MapObject item = ((MapObjectAdapter) getListAdapter())
.getItem(position);
OsmandSettings.setMapLocationToShow(this, item.getLocation()
.getLatitude(), item.getLocation().getLongitude(),
getString(R.string.address) + " : " + item.toString()); //$NON-NLS-1$
startActivity(new Intent(this, MapActivity.class));
}
@Override
protected void onStop() {
dismiss();
super.onStop();
}
private void dismiss() {
if (progressDlg != null) {
progressDlg.dismiss();
progressDlg = null;
}
}
/**
* geo:latitude,longitude<BR>
* geo:latitude,longitude?z=zoom<BR>
* geo:0,0?q=my+street+address<BR>
* geo:0,0?q=business+near+city
*
* @param data
* @return
*/
private MyService extract(Uri data) {
// it is 0,0? that means a search
if (data.getSchemeSpecificPart().indexOf("0,0?") != -1) {
return new GeoAddressSearch(data.getQuery());
} else {
return new GeoPointSearch(data.getSchemeSpecificPart());
}
}
private final class GeoAddressSearch implements MyService {
private List<String> elements;
public GeoAddressSearch(String query) {
StringTokenizer s = new StringTokenizer(query.substring(query
.indexOf("q=") + 2), ",");
elements = new ArrayList<String>(s.countTokens());
while (s.hasMoreTokens()) {
elements.add(s.nextToken().replace('+', ' ').trim());
}
}
@Override
public Collection<MapObject> execute() {
if (elements.isEmpty()) {
return Collections.emptyList();
}
// now try to search the City, Street, Etc.. if Street is not found,
// try to search POI
ResourceManager resourceManager = resourceManager();
List<RegionAddressRepository> foundCountries = new ArrayList<RegionAddressRepository>();
RegionAddressRepository country;
for (String maybeCountry : elements) {
country = resourceManager.getRegionRepository(maybeCountry);
if (country != null) {
foundCountries.add(country);
}
}
Collection<RegionAddressRepository> countriesToSearch = foundCountries;
if (foundCountries.isEmpty()) {
// there is no country, we have to search each country
countriesToSearch = resourceManager.getAddressRepositories();
}
// search cities for found countries
Map<RegionAddressRepository, List<MapObject>> citiesForRegion = new HashMap<RegionAddressRepository, List<MapObject>>();
for (RegionAddressRepository rar : countriesToSearch) {
List<MapObject> citiesFound = new ArrayList<MapObject>();
for (String maybeCity : elements) {
rar.fillWithSuggestedCities(maybeCity, citiesFound, null);
}
if (!citiesFound.isEmpty()) {
citiesForRegion.put(rar, citiesFound);
}
}
// no cities found, we should locate the country only
Map<MapObject, List<Street>> streetsForCity = new HashMap<MapObject, List<Street>>();
if (citiesForRegion.isEmpty()) {
for (RegionAddressRepository rar : countriesToSearch) {
ArrayList<MapObject> allcities = new ArrayList<MapObject>();
rar.fillWithSuggestedCities("", allcities, location);
findStreetsForCities(streetsForCity, rar, allcities);
}
} else {
// we have cities, now search for streets?
for (RegionAddressRepository rar : citiesForRegion.keySet()) {
findStreetsForCities(streetsForCity, rar,
citiesForRegion.get(rar));
}
}
// don't go deeper, now populate result list
Set<MapObject> results = new HashSet<MapObject>();
// add all found lists
for (List<Street> streets : streetsForCity.values()) {
results.addAll(streets);
}
// add all found cities for which street was not found
for (List<MapObject> cities : citiesForRegion.values()) {
cities.removeAll(streetsForCity.keySet());
results.addAll(cities);
}
// TODO add all regions for which city was not found
return results;
}
private void findStreetsForCities(
Map<MapObject, List<Street>> streetsForCity,
RegionAddressRepository rar, List<MapObject> allcities) {
for (MapObject city : allcities) {
List<Street> streets = new ArrayList<Street>();
rar.fillWithSuggestedStreets(city, streets,
elements.toArray(new String[] {}));
// we must do this, or we will fill up the whole memory (streets
// are preloaded...)
// TODO some street iterator would be better, is it possible to
// create one?
if (city instanceof City) {
((City) city).removeAllStreets();
} else if (city instanceof PostCode) {
((PostCode) city).removeAllStreets();
}
if (!streets.isEmpty()) {
streetsForCity.put(city, streets);
}
}
}
}
private ResourceManager resourceManager() {
return ((OsmandApplication) getApplication()).getResourceManager();
}
private class GeoPointSearch implements MyService {
private MapObject point;
/**
* geo:latitude,longitude geo:latitude,longitude?z=zoom
*/
public GeoPointSearch(final String geo) {
int latIndex = geo.indexOf(',');
int lonIndex = geo.indexOf('?');
lonIndex = lonIndex > 0 ? lonIndex : geo.length();
if (latIndex > 0) {
try {
double latitude = Double.parseDouble(geo.substring(0,
latIndex));
double longitude = Double.parseDouble(geo.substring(
latIndex + 1, lonIndex));
// TODO zoom is omited for now
point = new MapObject(new Node(latitude, longitude, -1)) {
};
point.setName("Lat: " + latitude + ",Lon:" + longitude);
} catch (NumberFormatException e) {
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(GeoIntentActivity.this,
getString(R.string.search_offline_geo_error, geo),
Toast.LENGTH_LONG);
}
});
}
}
}
@Override
public Collection<MapObject> execute() {
if (point != null) {
return Collections.singletonList(point);
} else {
return Collections.emptyList();
}
}
}
private interface MyService {
public Collection<MapObject> execute();
}
}

View file

@ -64,7 +64,7 @@ public interface RegionAddressRepository {
public void fillWithSuggestedStreetsIntersectStreets(City city, Street st, List<Street> streetsToFill);
public void fillWithSuggestedStreets(MapObject cityOrPostcode, String name, List<Street> streetsToFill);
public void fillWithSuggestedStreets(MapObject cityOrPostcode, List<Street> streetsToFill, String... name);
public void fillWithSuggestedCities(String name, List<MapObject> citiesToFill, LatLon currentLocation);

View file

@ -85,20 +85,22 @@ public class RegionAddressRepositoryBinary implements RegionAddressRepository {
@Override
public void fillWithSuggestedStreets(MapObject o, String name, List<Street> streetsToFill) {
public void fillWithSuggestedStreets(MapObject o, List<Street> streetsToFill, String... names) {
assert o instanceof PostCode || o instanceof City;
City city = (City) (o instanceof City ? o : null);
PostCode post = (PostCode) (o instanceof PostCode ? o : null);
name = name.toLowerCase();
preloadStreets(o);
Collection<Street> streets = post == null ? city.getStreets() : post.getStreets() ;
if(name.length() == 0){
if(names.length == 0){
streetsToFill.addAll(streets);
} else {
return;
}
int ind = 0;
for (Street s : streets) {
String sName = useEnglishNames ? s.getEnName() : s.getName(); //lower case not needed, collator ensures that
for (String name : names) {
if (cstartsWith(collator,sName,name)) {
streetsToFill.add(ind, s);
ind++;

View file

@ -36,7 +36,7 @@ public class SearchStreetByNameActivity extends SearchByNameAbstractActivity<Str
public List<Street> getObjects(String filter) {
List<Street> l = new ArrayList<Street>();
if (city != null || postcode != null) {
region.fillWithSuggestedStreets(postcode == null ? city : postcode, filter, l);
region.fillWithSuggestedStreets(postcode == null ? city : postcode, l, filter);
}
return l;
}