'Osmdroid loading only Elements inside of a Polygon with OverpassAPIProvider
So I am trying the following: Using the Overpass API in osmdroid to only load those nodes, ways or relations that are within or partially within a Polygon.
Tweaking the OverpassAPIProvider class from osmbonuspack I've managed to make it work with nodes (points), but for some reason it does not work for ways (areas/linestrings).
Here is my code:
package com.example.osmtestproject;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import android.view.Display;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import com.snatik.polygon.Point;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.osmdroid.bonuspack.kml.KmlFeature;
import org.osmdroid.bonuspack.kml.KmlFolder;
import org.osmdroid.bonuspack.kml.KmlGeometry;
import org.osmdroid.bonuspack.kml.KmlLineString;
import org.osmdroid.bonuspack.kml.KmlMultiGeometry;
import org.osmdroid.bonuspack.kml.KmlPlacemark;
import org.osmdroid.bonuspack.kml.KmlPoint;
import org.osmdroid.bonuspack.kml.KmlPolygon;
import org.osmdroid.bonuspack.location.POI;
import org.osmdroid.bonuspack.utils.BonusPackHelper;
import org.osmdroid.util.BoundingBox;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapView;
import org.osmdroid.views.overlay.Marker;
import org.osmdroid.views.overlay.Polygon;
import org.osmdroid.views.overlay.Polyline;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public class OverpassProvider {
ArrayList<KmlPlacemark> myKmlPlacemarks = new ArrayList<KmlPlacemark>();
public OverpassProvider(Context context) {
this.context = context;
}
public String buildUrl(BoundingBox bb, String tag, int limit) {
StringBuilder stringBuilder = new StringBuilder();
String mService = "https://overpass-api.de/api/interpreter?data=[out:json][timeout:100];(";
stringBuilder.append(mService);
String sBB = "("+bb.getLatSouth()+","+bb.getLonWest()+","+bb.getLatNorth()+","+bb.getLonEast()+")";
String data =
"node[" + tag + "]" + sBB + ";" +
"way[" + tag + "]" + sBB + ";" +
");out%20qt%20center%20" + limit + "%20tags;";
stringBuilder.append(data);
return stringBuilder.toString();
}
protected GeoPoint geoPointFromJson(JsonObject jLatLon){
double lat = jLatLon.get("lat").getAsDouble();
double lon = jLatLon.get("lon").getAsDouble();
return new GeoPoint(lat, lon);
}
protected String tagValueFromJson(String key, JsonObject jTags){
JsonElement jTag = jTags.get(key);
if (jTag == null)
return null;
else
return jTag.getAsString();
}
protected String tagValueFromJsonNotNull(String key, JsonObject jTags){
String v = tagValueFromJson(key, jTags);
return (v != null ? ","+v : "");
}
public ArrayList<POI> getPOIsFromUrl(String overpassUrl) {
getReturnAsString(overpassUrl);
if (jsonString == null) {
Log.e(BonusPackHelper.LOG_TAG, "OverpassAPIProvider: request failed.");
return null;
}
try {
//parse JSON and build POIs
JsonParser parser = new JsonParser();
JsonElement json = parser.parse(jsonString);
JsonObject jResult = json.getAsJsonObject();
JsonArray jElements = jResult.get("elements").getAsJsonArray();
ArrayList<POI> pois = new ArrayList<POI>(jElements.size());
for (JsonElement j:jElements){
JsonObject jo = j.getAsJsonObject();
POI poi = new POI(POI.POI_SERVICE_OVERPASS_API);
poi.mId = jo.get("id").getAsLong();
poi.mCategory = jo.get("type").getAsString();
if (jo.has("tags")){
JsonObject jTags = jo.get("tags").getAsJsonObject();
poi.mType = tagValueFromJson("name", jTags);
//Try to set a relevant POI type by searching for an OSM commonly used tag key, and getting its value:
poi.mDescription = tagValueFromJsonNotNull("amenity", jTags)
+ tagValueFromJsonNotNull("boundary", jTags)
+ tagValueFromJsonNotNull("building", jTags)
+ tagValueFromJsonNotNull("craft", jTags)
+ tagValueFromJsonNotNull("emergency", jTags)
+ tagValueFromJsonNotNull("highway", jTags)
+ tagValueFromJsonNotNull("historic", jTags)
+ tagValueFromJsonNotNull("landuse", jTags)
+ tagValueFromJsonNotNull("leisure", jTags)
+ tagValueFromJsonNotNull("natural", jTags)
+ tagValueFromJsonNotNull("shop", jTags)
+ tagValueFromJsonNotNull("sport", jTags)
+ tagValueFromJsonNotNull("tourism", jTags);
if (poi.mDescription.length()>0)
poi.mDescription = poi.mDescription.substring(1);
poi.mUrl = tagValueFromJson("website", jTags);
if (poi.mUrl != null){
if (!poi.mUrl.startsWith("http://") && !poi.mUrl.startsWith("https://"))
poi.mUrl = "http://" + poi.mUrl;
}
}
if ("node".equals(poi.mCategory)){
poi.mLocation = geoPointFromJson(jo);
} else {
if (jo.has("center")){
JsonObject jCenter = jo.get("center").getAsJsonObject();
poi.mLocation = geoPointFromJson(jCenter);
}
}
if (poi.mLocation != null)
pois.add(poi);
}
return pois;
} catch (JsonSyntaxException e) {
Log.e(BonusPackHelper.LOG_TAG, "OverpassAPIProvider: parsing error.");
return null;
}
}
public String buildUrlForTagSearchKml(String tag, BoundingBox bb, int limit, int timeout){
StringBuilder stringBuilder = new StringBuilder();
String mService = "https://overpass-api.de/api/interpreter?data=[out:json][timeout:100];(";
stringBuilder.append(mService);
String sBB = "("+bb.getLatSouth()+","+bb.getLonWest()+","+bb.getLatNorth()+","+bb.getLonEast()+")";
String data =
"node[" + tag + "]" + sBB + ";" +
"way[" + tag + "]" + sBB + ";" +
");out%20qt%20geom%20tags%20" + limit + ";" +
"relation[" + tag + "]" + sBB + ";out%20qt%20geom%20body%20" + limit + ";";
stringBuilder.append(data);
return stringBuilder.toString();
}
protected boolean isAnArea(ArrayList<GeoPoint> coords){
return (coords!=null) && (coords.size()>=3) && (coords.get(0).equals(coords.get(coords.size()-1)));
}
protected ArrayList<GeoPoint> parseGeometry(JsonObject jo){
JsonArray jGeometry = jo.get("geometry").getAsJsonArray();
ArrayList<GeoPoint> coords = new ArrayList<GeoPoint>(jGeometry.size());
for (JsonElement j:jGeometry){
JsonObject jLatLon = j.getAsJsonObject();
GeoPoint p = geoPointFromJson(jLatLon);
coords.add(p);
}
return coords;
}
protected KmlMultiGeometry buildMultiGeometry(JsonArray jMembers, Polygon isoPolygon){
KmlMultiGeometry geometry = new KmlMultiGeometry();
for (JsonElement j:jMembers){
JsonObject jMember = j.getAsJsonObject();
KmlGeometry item = buildGeometry(jMember, isoPolygon);
geometry.addItem(item);
}
return geometry;
}
protected KmlGeometry buildGeometry(JsonObject jo, Polygon isoPolygon){
KmlGeometry geometry; //Kml Geometrie erstellen
String type = jo.get("type").getAsString();
if ("node".equals(type) && isPointInCheckPolygon(isoPolygon, geoPointFromJson(jo))){
geometry = new KmlPoint(geoPointFromJson(jo));
} else if ("way".equals(type)){
ArrayList<GeoPoint> coords = parseGeometry(jo);
if (isAnArea(coords) && isMultiPointInCheckPolygon(isoPolygon, coords)){
geometry = new KmlPolygon();
geometry.mCoordinates = coords;
} else if (!isAnArea(coords) && isMultiPointInCheckPolygon(isoPolygon, coords)){
geometry = new KmlLineString();
geometry.mCoordinates = coords;
}
else {
geometry = new KmlLineString();
geometry.mCoordinates = coords;
}
} else { //relation:
//JsonArray jMembers = jo.get("members").getAsJsonArray();
//geometry = buildMultiGeometry(jMembers, isoPolygon);
return null;
}
return geometry;
}
public boolean addInKmlFolder(KmlFolder kmlFolder, String overpassUrl, Polygon isoPolygon){
String jsonString = getReturnAsString(overpassUrl);
if (jsonString == null) {
Log.e(BonusPackHelper.LOG_TAG, "OverpassAPIProvider: request failed.");
return false;
}
try {
//parse JSON and build KML
JsonParser parser = new JsonParser();
JsonElement json = parser.parse(jsonString);
JsonObject jResult = json.getAsJsonObject();
Log.d("Test", json.toString());
JsonArray jElements = jResult.get("elements").getAsJsonArray();
for (JsonElement j:jElements){
JsonObject jo = j.getAsJsonObject();
KmlPlacemark placemark = new KmlPlacemark();
placemark.mGeometry = buildGeometry(jo, isoPolygon);
placemark.mId = jo.get("id").getAsString();
//Tags:
if (jo.has("tags")){
JsonObject jTags = jo.get("tags").getAsJsonObject();
if (jTags.has("name"))
placemark.mName = jTags.get("name").getAsString();
//copy all tags as KML Extended Data:
Set<Map.Entry<String,JsonElement>> entrySet = jTags.entrySet();
for (Map.Entry<String,JsonElement> entry:entrySet){
String key = entry.getKey();
String value = entry.getValue().getAsString();
placemark.setExtendedData(key, value);
}
}
myKmlPlacemarks.add(placemark);
kmlFolder.add(placemark);
}
return true;
} catch (JsonSyntaxException e) {
Log.e(BonusPackHelper.LOG_TAG, "OverpassAPIProvider: parsing error.");
return false;
}
}
protected boolean isMultiPointInCheckPolygon(Polygon isoPolygon, ArrayList<GeoPoint> checkPoints) {
ArrayList<Boolean> containsAPoint = new ArrayList<Boolean>();
com.snatik.polygon.Polygon checkPolygon = checkPolygon(isoPolygon);
for (int i = 0; i < checkPoints.size(); i++) {
Point point = new Point(checkPoints.get(i).getLatitude(), checkPoints.get(i).getLongitude());
boolean contains = checkPolygon.contains(point);
containsAPoint.add(contains);
}
for(boolean value: containsAPoint){
if(value){ return true;}
}
return false;
}
protected boolean isPointInCheckPolygon(Polygon isoPolygon, GeoPoint checkPoint) {
com.snatik.polygon.Polygon checkPolygon = checkPolygon(isoPolygon);
Point point = new Point(checkPoint.getLatitude(), checkPoint.getLongitude());
return checkPolygon.contains(point);
}
protected com.snatik.polygon.Polygon checkPolygon(Polygon isoPolygon) {
List<GeoPoint> polygonPoints = isoPolygon.getActualPoints();
com.snatik.polygon.Polygon.Builder builder = new com.snatik.polygon.Polygon.Builder();
for (int i = 0; i < polygonPoints.size(); i++) {
Point point = new Point(polygonPoints.get(i).getLatitude(), polygonPoints.get(i).getLongitude());
builder.addVertex(point);
}
return builder.build();
}
protected String getReturnAsString(String overpassUrl) {
String result = new String();
try {
HttpURLConnection connection = (HttpURLConnection) new URL(overpassUrl).openConnection();
InputStream inputStream = connection.getInputStream();
Scanner s = new Scanner(inputStream).useDelimiter("\\A");
result = s.hasNext() ? s.next() : "";
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
}
I've yet to try and implement a way to check if a relation is inside of a polygon.
My idea to check if a way is, was to get all individual Points of the geometry, check each of the points individually and return true if at least one point is inside the polygon but as I mentioned, that didn't work.
How could I change and improve the code so it works?
Solution 1:[1]
Ah, I found the mistake myself.
In this method:
protected KmlGeometry buildGeometry(JsonObject jo, Polygon isoPolygon){
KmlGeometry geometry; //Kml Geometrie erstellen
String type = jo.get("type").getAsString();
if ("node".equals(type) && isPointInCheckPolygon(isoPolygon, geoPointFromJson(jo))){
geometry = new KmlPoint(geoPointFromJson(jo));
} else if ("way".equals(type)){
ArrayList<GeoPoint> coords = parseGeometry(jo);
if (isAnArea(coords) && isMultiPointInCheckPolygon(isoPolygon, coords)){
geometry = new KmlPolygon();
geometry.mCoordinates = coords;
} else if (!isAnArea(coords) && isMultiPointInCheckPolygon(isoPolygon, coords)){
geometry = new KmlLineString();
geometry.mCoordinates = coords;
}
else {
return null;
}
} else { //relation:
//JsonArray jMembers = jo.get("members").getAsJsonArray();
//geometry = buildMultiGeometry(jMembers, isoPolygon);
return null;
}
return geometry;
}
it still builds a geometry even if the area is not within the polygon.
To fix it I simply return null in the else statement instead.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | account28100210 |
