Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ask "Is this inside a building" #5278

Merged
merged 5 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package de.westnordost.streetcomplete.data.user.achievements
import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.overlays.street_parking.StreetParkingOverlay
import de.westnordost.streetcomplete.quests.amenity_cover.AddAmenityCover
import de.westnordost.streetcomplete.quests.amenity_indoor.AddIsAmenityIndoor
import de.westnordost.streetcomplete.quests.cycleway.AddCycleway
import de.westnordost.streetcomplete.quests.foot.AddProhibitedForPedestrians
import de.westnordost.streetcomplete.quests.oneway.AddOneway
Expand Down Expand Up @@ -55,7 +56,8 @@ private val typeAliases = listOf(
"WayLitOverlay" to AddWayLit::class.simpleName!!,
"SidewalkOverlay" to AddSidewalk::class.simpleName!!,
"CyclewayOverlay" to AddCycleway::class.simpleName!!,
"AddStreetParking" to StreetParkingOverlay::class.simpleName!!
"AddStreetParking" to StreetParkingOverlay::class.simpleName!!,
"AddIsDefibrillatorIndoor" to AddIsAmenityIndoor::class.simpleName!!
)

private val links = listOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ import de.westnordost.streetcomplete.quests.crossing_island.AddCrossingIsland
import de.westnordost.streetcomplete.quests.crossing_kerb_height.AddCrossingKerbHeight
import de.westnordost.streetcomplete.quests.crossing_type.AddCrossingType
import de.westnordost.streetcomplete.quests.cycleway.AddCycleway
import de.westnordost.streetcomplete.quests.defibrillator.AddIsDefibrillatorIndoor
import de.westnordost.streetcomplete.quests.amenity_indoor.AddIsAmenityIndoor
import de.westnordost.streetcomplete.quests.diet_type.AddHalal
import de.westnordost.streetcomplete.quests.diet_type.AddKosher
import de.westnordost.streetcomplete.quests.diet_type.AddVegan
Expand Down Expand Up @@ -438,7 +438,7 @@ fun questTypeRegistry(

112 to AddWheelchairAccessPublicTransport(), // need to look out for lifts etc, maybe even enter the station

113 to AddIsDefibrillatorIndoor(), // need to go inside in case it is inside (or gone)
113 to AddIsAmenityIndoor(getFeature), // need to go inside in case it is inside (or gone)

// inside camping sites
114 to AddCampType(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package de.westnordost.streetcomplete.quests.amenity_indoor

import de.westnordost.osmfeatures.Feature
import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.data.elementfilter.toElementFilterExpression
import de.westnordost.streetcomplete.data.osm.geometry.ElementGeometry
import de.westnordost.streetcomplete.data.osm.geometry.ElementPolygonsGeometry
import de.westnordost.streetcomplete.data.osm.mapdata.Element
import de.westnordost.streetcomplete.data.osm.mapdata.MapDataWithGeometry
import de.westnordost.streetcomplete.data.osm.osmquests.OsmElementQuestType
import de.westnordost.streetcomplete.data.user.achievements.EditTypeAchievement.*
import de.westnordost.streetcomplete.osm.Tags
import de.westnordost.streetcomplete.quests.YesNoQuestForm
import de.westnordost.streetcomplete.util.ktx.toYesNo
import de.westnordost.streetcomplete.util.math.LatLonRaster
import de.westnordost.streetcomplete.util.math.contains
import de.westnordost.streetcomplete.util.math.isCompletelyInside
import de.westnordost.streetcomplete.util.math.isInMultipolygon
import java.util.concurrent.FutureTask

class AddIsAmenityIndoor(private val getFeature: (tags: Map<String, String>) -> Feature?) :
OsmElementQuestType<Boolean> {

private val nodesFilter by lazy { """
nodes with
(
emergency ~ defibrillator|fire_extinguisher
or amenity ~ atm|telephone|parcel_locker|luggage_locker|locker|clock|post_box|public_bookcase|give_box|ticket_validator|vending_machine
)
and access !~ private|no
and !indoor and !location and !level and !level:ref
""".toElementFilterExpression() }

/* We only want survey nodes within building outlines. */
private val buildingFilter by lazy { """
ways, relations with building
""".toElementFilterExpression() }

override val changesetComment = "Determine whether amenities are inside buildings"
override val wikiLink = "Key:indoor"

override val icon = R.drawable.ic_quest_building_inside
override val achievements = listOf(CITIZEN)

override fun getTitle(tags: Map<String, String>) = R.string.quest_is_amenity_inside_title

override fun getApplicableElements(mapData: MapDataWithGeometry): Iterable<Element> {
val bbox = mapData.boundingBox ?: return listOf()
val nodes = mapData.nodes.filter {
nodesFilter.matches(it) && hasAnyName(it.tags)
}
val buildings = mapData.filter { buildingFilter.matches(it) }.toMutableList()

val buildingGeometriesById = buildings.associate {
it.id to mapData.getGeometry(it.type, it.id) as? ElementPolygonsGeometry
}

val nodesPositions = LatLonRaster(bbox, 0.0005)
for (node in nodes) {
nodesPositions.insert(node.position)
}

buildings.removeAll { building ->
val buildingBounds = buildingGeometriesById[building.id]?.getBounds()
(buildingBounds == null || !buildingBounds.isCompletelyInside(bbox) || nodesPositions.getAll(buildingBounds).count() == 0)
}

//Reduce all matching nodes to nodes within building outlines
val nodesInBuildings = nodes.filter {
buildings.any { building ->
val buildingGeometry = buildingGeometriesById[building.id]

if (buildingGeometry != null && buildingGeometry.getBounds().contains(it.position) )
it.position.isInMultipolygon(buildingGeometry.polygons)
else
false
}

}

return nodesInBuildings
}

override fun isApplicableTo(element: Element) =
if (!nodesFilter.matches(element) || !hasAnyName(element.tags)) false else null

private fun hasAnyName(tags: Map<String, String>) = getFeature(tags) != null

override fun getHighlightedElements(element: Element, getMapData: () -> MapDataWithGeometry): Sequence<Element> {
/* put markers for objects that are exactly the same as for which this quest is asking for
e.g. it's a ticket validator? -> display other ticket validators. Etc. */
val feature = getFeature(element.tags) ?: return emptySequence()

return getMapData().filter { it.tags.containsAll(feature.tags) }.asSequence()
}

override fun createForm() = YesNoQuestForm()

override fun applyAnswerTo(answer: Boolean, tags: Tags, geometry: ElementGeometry, timestampEdited: Long) {
tags["indoor"] = answer.toYesNo()
}
}

private fun <X, Y> Map<X, Y>.containsAll(other: Map<X, Y>) = other.all { this[it.key] == it.value }


This file was deleted.

2 changes: 1 addition & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1053,7 +1053,7 @@ Before uploading your changes, the app checks with a &lt;a href=\"https://www.we
<string name="quest_internet_access_terminal">Computer with Internet access</string>
<string name="quest_internet_access_no">No connection</string>

<string name="quest_is_defibrillator_inside_title">Is this defibrillator (AED) inside a building?</string>
<string name="quest_is_amenity_inside_title">Is this inside a building?</string>

<string name="quest_kerb_height_title">What’s the height of this curb?</string>
<string name="quest_kerb_height_flush">Same level as road surface</string>
Expand Down