package net.osmand.util; import com.google.openlocationcode.OpenLocationCode; import com.jwetherell.openmap.common.LatLonPoint; import com.jwetherell.openmap.common.UTMPoint; import net.osmand.data.LatLon; import java.util.ArrayList; import java.util.List; public class LocationParser { public static LatLon parseLocation(String locPhrase, LatLon searchLocation) { locPhrase = locPhrase.trim(); // detect OLC first // avoid throwing exceptions by carefully checking exceptions if (locPhrase.length() > 0 && OpenLocationCode.isValidCode(locPhrase)) { OpenLocationCode olc = new OpenLocationCode(locPhrase); if (olc.isFull()) { OpenLocationCode.CodeArea codeArea = olc.decode(); return new LatLon(codeArea.getCenterLatitude(), codeArea.getCenterLongitude()); } else if (olc.isShort() && searchLocation != null) { OpenLocationCode.CodeArea codeArea = olc.recover(searchLocation.getLatitude(), searchLocation.getLongitude()).decode(); return new LatLon(codeArea.getCenterLatitude(), codeArea.getCenterLongitude()); } } boolean valid = isValidLocPhrase(locPhrase); if (!valid) { String[] split = locPhrase.split(" "); if (split.length == 4 && split[1].contains(".") && split[3].contains(".")) { locPhrase = split[1] + " " + split[3]; valid = isValidLocPhrase(locPhrase); } } if (!valid) { return null; } List d = new ArrayList<>(); List all = new ArrayList<>(); List strings = new ArrayList<>(); splitObjects(locPhrase, 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 validOrNull(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 validOrNull(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 boolean finishDegSplit = false; 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) { if (!finishDegSplit) { if (degType < dg) { degSplit = i + 1; degType = dg; } else { finishDegSplit = true; degType = dg; } } else { if (degType < dg) { degType = dg; } else { // reject delimiter degSplit = -1; } } } } int split = -1; if (jointNumbers == 1) { split = lastJoin; } if (northSplit != -1 && northSplit < all.size() -1) { split = northSplit; } else if (eastSplit != -1 && eastSplit < all.size() -1) { split = eastSplit; } else 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 validOrNull(new LatLon(lat, lon)); } if (d.size() == 2) { return validOrNull(new LatLon(d.get(0), d.get(1))); } // simple url case if (locPhrase.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 validOrNull(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 validOrNull(new LatLon(lat, lon)); } } return null; } private static LatLon validOrNull(LatLon latLon) { if (isValidCoordinate(latLon.getLatitude()) && isValidCoordinate(latLon.getLongitude())) { return latLon; } return null; } private static boolean isValidCoordinate(double coordinate) { double coord = Math.abs(coordinate); return 0 <= coord && coord <= 180; } private static boolean isValidLocPhrase(String locPhrase) { if (!locPhrase.isEmpty()) { char ch = Character.toLowerCase(locPhrase.charAt(0)); return ch == '-' || Character.isDigit(ch) || ch == 's' || ch == 'n' || locPhrase.contains("://"); } return false; } public static double parse1Coordinate(List 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; } public static void splitObjects(String s, List d, List all, List 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){ if (word != -1) { 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; } } } } }