update location search

This commit is contained in:
Victor Shcherb 2016-07-14 12:02:07 +02:00
parent ab9e6c4198
commit 4652763b95
3 changed files with 390 additions and 84 deletions

View file

@ -15,7 +15,6 @@ 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;
import net.osmand.binary.BinaryMapIndexReader;
@ -48,24 +47,46 @@ import com.jwetherell.openmap.common.UTMPoint;
public class SearchCoreFactory {
// TODO add location parse (+)
// TODO add location partial (+)
// TODO geo:34.99393,-106.61568 (Treasure Island) and display url parse (+)
// TODO location 43°3833.24N 79°2313.7W,
// TODO tests for geo:, location,
// TODO add UTM support
// TODO tests for geo:, location (+)
// TODO add UTM support (+)
// TODO location 43°3833.24N 79°2313.7W (+)
// TODO partial location
// TODO add full text search with comma correct order
// TODO MED add full text search without comma and different word order
// TODO MED edit in the middle (with words and comma)?
// TODO exclude duplicate streets/cities/pois...
// TODO MED support poi additional select type and search
// TODO LOW display results momentarily
// TODO LOW automatically increase radius if nothing found (log radius search)
// TODO UI support poi additional select type and search
// TODO UI display results momentarily
// TODO UI automatically increase radius if nothing found (log radius search)
//////////////// CONSTANTS //////////
public static final int SEARCH_REGION_API_PRIORITY = 3;
public static final int SEARCH_REGION_OBJECT_PRIORITY = 10;
// context less
public static final int SEARCH_LOCATION_PRIORITY = 0;
public static final int SEARCH_AMENITY_TYPE_PRIORITY = 1;
public static final int SEARCH_AMENITY_TYPE_API_PRIORITY = 1;
// context
public static final int SEARCH_STREET_BY_CITY_PRIORITY = 2;
public static final int SEARCH_BUILDING_BY_CITY_PRIORITY = 3;
public static final int SEARCH_BUILDING_BY_STREET_PRIORITY = 1;
public static final int SEARCH_AMENITY_BY_TYPE_PRIORITY = 3;
// context less (slow)
public static final int SEARCH_ADDRESS_BY_NAME_API_PRIORITY = 5;
public static final int SEARCH_ADDRESS_BY_NAME_API_PRIORITY_RADIUS2 = 5;
public static final int SEARCH_ADDRESS_BY_NAME_PRIORITY = 5;
public static final int SEARCH_ADDRESS_BY_NAME_PRIORITY_RADIUS2 = 5;
// context less (slower)
public static final int SEARCH_AMENITY_BY_NAME_PRIORITY = 7;
public static final int SEARCH_AMENITY_BY_NAME_API_PRIORITY_IF_POI_TYPE = 7;
public static final int SEARCH_AMENITY_BY_NAME_API_PRIORITY_IF_3_CHAR = 7;
public static abstract class SearchBaseAPI implements SearchCoreAPI {
@Override
@ -79,8 +100,11 @@ public class SearchCoreFactory {
}
}
public static class SearchRegionByNameAPI extends SearchBaseAPI {
@Override
public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) {
for (BinaryMapIndexReader bmir : phrase.getOfflineIndexes()) {
@ -89,7 +113,7 @@ public class SearchCoreFactory {
sr.localeName = bmir.getRegionName();
sr.object = bmir;
sr.file = bmir;
sr.priority = 10;
sr.priority = SEARCH_REGION_OBJECT_PRIORITY;
sr.objectType = ObjectType.REGION;
sr.location = bmir.getRegionCenter();
sr.preferredZoom = 6;
@ -108,7 +132,7 @@ public class SearchCoreFactory {
if(!p.isNoSelectedType()) {
return -1;
}
return 3;
return SEARCH_REGION_API_PRIORITY;
}
}
@ -116,6 +140,8 @@ public class SearchCoreFactory {
private static final int DEFAULT_ADDRESS_BBOX_RADIUS = 1000*1000;
private static final int LIMIT = 10000;
private Map<BinaryMapIndexReader, List<City>> townCities = new LinkedHashMap<>();
private QuadTree<City> townCitiesQR = new QuadTree<City>(new QuadRect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE),
8, 0.55f);
@ -123,10 +149,13 @@ public class SearchCoreFactory {
@Override
public int getSearchPriority(SearchPhrase p) {
if (p.isNoSelectedType()) {
return 5;
if (!p.isNoSelectedType() && p.getRadiusLevel() == 1) {
return -1;
}
return 10;
if (p.isNoSelectedType()) {
return SEARCH_ADDRESS_BY_NAME_API_PRIORITY;
}
return SEARCH_ADDRESS_BY_NAME_API_PRIORITY_RADIUS2;
}
@Override
@ -198,7 +227,8 @@ public class SearchCoreFactory {
final QuadRect postcodeBbox = phrase.getRadiusBBoxToSearch(DEFAULT_ADDRESS_BBOX_RADIUS * 5);
final QuadRect villagesBbox = phrase.getRadiusBBoxToSearch(DEFAULT_ADDRESS_BBOX_RADIUS * 3);
final QuadRect cityBbox = phrase.getRadiusBBoxToSearch(DEFAULT_ADDRESS_BBOX_RADIUS * 10);
final int priority = phrase.isNoSelectedType() ? 3 : 5;
final int priority = phrase.isNoSelectedType() ?
SEARCH_ADDRESS_BY_NAME_PRIORITY : SEARCH_ADDRESS_BY_NAME_PRIORITY_RADIUS2;
final BinaryMapIndexReader[] currentFile = new BinaryMapIndexReader[1];
ResultMatcher<MapObject> rm = new ResultMatcher<MapObject>() {
int limit = 0;
@ -306,6 +336,7 @@ public class SearchCoreFactory {
public static class SearchAmenityByNameAPI extends SearchBaseAPI {
private static final int LIMIT = 10000;
private static final int BBOX_RADIUS = 1000 * 1000;
@Override
public boolean search(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException {
if(phrase.getLastWord().length() == 0) {
@ -334,7 +365,7 @@ public class SearchCoreFactory {
sr.preferredZoom = 17;
sr.file = currentFile[0];
sr.location = object.getLocation();
sr.priority = 3;
sr.priority = SEARCH_AMENITY_BY_NAME_PRIORITY;
sr.priorityDistance = 1;
sr.objectType = ObjectType.POI;
@ -363,13 +394,13 @@ public class SearchCoreFactory {
}
if(p.hasObjectType(ObjectType.POI_TYPE)) {
if(p.getRadiusLevel() > 1) {
return 5;
return SEARCH_AMENITY_BY_NAME_API_PRIORITY_IF_POI_TYPE;
} else {
return -1;
}
}
if(p.getLastWord().length() > 3 || p.getRadiusLevel() > 1) {
return 3;
return SEARCH_AMENITY_BY_NAME_API_PRIORITY_IF_3_CHAR;
}
return -1;
}
@ -378,28 +409,17 @@ public class SearchCoreFactory {
public static class SearchAmenityTypesAPI extends SearchBaseAPI {
private Map<String, PoiType> translatedNames;
private List<PoiFilter> topVisibleFilters;
private TreeSet<AbstractPoiType> results;
public SearchAmenityTypesAPI(MapPoiTypes types) {
translatedNames = types.getAllTranslatedNames(false);
topVisibleFilters = types.getTopVisibleFilters();
final net.osmand.Collator clt = OsmAndCollator.primaryCollator();
results = new TreeSet<>(new Comparator<AbstractPoiType>() {
@Override
public int compare(AbstractPoiType o1, AbstractPoiType o2) {
return clt.compare(o1.getTranslation(), o2.getTranslation());
}
});
}
@Override
public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) throws IOException {
// results.clear();
// final net.osmand.Collator clt = OsmAndCollator.primaryCollator();
TreeSet<AbstractPoiType> results = new TreeSet<>(new Comparator<AbstractPoiType>() {
@Override
@ -426,7 +446,7 @@ public class SearchCoreFactory {
SearchResult res = new SearchResult(phrase);
res.localeName = p.getTranslation();
res.object = p;
res.priority = 5;
res.priority = SEARCH_AMENITY_TYPE_PRIORITY;
res.priorityDistance = 0;
res.objectType = ObjectType.POI_TYPE;
resultMatcher.publish(res);
@ -436,15 +456,16 @@ public class SearchCoreFactory {
@Override
public int getSearchPriority(SearchPhrase p) {
if(p.hasObjectType(ObjectType.POI) ||p.hasObjectType(ObjectType.POI_TYPE)) {
if (p.hasObjectType(ObjectType.POI) || p.hasObjectType(ObjectType.POI_TYPE)) {
return -1;
}
return 1;
return SEARCH_AMENITY_TYPE_API_PRIORITY;
}
}
public static class SearchAmenityByTypeAPI extends SearchBaseAPI {
private MapPoiTypes types;
public SearchAmenityByTypeAPI(MapPoiTypes types) {
@ -542,7 +563,7 @@ public class SearchCoreFactory {
res.preferredZoom = 17;
res.file = selected[0];
res.location = object.getLocation();
res.priority = 3;
res.priority = SEARCH_AMENITY_BY_TYPE_PRIORITY;
res.priorityDistance = 1;
res.objectType = ObjectType.POI;
resultMatcher.publish(res);
@ -566,7 +587,7 @@ public class SearchCoreFactory {
public int getSearchPriority(SearchPhrase p) {
if(p.isLastWord(ObjectType.POI_TYPE) &&
p.getLastTokenLocation() != null) {
return 3;
return SEARCH_AMENITY_BY_TYPE_PRIORITY;
}
return -1;
}
@ -575,6 +596,7 @@ public class SearchCoreFactory {
public static class SearchStreetByCityAPI extends SearchBaseAPI {
private static int LIMIT = 10000;
@Override
public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) throws IOException {
@ -604,7 +626,7 @@ public class SearchCoreFactory {
res.preferredZoom = 17;
res.file = sw.getResult().file;
res.location = object.getLocation();
res.priority = 1;
res.priority = SEARCH_STREET_BY_CITY_PRIORITY;
//res.priorityDistance = 1;
res.objectType = ObjectType.STREET;
if (limit++ > LIMIT) {
@ -621,7 +643,7 @@ public class SearchCoreFactory {
@Override
public int getSearchPriority(SearchPhrase p) {
if(isLastWordCityGroup(p)) {
return 1;
return SEARCH_STREET_BY_CITY_PRIORITY;
}
return -1;
}
@ -639,12 +661,12 @@ public class SearchCoreFactory {
@Override
public boolean search(SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException {
Street s = null;
int priority = 1;
int priority = SEARCH_BUILDING_BY_STREET_PRIORITY;
if(phrase.isLastWord(ObjectType.STREET)) {
s = (Street) phrase.getLastSelectedWord().getResult().object;
}
if(isLastWordCityGroup(phrase)) {
priority = 3;
priority = SEARCH_BUILDING_BY_CITY_PRIORITY;
Object o = phrase.getLastSelectedWord().getResult().object;
if(o instanceof City) {
List<Street> streets = ((City) o).getStreets();
@ -745,19 +767,21 @@ public class SearchCoreFactory {
@Override
public int getSearchPriority(SearchPhrase p) {
if(isLastWordCityGroup(p)) {
return 10;
return SEARCH_BUILDING_BY_CITY_PRIORITY;
}
if(!p.isLastWord(ObjectType.STREET)) {
return -1;
}
return 1;
return SEARCH_BUILDING_BY_STREET_PRIORITY;
}
}
public static class SearchLocationAndUrlAPI extends SearchBaseAPI {
// newFormat = PointDescription.FORMAT_DEGREES;
// newFormat = PointDescription.FORMAT_MINUTES;
// newFormat = PointDescription.FORMAT_SECONDS;
@ -777,8 +801,10 @@ public class SearchCoreFactory {
if(phrase.getLastWord().length() == 0) {
return false;
}
parseLocation(phrase, resultMatcher);
parseUrl(phrase, resultMatcher);
boolean parseUrl = parseUrl(phrase, resultMatcher);
if(!parseUrl) {
parseLocation2(phrase, resultMatcher);
}
return super.search(phrase, resultMatcher);
}
private boolean isKindOfNumber(String s) {
@ -793,6 +819,277 @@ public class SearchCoreFactory {
return true;
}
LatLon parseLocation(String s) {
s = s.trim();
if(s.length() == 0 || !(s.charAt(0) == '-' || Character.isDigit(s.charAt(0))
|| s.charAt(0) == 'S' || s.charAt(0) == 's'
|| s.charAt(0) == 'N' || s.charAt(0) == 'n'
|| s.contains("://"))) {
return null;
}
List<Double> d = new ArrayList<>();
List<Object> all = new ArrayList<>();
List<String> strings = new ArrayList<>();
splitObjects(s, d, all, strings);
if(d.size() == 0) {
return null;
}
// detect UTM
if (all.size() == 4 && d.size() == 3 && all.get(1) instanceof String) {
char ch = all.get(1).toString().charAt(0);
if (Character.isLetter(ch)) {
UTMPoint upoint = new UTMPoint(d.get(2), d.get(1), d.get(0).intValue(), ch);
LatLonPoint ll = upoint.toLatLonPoint();
return new LatLon(ll.getLatitude(), ll.getLongitude());
}
}
if (all.size() == 3 && d.size() == 2 && all.get(1) instanceof String) {
char ch = all.get(1).toString().charAt(0);
String combined = strings.get(2);
if (Character.isLetter(ch)) {
try {
String east = combined.substring(0, combined.length() / 2);
String north = combined.substring(combined.length() / 2, combined.length());
UTMPoint upoint = new UTMPoint(Double.parseDouble(north), Double.parseDouble(east), d.get(0)
.intValue(), ch);
LatLonPoint ll = upoint.toLatLonPoint();
return new LatLon(ll.getLatitude(), ll.getLongitude());
} catch (NumberFormatException e) {
}
}
}
// try to find split lat/lon position
int jointNumbers = 0;
int lastJoin = 0;
int degSplit = -1;
int degType = -1; // 0 - degree, 1 - minutes, 2 - seconds
int northSplit = -1;
int eastSplit = -1;
for(int i = 1; i < all.size(); i++ ) {
if(all.get(i - 1) instanceof Double && all.get(i) instanceof Double) {
jointNumbers ++;
lastJoin = i;
}
if(all.get(i).equals("n") || all.get(i).equals("s") ||
all.get(i).equals("N") || all.get(i).equals("S")) {
northSplit = i + 1;
}
if(all.get(i).equals("e") || all.get(i).equals("w") ||
all.get(i).equals("E") || all.get(i).equals("W")) {
eastSplit = i;
}
int dg = -1;
if (all.get(i).equals("°")) {
dg = 0;
} else if (all.get(i).equals("\'") || all.get(i).equals("")) {
dg = 1;
} else if (all.get(i).equals("") || all.get(i).equals("\"")) {
dg = 2;
}
if (dg != -1 && degType != -2) {
if (degType < dg) {
degSplit = i + 1;
} else {
degType = -2; // finish search
}
}
}
int split = -1;
if(jointNumbers == 1) {
split = lastJoin;
}
if(northSplit != -1 && northSplit < all.size() -1) {
split = northSplit;
}
if(eastSplit != -1 && eastSplit < all.size() -1) {
split = eastSplit;
}
if(degSplit != -1 && degSplit < all.size() -1) {
split = degSplit;
}
if(split != -1) {
double lat = parse1Coordinate(all, 0, split);
double lon = parse1Coordinate(all, split, all.size());
return new LatLon(lat, lon);
}
if(d.size() == 2) {
return new LatLon(d.get(0), d.get(1));
}
// simple url case
if (s.contains("://")) {
double lat = 0;
double lon = 0;
boolean only2decimals = true;
for (int i = 0; i < d.size(); i++) {
if (d.get(i).doubleValue() != d.get(i).intValue()) {
if (lat == 0) {
lat = d.get(i);
} else if (lon == 0) {
lon = d.get(i);
} else {
only2decimals = false;
}
}
}
if (lat != 0 && lon != 0 && only2decimals) {
return new LatLon(lat, lon);
}
}
// split by equal number of digits
if (d.size() > 2 && d.size() % 2 == 0) {
int ind = d.size() / 2 + 1;
int splitEq = -1;
for (int i = 0; i < all.size(); i++) {
if(all.get(i) instanceof Double) {
ind --;
}
if(ind == 0) {
splitEq = i;
break;
}
}
if (splitEq != -1) {
double lat = parse1Coordinate(all, 0, splitEq);
double lon = parse1Coordinate(all, splitEq, all.size());
return new LatLon(lat, lon);
}
}
return null;
}
public double parse1Coordinate(List<Object> all, int begin, int end) {
boolean neg = false;
double d = 0;
int type = 0; // degree - 0, minutes - 1, seconds = 2
Double prevDouble = null;
for(int i = begin; i <= end; i++) {
Object o = i == end ? "" : all.get(i);
if(o.equals("S") || o.equals("W")) {
neg = !neg;
}
if (prevDouble != null) {
if(o.equals("°")) {
type = 0;
} else if(o.equals("") /*o.equals("'")*/) {
// ' can be used as delimeter ignore it
type = 1;
} else if(o.equals("\"") || o.equals("")) {
type = 2;
}
if (type == 0) {
double ld = prevDouble.doubleValue();
if(ld < 0) {
ld = -ld;
neg = true;
}
d += ld;
} else if (type == 1) {
d += prevDouble.doubleValue() / 60.f;
} else /*if (type == 1) */ {
d += prevDouble.doubleValue() / 3600.f;
}
type++;
}
if(o instanceof Double) {
prevDouble = (Double) o;
} else {
prevDouble = null;
}
}
if(neg) {
d = -d;
}
return d;
}
private void splitObjects(String s, List<Double> d, List<Object> all, List<String> strings) {
boolean digit = false;
int word = -1;
for(int i = 0; i <= s.length(); i++) {
char ch = i == s.length() ? ' ' : s.charAt(i);
boolean dg = Character.isDigit(ch);
boolean nonwh = ch != ',' && ch != ' ' && ch != ';';
if (ch == '.' || dg || ch == '-' ) {
if(!digit) {
if(word != -1) {
all.add(s.substring(word, i));
strings.add(s.substring(word, i));
}
digit = true;
word = i;
} else {
if(word == -1) {
word = i;
}
// if digit
// continue
}
} else {
if(digit){
try {
double dl = Double.parseDouble(s.substring(word, i));
d.add(dl);
all.add(dl);
strings.add(s.substring(word, i));
digit = false;
word = -1;
} catch (NumberFormatException e) {
}
}
if(nonwh) {
if(!Character.isLetter(ch)) {
if(word != -1) {
all.add(s.substring(word, i));
strings.add(s.substring(word, i));
}
all.add(s.substring(i, i + 1));
strings.add(s.substring(i, i +1));
word = -1;
} else if(word == -1) {
word = i;
}
} else {
if(word != -1) {
all.add(s.substring(word, i));
strings.add(s.substring(word, i));
}
word = -1;
}
}
}
}
private void parseLocation2(SearchPhrase phrase, SearchResultMatcher resultMatcher) {
String lw = phrase.getLastWord();
SearchWord sw = phrase.getLastSelectedWord();
LatLon l = null;
if (sw != null && sw.getType() == ObjectType.UNKNOWN_NAME_FILTER) {
l = parseLocation(sw.getWord() + ", " + lw);
}
if (l == null) {
l = parseLocation(lw);
}
if (l != null) {
SearchResult sp = new SearchResult(phrase);
sp.priority = SEARCH_LOCATION_PRIORITY;
sp.object = sp.location = l;
sp.localeName = ((float) sp.location.getLatitude()) + ", " + ((float) sp.location.getLongitude());
sp.objectType = ObjectType.LOCATION;
sp.wordsSpan = 2;
resultMatcher.publish(sp);
} else if (phrase.isNoSelectedType()) {
// SearchResult sp = new SearchResult(phrase);
// sp.priority = SEARCH_LOCATION_PRIORITY;
// 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 parseLocation(SearchPhrase phrase, SearchResultMatcher resultMatcher) {
String lw = phrase.getLastWord();
SearchWord sw = phrase.getLastSelectedWord();
@ -806,7 +1103,7 @@ public class SearchCoreFactory {
}
if(!Double.isNaN(pd)) {
SearchResult sp = new SearchResult(phrase);
sp.priority = 0;
sp.priority = SEARCH_LOCATION_PRIORITY;
sp.object = sp.location = new LatLon(pd, dd);
sp.localeName = ((float)sp.location.getLatitude()) +", " + ((float) sp.location.getLongitude());
sp.objectType = ObjectType.LOCATION;
@ -814,7 +1111,7 @@ public class SearchCoreFactory {
resultMatcher.publish(sp);
} else if (phrase.isNoSelectedType()) {
SearchResult sp = new SearchResult(phrase);
sp.priority = 0;
sp.priority = SEARCH_LOCATION_PRIORITY;
sp.object = sp.location = new LatLon(dd, 0);
sp.localeName = ((float) sp.location.getLatitude()) + ", <input> ";
sp.objectType = ObjectType.PARTIAL_LOCATION;
@ -824,7 +1121,7 @@ public class SearchCoreFactory {
}
private void parseUrl(SearchPhrase phrase, SearchResultMatcher resultMatcher) {
private boolean parseUrl(SearchPhrase phrase, SearchResultMatcher resultMatcher) {
String text = phrase.getLastWord();
GeoParsedPoint pnt = GeoPointParserUtil.parse(text);
int wordsSpan= 1;
@ -853,24 +1150,15 @@ public class SearchCoreFactory {
}
sp.objectType = ObjectType.LOCATION;
resultMatcher.publish(sp);
return true;
}
return false;
}
@Override
public int getSearchPriority(SearchPhrase p) {
return 1;
return SEARCH_LOCATION_PRIORITY;
}
}
public static void main(String[] args) throws IOException {
testSearchLocationAndUrlAPI(new SearchLocationAndUrlAPI());
}
private static void testSearchLocationAndUrlAPI(SearchLocationAndUrlAPI api) throws IOException {
SearchResultMatcher srm = new SearchResultMatcher(null, 0, null, 10);
api.search(new SearchPhrase(null).generateNewPhrase("17R 419230 2714967", null), srm);
System.out.println(srm.getRequestResults());
}
}

View file

@ -383,6 +383,6 @@ public class SearchPhrase {
}
public int getRadiusSearch(int meters) {
return getRadiusLevel() * meters;
return (1 << (getRadiusLevel() - 1)) * meters;
}
}

View file

@ -17,8 +17,8 @@ public class LocationSearchTest {
SearchResultMatcher srm = new SearchUICore.SearchResultMatcher(null, 0, null, 100);
new SearchCoreFactory.SearchLocationAndUrlAPI().
search(new SearchPhrase(null).generateNewPhrase(string, null), srm);
Assert.assertEquals(srm.getRequestResults().size(), 1);
Assert.assertEquals(srm.getRequestResults().get(0).location, latLon);
Assert.assertEquals(1, srm.getRequestResults().size());
Assert.assertEquals(latLon, srm.getRequestResults().get(0).location);
}
@Test
@ -33,33 +33,51 @@ public class LocationSearchTest {
search("5.445,3.523", new LatLon(5.445, 3.523));
search("5:1:1,3:1", new LatLon(5 + 1/60f + 1/3600f, 3 + 1/60f));
}
@Test
public void testUTMSearch() throws IOException {
search("17N6734294749123", new LatLon(42.875017, -78.87659050764749));
search("17 N 673429 4749123", new LatLon(42.875017, -78.87659050764749));
}
@Test
public void testBasicSpaceSearch() throws IOException {
search("5.0 3.0", new LatLon(5, 3));
search("-5.0 -3.0", new LatLon(-5, -3));
search("-45.5 3.0S", new LatLon(-45.5, -3));
search("45.5S 3.0 W", new LatLon(-45.5, -3));
search("5.445 3.523", new LatLon(5.445, 3.523));
search("5:1:1 3:1", new LatLon(5 + 1/60f + 1/3600f, 3 + 1/60f));
search("5:1#1 3#1", new LatLon(5 + 1/60f + 1/3600f, 3 + 1/60f));
search("5'1'1 3'1", new LatLon(5 + 1/60f + 1/3600f, 3 + 1/60f));
search("5#1#1 3#1", new LatLon(5 + 1/60f + 1/3600f, 3 + 1/60f));
}
// TODO 17R 419230 2714967
// 17N6734294749123
// 45° 30'30"W
// -45
// 45 W
// 45 S
// 45.50W
// 45.50S
// W45
// S45
// 45 30.5W
// 44 30.5S
// 45 30 30 W
// 45 30 30 N
// -45 30 30
// 45 30 30
// 45 30.50W
// 45 30.50
@Test
public void testSimpleURLSearch() throws IOException {
search("ftp://simpleurl?lat=34.23&lon=-53.2&z=15", new LatLon(34.23, -53.2));
search("ftp://simpleurl?z=15&lat=34.23&lon=-53.2", new LatLon(34.23, -53.2));
}
@Test
public void testAdvancedSpaceSearch() throws IOException {
search("5 30 30 N 4 30 W", new LatLon(5.5 + 30 / 3600f, -4.5));
search("5 30 -4 30", new LatLon(5.5, -4.5));
search("S 5 30 4 30 W", new LatLon(-5.5, -4.5));
search("S5.4232 4.30W", new LatLon(-5.4232, -4.3));
search("S5.4232 W4.30", new LatLon(-5.4232, -4.3));
search("5.4232, W4.30", new LatLon(5.4232, -4.3));
search("5.4232N, 45 30.5W", new LatLon(5.4232, -(45 + 30.5 / 60f)));
}
@Test
public void testArcgisSpaceSearch() throws IOException {
search("43°3833.24″N 79°2313.7″W", new LatLon(43 + 38/60f + 33.24/3600f,-(79 + 23/60f + 13.7/3600f)));
search("45° 30'30\"W 3.0", new LatLon(45 + 0.5 + 1 / 120f, -3));
search("43°S 79°2313.7″W", new LatLon(-43,-(79 + 23/60f + 13.7/3600f)));
search("43° 79°2313.7″E", new LatLon(43,79 + 23/60f + 13.7/3600f));
search("43°38 79°2313.7″E", new LatLon(43 + 38/60f,79 + 23/60f + 13.7/3600f));
}