Support parse location

This commit is contained in:
Victor Shcherb 2016-07-12 22:06:57 +03:00
parent f7736d6263
commit 9bec23f6ee
16 changed files with 259 additions and 150 deletions

View file

@ -0,0 +1,153 @@
package net.osmand;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import java.util.StringTokenizer;
public class LocationConvert {
////////////////////////////////////////////////////////////////////////////
// THIS code is copied from Location.convert() in order to change locale
// and to fix bug in android implementation : doesn't convert if min = 59.23 or sec = 59.32 or deg=179.3
public static final int FORMAT_DEGREES = 0;
public static final int FORMAT_MINUTES = 1;
public static final int FORMAT_SECONDS = 2;
public static final int UTM_FORMAT = 3;
private static final char DELIM = ':';
/**
* Converts a String in one of the formats described by
* FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS into a
* double.
*
* @throws NullPointerException if coordinate is null
* @throws IllegalArgumentException if the coordinate is not
* in one of the valid formats.
*/
public static double convert(String coordinate, boolean throwException) {
coordinate = coordinate.replace(' ', ':').replace('#', ':').replace(',', '.')
.replace('\'', ':').replace('\"', ':');
if (coordinate == null) {
if(!throwException) {
return Double.NaN;
} else {
throw new NullPointerException("coordinate");
}
}
boolean negative = false;
if (coordinate.charAt(0) == '-') {
coordinate = coordinate.substring(1);
negative = true;
}
StringTokenizer st = new StringTokenizer(coordinate, ":");
int tokens = st.countTokens();
if (tokens < 1) {
if(!throwException) {
return Double.NaN;
} else {
throw new IllegalArgumentException("coordinate=" + coordinate);
}
}
try {
String degrees = st.nextToken();
double val;
if (tokens == 1) {
val = Double.parseDouble(degrees);
return negative ? -val : val;
}
String minutes = st.nextToken();
int deg = Integer.parseInt(degrees);
double min;
double sec = 0.0;
if (st.hasMoreTokens()) {
min = Integer.parseInt(minutes);
String seconds = st.nextToken();
sec = Double.parseDouble(seconds);
} else {
min = Double.parseDouble(minutes);
}
boolean isNegative180 = negative && (deg == 180) &&
(min == 0) && (sec == 0);
// deg must be in [0, 179] except for the case of -180 degrees
if ((deg < 0.0) || (deg > 180 && !isNegative180)) {
if(!throwException) {
return Double.NaN;
} else {
throw new IllegalArgumentException("coordinate=" + coordinate);
}
}
if (min < 0 || min > 60d) {
if(!throwException) {
return Double.NaN;
} else {
throw new IllegalArgumentException("coordinate=" +
coordinate);
}
}
if (sec < 0 || sec > 60d) {
if(!throwException) {
return Double.NaN;
} else {
throw new IllegalArgumentException("coordinate=" +
coordinate);
}
}
val = deg*3600.0 + min*60.0 + sec;
val /= 3600.0;
return negative ? -val : val;
} catch (NumberFormatException nfe) {
if(!throwException) {
return Double.NaN;
} else {
throw new IllegalArgumentException("coordinate=" + coordinate);
}
}
}
public static String convert(double coordinate, int outputType) {
if (coordinate < -180.0 || coordinate > 180.0 || Double.isNaN(coordinate)) {
throw new IllegalArgumentException("coordinate=" + coordinate); //$NON-NLS-1$
}
if ((outputType != FORMAT_DEGREES) && (outputType != FORMAT_MINUTES) && (outputType != FORMAT_SECONDS)) {
throw new IllegalArgumentException("outputType=" + outputType); //$NON-NLS-1$
}
StringBuilder sb = new StringBuilder();
// Handle negative values
if (coordinate < 0) {
sb.append('-');
coordinate = -coordinate;
}
DecimalFormat df = new DecimalFormat("###.#####", new DecimalFormatSymbols(Locale.US)); //$NON-NLS-1$
if (outputType == FORMAT_MINUTES || outputType == FORMAT_SECONDS) {
int degrees = (int) Math.floor(coordinate);
sb.append(degrees);
sb.append(DELIM);
coordinate -= degrees;
coordinate *= 60.0;
if (outputType == FORMAT_SECONDS) {
int minutes = (int) Math.floor(coordinate);
sb.append(minutes);
sb.append(DELIM);
coordinate -= minutes;
coordinate *= 60.0;
}
}
sb.append(df.format(coordinate));
return sb.toString();
}
}

