package de.westnordost.streetcomplete.quests.housenumber; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import de.westnordost.osmapi.map.data.BoundingBox; import de.westnordost.osmapi.map.data.Element; import de.westnordost.streetcomplete.data.QuestImportance; import de.westnordost.streetcomplete.data.osm.ElementGeometry; import de.westnordost.streetcomplete.data.osm.OsmElementQuestType; import de.westnordost.streetcomplete.data.osm.changes.StringMapChangesBuilder; import de.westnordost.streetcomplete.data.osm.download.MapDataWithGeometryHandler; import de.westnordost.streetcomplete.data.osm.download.OverpassMapDataDao; import de.westnordost.streetcomplete.data.osm.tql.FiltersParser; import de.westnordost.streetcomplete.data.osm.tql.TagFilterExpression; import de.westnordost.streetcomplete.quests.AbstractQuestAnswerFragment; import de.westnordost.streetcomplete.util.JTSConst; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.operation.valid.IsValidOp; public class AddHousenumber implements OsmElementQuestType { private static final TagFilterExpression HOUSES_WITHOUT_HOUSENUMBERS = new FiltersParser().parse( " ways, relations with " + " building ~ house|residential|apartments|detached|terrace|hotel|dormitory|houseboat|" + "school|civic|college|university|public|hospital|kindergarten|train_station|" + "retail|commercial" + " and !addr:housenumber"); private static final TagFilterExpression NODES_WITH_HOUSENUMBERS = new FiltersParser().parse( " nodes with addr:housenumber"); private final OverpassMapDataDao overpassServer; @Inject public AddHousenumber(OverpassMapDataDao overpassServer) { this.overpassServer = overpassServer; } @Override public boolean download(BoundingBox bbox, final MapDataWithGeometryHandler handler) { boolean success = true; final List<Point> housenumberCoords = new ArrayList<>(); String nodesWithHousenumbersQuery = NODES_WITH_HOUSENUMBERS.toOverpassQLString(bbox); success = overpassServer.getAndHandleQuota(nodesWithHousenumbersQuery, new MapDataWithGeometryHandler() { @Override public void handle(@NonNull Element element, @Nullable ElementGeometry geometry) { if(geometry != null) { housenumberCoords.add(JTSConst.toPoint(geometry.center)); } } }); if(!success) return false; String buildingsWithoutHousenumbersQuery = HOUSES_WITHOUT_HOUSENUMBERS.toOverpassQLString(bbox); success = overpassServer.getAndHandleQuota(buildingsWithoutHousenumbersQuery, new MapDataWithGeometryHandler() { @Override public void handle(@NonNull Element element, @Nullable ElementGeometry geometry) { // invalid geometry if(geometry == null) return; Geometry g = JTSConst.toGeometry(geometry); // invalid geometry out of other reasons? (Not sure when this can happen...) if(!g.isValid()) return; // exclude buildings with housenumber-nodes inside them for(Point p : housenumberCoords) { if(g.covers(p)) return; } handler.handle(element, geometry); } }); if(!success) return false; return true; } @Override public void applyAnswerTo(Bundle answer, StringMapChangesBuilder changes) { String housenumber = answer.getString(AddHousenumberForm.HOUSENUMBER); changes.add("addr:housenumber", housenumber); } @Override public int importance() { return QuestImportance.MAJOR; } @Override public AbstractQuestAnswerFragment createForm() { return new AddHousenumberForm(); } @Override public String getCommitMessage() { return "Add housenumbers"; } @Override public String getIconName() { return "housenumber"; } }