View file

@ -94,7 +94,7 @@ public class SearchUICore {
apis.add(new SearchCoreFactory.SearchAmenityByNameAPI());
apis.add(new SearchCoreFactory.SearchStreetByCityAPI());
apis.add(new SearchCoreFactory.SearchBuildingAndIntersectionsByStreetAPI());
apis.add(new SearchCoreFactory.SearchUrlAPI());
apis.add(new SearchCoreFactory.SearchLocationAndUrlAPI());
apis.add(new SearchCoreFactory.SearchAddressByNameAPI());
}

View file

@ -2,7 +2,7 @@ package net.osmand.search.example.core;
public enum ObjectType {
CITY(true), VILLAGE(true), POSTCODE(true), STREET(true), HOUSE(true),
STREET_INTERSECTION(true), POI_TYPE(false), POI(true), LOCATION(true), FAVORITE(true),
STREET_INTERSECTION(true), POI_TYPE(false), POI(true), LOCATION(true), PARTIAL_LOCATION(false), FAVORITE(true),
REGION(true), RECENT_OBJ(true), WPT(true), UNKNOWN_NAME_FILTER(false);
private boolean hasLocation;
private ObjectType(boolean location) {

View file

@ -14,6 +14,7 @@ import java.util.Map.Entry;
import java.util.TreeSet;
import net.osmand.CollatorStringMatcher.StringMatcherMode;
import net.osmand.LocationConvert;
import net.osmand.OsmAndCollator;
import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapAddressReaderAdapter;
@ -42,18 +43,20 @@ import net.osmand.util.GeoPointParserUtil;
import net.osmand.util.GeoPointParserUtil.GeoParsedPoint;
import net.osmand.util.MapUtils;
import com.jwetherell.openmap.common.LatLonPoint;
import com.jwetherell.openmap.common.UTMPoint;
public class SearchCoreFactory {
// TODO streets by city (+)
// TODO add url parse (geo) (+)
// TODO amenity by name (+)
// TODO show buildings if street is one or default ( <CITY>, CITY (den ilp), 1186RM) Зеленогорск? (+)
// TODO ignore streets with <> when search by name (+)
// TODO add location parse (+)
// TODO add location partial (+)
// TODO add UTM support
// TODO geo:34.99393,-106.61568 (Treasure Island) and display url parse
// TODO add full text search with comma correct order
// TODO MED add full text search without comma and different word order
// TODO add location parse
// TODO geo:34.99393,-106.61568 (Treasure Island) and display url parse
// TODO exclude duplicate streets/cities...
// TODO MED support poi additional select type and search
@ -750,24 +753,85 @@ public class SearchCoreFactory {
public static class SearchUrlAPI extends SearchBaseAPI {
public static class SearchLocationAndUrlAPI extends SearchBaseAPI {
// newFormat = PointDescription.FORMAT_DEGREES;
// newFormat = PointDescription.FORMAT_MINUTES;
// newFormat = PointDescription.FORMAT_SECONDS;
public void testUTM() {
double northing = 0;
double easting = 0;
String zone = "";
char c = zone.charAt(zone.length() -1);
int z = Integer.parseInt(zone.substring(0, zone.length() - 1));
UTMPoint upoint = new UTMPoint(northing, easting, z, c);
LatLonPoint ll = upoint.toLatLonPoint();
LatLon loc = new LatLon(ll.getLatitude(), ll.getLongitude());
}
@Override
public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) throws IOException {
if(phrase.getLastWord().length() == 0) {
return false;
}
parseLocation(phrase, resultMatcher);
parseUrl(phrase, resultMatcher);
return super.search(phrase, resultMatcher);
}
private boolean isKindOfNumber(String s) {
for(int i = 0; i < s.length(); i ++) {
char c = s.charAt(i);
if(c >= '0' && c <= '9') {
} else if(c == ':' || c == '.' || c == '#' || c == ',' || c == '-' || c == '\'' || c == '"') {
} else {
return false;
}
}
return true;
}
private void parseLocation(SearchPhrase phrase, SearchResultMatcher resultMatcher) {
String lw = phrase.getLastWord();
SearchWord sw = phrase.getLastSelectedWord();
double dd = LocationConvert.convert(lw, false);
if(!Double.isNaN(dd)) {
double pd = Double.NaN;
if(sw != null && sw.getType() == ObjectType.UNKNOWN_NAME_FILTER) {
if(isKindOfNumber(sw.getWord())) {
pd = LocationConvert.convert(sw.getWord(), false);
}
}
if(!Double.isNaN(pd)) {
SearchResult sp = new SearchResult(phrase);
sp.priority = 0;
sp.object = sp.location = new LatLon(pd, dd);
sp.localeName = ((float)sp.location.getLatitude()) +", " + ((float) sp.location.getLongitude());
sp.objectType = ObjectType.LOCATION;
sp.wordsSpan = 2;
resultMatcher.publish(sp);
} else {
SearchResult sp = new SearchResult(phrase);
sp.priority = 0;
sp.object = sp.location = new LatLon(dd, 0);
sp.localeName = ((float) sp.location.getLatitude()) + ", <input> ";
sp.objectType = ObjectType.PARTIAL_LOCATION;
resultMatcher.publish(sp);
}
}
}
private void parseUrl(SearchPhrase phrase, SearchResultMatcher resultMatcher) {
GeoParsedPoint pnt = GeoPointParserUtil.parse(phrase.getLastWord());
if(pnt != null && pnt.isGeoPoint()) {
SearchResult sp = new SearchResult(phrase);
sp.priority = 0;
sp.object = pnt;
sp.location = new LatLon(pnt.getLatitude(), pnt.getLongitude());
sp.localeName = "geo:" + ((float)pnt.getLatitude()) +", " + ((float) pnt.getLongitude());
sp.localeName = ((float)pnt.getLatitude()) +", " + ((float) pnt.getLongitude());
sp.objectType = ObjectType.LOCATION;
resultMatcher.publish(sp);
}
return super.search(phrase, resultMatcher);
}
@Override

View file

@ -191,6 +191,12 @@ public class SearchPhrase {
public SearchPhrase selectWord(SearchResult res) {
SearchPhrase sp = new SearchPhrase(this.settings);
sp.words.addAll(this.words);
while(res.wordsSpan > 1) {
if(sp.words.size() > 0) {
sp.words.remove(sp.words.size() - 1);
}
res.wordsSpan--;
}
SearchWord sw = new SearchWord(res.localeName.trim(), res);
sp.words.add(sw);
return sp;

View file

@ -37,6 +37,8 @@ public class SearchResult {
public String localeRelatedObjectName;
public double distRelatedObjectName;
public int wordsSpan = 1;

View file

@ -1,21 +1,16 @@
package net.osmand.data;
import net.osmand.LocationConvert;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandSettings;
import net.osmand.plus.R;
import net.osmand.util.Algorithms;
import android.content.Context;
import android.support.annotation.NonNull;
import com.jwetherell.openmap.common.LatLonPoint;
import com.jwetherell.openmap.common.UTMPoint;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandSettings;
import net.osmand.plus.R;
import net.osmand.util.Algorithms;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import java.util.StringTokenizer;
public class PointDescription {
private String type = "";
private String name = "";
@ -150,8 +145,8 @@ public class PointDescription {
+ ((long) pnt.easting);
} else {
try {
return ctx.getString(sh ? R.string.short_location_on_map : R.string.location_on_map, convert(lat, f),
convert(lon, f));
return ctx.getString(sh ? R.string.short_location_on_map : R.string.location_on_map, LocationConvert.convert(lat, f),
LocationConvert.convert(lon, f));
} catch(RuntimeException e) {
e.printStackTrace();
return ctx.getString(sh ? R.string.short_location_on_map : R.string.location_on_map, 0, 0);
@ -294,136 +289,24 @@ public class PointDescription {
return pd;
}
////////////////////////////////////////////////////////////////////////////
// THIS code is copied from Location.convert() in order to change locale
// and to fix bug in android implementation : doesn't convert if min = 59.23 or sec = 59.32 or deg=179.3
public static final int FORMAT_DEGREES = 0;
public static final int FORMAT_MINUTES = 1;
public static final int FORMAT_SECONDS = 2;
public static final int UTM_FORMAT = 3;
private static final char DELIM = ':';
public static final int FORMAT_DEGREES = LocationConvert.FORMAT_DEGREES;
public static final int FORMAT_MINUTES = LocationConvert.FORMAT_MINUTES;
public static final int FORMAT_SECONDS = LocationConvert.FORMAT_SECONDS;
public static final int UTM_FORMAT = LocationConvert.UTM_FORMAT;
public static String formatToHumanString(Context ctx, int format) {
switch (format) {
case FORMAT_DEGREES:
case LocationConvert.FORMAT_DEGREES:
return ctx.getString(R.string.navigate_point_format_D);
case FORMAT_MINUTES:
case LocationConvert.FORMAT_MINUTES:
return ctx.getString(R.string.navigate_point_format_DM);
case FORMAT_SECONDS:
case LocationConvert.FORMAT_SECONDS:
return ctx.getString(R.string.navigate_point_format_DMS);
case UTM_FORMAT:
case LocationConvert.UTM_FORMAT:
return "UTM";
default:
return "Unknown format";
}
}
/**
* Converts a String in one of the formats described by
* FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS into a
* double.
*
* @throws NullPointerException if coordinate is null
* @throws IllegalArgumentException if the coordinate is not
* in one of the valid formats.
*/
public static double convert(String coordinate) {
coordinate = coordinate.replace(' ', ':').replace('#', ':').replace(',', '.');
if (coordinate == null) {
throw new NullPointerException("coordinate");
}
boolean negative = false;
if (coordinate.charAt(0) == '-') {
coordinate = coordinate.substring(1);
negative = true;
}
StringTokenizer st = new StringTokenizer(coordinate, ":");
int tokens = st.countTokens();
if (tokens < 1) {
throw new IllegalArgumentException("coordinate=" + coordinate);
}
try {
String degrees = st.nextToken();
double val;
if (tokens == 1) {
val = Double.parseDouble(degrees);
return negative ? -val : val;
}
String minutes = st.nextToken();
int deg = Integer.parseInt(degrees);
double min;
double sec = 0.0;
if (st.hasMoreTokens()) {
min = Integer.parseInt(minutes);
String seconds = st.nextToken();
sec = Double.parseDouble(seconds);
} else {
min = Double.parseDouble(minutes);
}
boolean isNegative180 = negative && (deg == 180) &&
(min == 0) && (sec == 0);
// deg must be in [0, 179] except for the case of -180 degrees
if ((deg < 0.0) || (deg > 180 && !isNegative180)) {
throw new IllegalArgumentException("coordinate=" + coordinate);
}
if (min < 0 || min > 60d) {
throw new IllegalArgumentException("coordinate=" +
coordinate);
}
if (sec < 0 || sec > 60d) {
throw new IllegalArgumentException("coordinate=" +
coordinate);
}
val = deg*3600.0 + min*60.0 + sec;
val /= 3600.0;
return negative ? -val : val;
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException("coordinate=" + coordinate);
}
}
public static String convert(double coordinate, int outputType) {
if (coordinate < -180.0 || coordinate > 180.0 || Double.isNaN(coordinate)) {
throw new IllegalArgumentException("coordinate=" + coordinate); //$NON-NLS-1$
}
if ((outputType != FORMAT_DEGREES) && (outputType != FORMAT_MINUTES) && (outputType != FORMAT_SECONDS)) {
throw new IllegalArgumentException("outputType=" + outputType); //$NON-NLS-1$
}
StringBuilder sb = new StringBuilder();
// Handle negative values
if (coordinate < 0) {
sb.append('-');
coordinate = -coordinate;
}
DecimalFormat df = new DecimalFormat("###.#####", new DecimalFormatSymbols(Locale.US)); //$NON-NLS-1$
if (outputType == FORMAT_MINUTES || outputType == FORMAT_SECONDS) {
int degrees = (int) Math.floor(coordinate);
sb.append(degrees);
sb.append(DELIM);
coordinate -= degrees;
coordinate *= 60.0;
if (outputType == FORMAT_SECONDS) {
int minutes = (int) Math.floor(coordinate);
sb.append(minutes);
sb.append(DELIM);
coordinate -= minutes;
coordinate *= 60.0;
}
}
sb.append(df.format(coordinate));
return sb.toString();
}
}

View file

@ -23,6 +23,7 @@ import android.widget.TextView;
import com.jwetherell.openmap.common.LatLonPoint;
import com.jwetherell.openmap.common.UTMPoint;
import net.osmand.LocationConvert;
import net.osmand.PlatformUtil;
import net.osmand.data.LatLon;
import net.osmand.data.PointDescription;
@ -166,14 +167,14 @@ public class NavigatePointFragment extends Fragment implements SearchActivityChi
northingEdit.setText(((long)pnt.northing)+"");
eastingEdit.setText(((long)pnt.easting)+"");
} else {
latEdit.setText(PointDescription. convert(MapUtils.checkLatitude(l.getLatitude()), currentFormat));
lonEdit.setText(PointDescription. convert(MapUtils.checkLongitude(l.getLongitude()), currentFormat));
latEdit.setText(LocationConvert.convert(MapUtils.checkLatitude(l.getLatitude()), currentFormat));
lonEdit.setText(LocationConvert.convert(MapUtils.checkLongitude(l.getLongitude()), currentFormat));
}
}
protected LatLon parseLocation() {
LatLon loc ;
if(currentFormat == PointDescription.UTM_FORMAT) {
if(currentFormat == LocationConvert.UTM_FORMAT) {
double northing = Double.parseDouble(((EditText)view.findViewById(R.id.NorthingEdit)).getText().toString());
double easting = Double.parseDouble(((EditText)view.findViewById(R.id.EastingEdit)).getText().toString());
String zone = ((EditText)view.findViewById(R.id.ZoneEdit)).getText().toString();
@ -183,8 +184,8 @@ public class NavigatePointFragment extends Fragment implements SearchActivityChi
LatLonPoint ll = upoint.toLatLonPoint();
loc = new LatLon(ll.getLatitude(), ll.getLongitude());
} else {
double lat = PointDescription. convert(((EditText) view.findViewById(R.id.LatitudeEdit)).getText().toString());
double lon = PointDescription. convert(((EditText) view.findViewById(R.id.LongitudeEdit)).getText().toString());
double lat = LocationConvert.convert(((EditText) view.findViewById(R.id.LatitudeEdit)).getText().toString(), true);
double lon = LocationConvert.convert(((EditText) view.findViewById(R.id.LongitudeEdit)).getText().toString(), true);
loc = new LatLon(lat, lon);
}
return loc;