Skip to content

Commit

Permalink
Merge branch 'master' into parcel_locker_brand
Browse files Browse the repository at this point in the history
  • Loading branch information
westnordost committed May 16, 2024
2 parents d9c70bd + 4d49e0a commit 23279ab
Show file tree
Hide file tree
Showing 22 changed files with 386 additions and 92 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ dependencies {
// finding in which country we are for country-specific logic
implementation("de.westnordost:countryboundaries:2.1")
// finding a name for a feature without a name tag
implementation("de.westnordost:osmfeatures-android:5.2")
implementation("de.westnordost:osmfeatures:6.0")
// talking with the OSM API
implementation("de.westnordost:osmapi-map:3.0")
implementation("de.westnordost:osmapi-changesets:3.0")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package de.westnordost.streetcomplete.util
import android.content.res.Configuration
import android.content.res.Resources
import androidx.test.platform.app.InstrumentationRegistry
import de.westnordost.osmfeatures.AndroidFeatureDictionary
import de.westnordost.osmfeatures.FeatureDictionary
import de.westnordost.osmfeatures.create
import de.westnordost.streetcomplete.data.osm.mapdata.LatLon
import de.westnordost.streetcomplete.data.osm.mapdata.Node
import de.westnordost.streetcomplete.data.osm.mapdata.Way
Expand All @@ -19,7 +19,7 @@ class NameAndLocationLabelTest {

init {
val context = InstrumentationRegistry.getInstrumentation().targetContext
featureDictionary = AndroidFeatureDictionary.create(context.assets, "osmfeatures/default", "osmfeatures/brands")
featureDictionary = FeatureDictionary.create(context.assets, "osmfeatures/default", "osmfeatures/brands")

val conf = Configuration(context.resources.configuration)
conf.setLocale(Locale.ENGLISH)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package de.westnordost.streetcomplete.data.meta

import android.content.res.AssetManager
import de.westnordost.countryboundaries.CountryBoundaries
import de.westnordost.osmfeatures.AndroidFeatureDictionary
import de.westnordost.osmfeatures.FeatureDictionary
import de.westnordost.osmfeatures.create
import org.koin.core.qualifier.named
import org.koin.dsl.module

Expand All @@ -17,7 +17,7 @@ val metadataModule = module {
}
single<Lazy<FeatureDictionary>>(named("FeatureDictionaryLazy")) {
lazy {
AndroidFeatureDictionary.create(get(), "osmfeatures/default", "osmfeatures/brands")
FeatureDictionary.create(get<AssetManager>(), "osmfeatures/default", "osmfeatures/brands")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import de.westnordost.streetcomplete.overlays.AbstractOverlayForm
import de.westnordost.streetcomplete.overlays.AnswerItem
import de.westnordost.streetcomplete.quests.LocalizedNameAdapter
import de.westnordost.streetcomplete.util.DummyFeature
import de.westnordost.streetcomplete.util.getLocalesForFeatureDictionary
import de.westnordost.streetcomplete.util.getLanguagesForFeatureDictionary
import de.westnordost.streetcomplete.util.getLocationLabel
import de.westnordost.streetcomplete.util.ktx.geometryType
import de.westnordost.streetcomplete.util.ktx.viewLifecycleScope
Expand Down Expand Up @@ -69,19 +69,18 @@ class PlacesOverlayForm : AbstractOverlayForm() {

val element = element
originalFeature = element?.let {
val locales = getLocalesForFeatureDictionary(resources.configuration)
val languages = getLanguagesForFeatureDictionary(resources.configuration)
val geometryType = if (element.type == ElementType.NODE) null else element.geometryType

if (element.isDisusedPlace()) {
featureDictionary.byId("shop/vacant").forLocale(*locales).get()
featureDictionary.getById("shop/vacant", languages = languages)
} else {
featureDictionary
.byTags(element.tags)
.forLocale(*locales)
.forGeometry(geometryType)
.inCountry(countryOrSubdivisionCode)
.find()
.firstOrNull()
featureDictionary.getByTags(
tags = element.tags,
languages = languages,
country = countryOrSubdivisionCode,
geometry = geometryType,
).firstOrNull()
// if not found anything in the iD presets, it's a shop type unknown to iD presets
?: DummyFeature(
"shop/unknown",
Expand Down Expand Up @@ -165,7 +164,7 @@ class PlacesOverlayForm : AbstractOverlayForm() {
featureCtrl.feature = feature
// clear (previous) names if selected feature contains already a name (i.e. is a brand feature)
// or is vacant
if (feature.addTags?.get("name") != null || feature.id == "shop/vacant") {
if (feature.addTags["name"] != null || feature.id == "shop/vacant") {
namesAdapter?.names = emptyList()
}

Expand All @@ -174,7 +173,7 @@ class PlacesOverlayForm : AbstractOverlayForm() {
}

private fun setVacant() {
onSelectedFeature(featureDictionary.byId("shop/vacant").get())
onSelectedFeature(featureDictionary.getById("shop/vacant")!!)
}

private fun createNoNameAnswer(): AnswerItem? =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import de.westnordost.streetcomplete.overlays.AbstractOverlayForm
import de.westnordost.streetcomplete.overlays.AnswerItem
import de.westnordost.streetcomplete.overlays.IAnswerItem
import de.westnordost.streetcomplete.util.LastPickedValuesStore
import de.westnordost.streetcomplete.util.getLocalesForFeatureDictionary
import de.westnordost.streetcomplete.util.getLanguagesForFeatureDictionary
import de.westnordost.streetcomplete.util.ktx.couldBeSteps
import de.westnordost.streetcomplete.util.ktx.valueOfOrNull
import de.westnordost.streetcomplete.view.setImage
Expand Down Expand Up @@ -142,9 +142,11 @@ class SurfaceOverlayForm : AbstractOverlayForm() {
switchToFootwayCyclewaySurfaceLayout()
}

val locales = getLocalesForFeatureDictionary(resources.configuration)
binding.cyclewaySurfaceLabel.text = featureDictionary.byId("highway/cycleway").forLocale(*locales).get()?.name
binding.footwaySurfaceLabel.text = featureDictionary.byId("highway/footway").forLocale(*locales).get()?.name
val languages = getLanguagesForFeatureDictionary(resources.configuration)
binding.cyclewaySurfaceLabel.text =
featureDictionary.getById("highway/cycleway", languages = languages)?.name
binding.footwaySurfaceLabel.text =
featureDictionary.getById("highway/footway", languages = languages)?.name

checkIsFormComplete()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import de.westnordost.streetcomplete.overlays.AbstractOverlayForm
import de.westnordost.streetcomplete.overlays.AnswerItem
import de.westnordost.streetcomplete.overlays.IAnswerItem
import de.westnordost.streetcomplete.util.DummyFeature
import de.westnordost.streetcomplete.util.getLocalesForFeatureDictionary
import de.westnordost.streetcomplete.util.getLanguagesForFeatureDictionary
import de.westnordost.streetcomplete.util.getNameAndLocationLabel
import de.westnordost.streetcomplete.util.ktx.geometryType
import de.westnordost.streetcomplete.view.controller.FeatureViewController
Expand Down Expand Up @@ -72,16 +72,15 @@ class ThingsOverlayForm : AbstractOverlayForm() {
}

private fun getFeatureDictionaryFeature(element: Element): Feature? {
val locales = getLocalesForFeatureDictionary(resources.configuration)
val languages = getLanguagesForFeatureDictionary(resources.configuration)
val geometryType = if (element.type == ElementType.NODE) null else element.geometryType

return featureDictionary
.byTags(element.tags)
.forLocale(*locales)
.forGeometry(geometryType)
.inCountry(countryOrSubdivisionCode)
.find()
.firstOrNull()
return featureDictionary.getByTags(
tags = element.tags,
languages = languages,
country = countryOrSubdivisionCode,
geometry = geometryType
).firstOrNull()
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ import de.westnordost.streetcomplete.quests.opening_hours.AddOpeningHours
import de.westnordost.streetcomplete.quests.opening_hours_signed.CheckOpeningHoursSigned
import de.westnordost.streetcomplete.quests.orchard_produce.AddOrchardProduce
import de.westnordost.streetcomplete.quests.parcel_locker_brand.AddParcelLockerBrand
import de.westnordost.streetcomplete.quests.parcel_locker_mail_in.AddParcelLockerMailIn
import de.westnordost.streetcomplete.quests.parcel_locker_pickup.AddParcelLockerPickup
import de.westnordost.streetcomplete.quests.parking_access.AddBikeParkingAccess
import de.westnordost.streetcomplete.quests.parking_access.AddParkingAccess
import de.westnordost.streetcomplete.quests.parking_fee.AddBikeParkingFee
Expand Down Expand Up @@ -387,6 +389,8 @@ fun questTypeRegistry(
74 to AddBikeParkingCapacity(), // used by cycle map layer on osm.org, OsmAnd

167 to AddParcelLockerBrand(),
168 to AddParcelLockerPickup(),
169 to AddParcelLockerMailIn(),

// address: usually only visible when just in front + sometimes requires to take "other answer"
75 to AddHousenumber(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.data.meta.AbbreviationsByLocale
import de.westnordost.streetcomplete.data.osm.mapdata.LatLon
import de.westnordost.streetcomplete.databinding.ViewStreetOrPlaceNameInputBinding
import de.westnordost.streetcomplete.osm.address.PlaceName
import de.westnordost.streetcomplete.osm.address.StreetOrPlaceName
import de.westnordost.streetcomplete.osm.address.StreetOrPlaceNameViewController
import de.westnordost.streetcomplete.quests.AbstractOsmQuestForm
Expand Down Expand Up @@ -72,8 +73,9 @@ class AddAddressStreetForm : AbstractOsmQuestForm<StreetOrPlaceName>() {
streetOrPlaceCtrl.selectStreetAt(position, clickAreaSizeInMeters)

override fun onClickOk() {
lastWasPlaceName = isShowingPlaceName
applyAnswer(streetOrPlaceCtrl.streetOrPlaceName!!)
val streetOrPlaceName = streetOrPlaceCtrl.streetOrPlaceName!!
lastWasPlaceName = streetOrPlaceName is PlaceName
applyAnswer(streetOrPlaceName)
}

override fun isFormComplete(): Boolean =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package de.westnordost.streetcomplete.quests.parcel_locker_mail_in

import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.data.osm.geometry.ElementGeometry
import de.westnordost.streetcomplete.data.osm.mapdata.Element
import de.westnordost.streetcomplete.data.osm.mapdata.MapDataWithGeometry
import de.westnordost.streetcomplete.data.osm.mapdata.filter
import de.westnordost.streetcomplete.data.osm.osmquests.OsmFilterQuestType
import de.westnordost.streetcomplete.data.user.achievements.EditTypeAchievement.POSTMAN
import de.westnordost.streetcomplete.osm.Tags
import de.westnordost.streetcomplete.quests.YesNoQuestForm
import de.westnordost.streetcomplete.util.ktx.toYesNo

class AddParcelLockerMailIn : OsmFilterQuestType<Boolean>() {

override val elementFilter = "nodes with amenity = parcel_locker and !parcel_mail_in"
override val changesetComment = "Specify if it's possible to drop off parcels with this locker"
override val wikiLink = "Tag:amenity=parcel_locker"
override val icon = R.drawable.ic_quest_parcel_locker_deposit
override val isDeleteElementEnabled = true
override val achievements = listOf(POSTMAN)

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

override fun getHighlightedElements(element: Element, getMapData: () -> MapDataWithGeometry) =
getMapData().filter("nodes with amenity = parcel_locker")

override fun createForm() = YesNoQuestForm()

override fun applyAnswerTo(answer: Boolean, tags: Tags, geometry: ElementGeometry, timestampEdited: Long) {
tags["parcel_mail_in"] = answer.toYesNo()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package de.westnordost.streetcomplete.quests.parcel_locker_pickup

import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.data.osm.geometry.ElementGeometry
import de.westnordost.streetcomplete.data.osm.mapdata.Element
import de.westnordost.streetcomplete.data.osm.mapdata.MapDataWithGeometry
import de.westnordost.streetcomplete.data.osm.mapdata.filter
import de.westnordost.streetcomplete.data.osm.osmquests.OsmFilterQuestType
import de.westnordost.streetcomplete.data.user.achievements.EditTypeAchievement.POSTMAN
import de.westnordost.streetcomplete.osm.Tags
import de.westnordost.streetcomplete.quests.YesNoQuestForm
import de.westnordost.streetcomplete.util.ktx.toYesNo

class AddParcelLockerPickup : OsmFilterQuestType<Boolean>() {

override val elementFilter = "nodes with amenity = parcel_locker and !parcel_pickup"
override val changesetComment = "Specify if it's possible to pickup parcels with this locker"
override val wikiLink = "Tag:amenity=parcel_locker"
override val icon = R.drawable.ic_quest_parcel_locker_pickup
override val isDeleteElementEnabled = true
override val achievements = listOf(POSTMAN)

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

override fun getHighlightedElements(element: Element, getMapData: () -> MapDataWithGeometry) =
getMapData().filter("nodes with amenity = parcel_locker")

override fun createForm() = YesNoQuestForm()

override fun applyAnswerTo(answer: Boolean, tags: Tags, geometry: ElementGeometry, timestampEdited: Long) {
tags["parcel_pickup"] = answer.toYesNo()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,23 @@ import de.westnordost.osmfeatures.GeometryType
import java.util.Locale

data class DummyFeature(
private val id: String,
private val name: String,
private val icon: String,
private val addTags: Map<String, String>
override val id: String,
override val name: String,
override val icon: String?,
override val tags: Map<String, String>
) : Feature {
override fun getId() = id
override fun getTags() = addTags
override fun getGeometry() = listOf(GeometryType.POINT, GeometryType.AREA)
override fun getName() = name
override fun getIcon() = icon
override fun getImageURL() = null
override fun getNames() = listOf(name)
override fun getTerms() = emptyList<String>()
override fun getIncludeCountryCodes() = emptyList<String>()
override fun getExcludeCountryCodes() = emptyList<String>()
override fun isSearchable() = false
override fun getMatchScore() = 1.0
override fun getAddTags() = addTags
override fun getRemoveTags() = emptyMap<String, String>()
override fun getCanonicalNames() = emptyList<String>()
override fun getCanonicalTerms() = emptyList<String>()
override fun isSuggestion() = false
override fun getLocale(): Locale = Locale.getDefault()
override val geometry = listOf(GeometryType.POINT, GeometryType.AREA)
override val imageURL = null
override val names = listOf(name)
override val terms = emptyList<String>()
override val includeCountryCodes = emptyList<String>()
override val excludeCountryCodes = emptyList<String>()
override val isSearchable = false
override val matchScore = 1.0f
override val addTags = tags
override val removeTags = emptyMap<String, String>()
override val canonicalNames = emptyList<String>()
override val canonicalTerms = emptyList<String>()
override val isSuggestion = false
override val language: String? = Locale.getDefault().toLanguageTag()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@ package de.westnordost.streetcomplete.util
import android.content.res.Configuration
import androidx.core.os.ConfigurationCompat
import de.westnordost.streetcomplete.util.ktx.toList
import java.util.Locale

fun getLocalesForFeatureDictionary(configuration: Configuration): Array<Locale?> {
val result = ArrayList<Locale?>()
result.addAll(ConfigurationCompat.getLocales(configuration).toList())
fun getLanguagesForFeatureDictionary(configuration: Configuration): List<String?> {
val result = ArrayList<String?>()
result.addAll(ConfigurationCompat.getLocales(configuration).toList().map { it.toLanguageTag() } )
/* add fallback to English if (some) English is not part of the locale list already as the
fallback for text is also always English in this app (strings.xml) independent of, or rather
additionally to what is in the user's LocaleList. */
if (result.none { it?.language == Locale.ENGLISH.language }) {
result.add(Locale.ENGLISH)
if (result.none { it == "en" }) {
result.add("en")
}
// add null to allow unlocalized features
result.add(null)
return result.toTypedArray()
return result
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ fun getNameAndLocationLabel(
featureDictionary: FeatureDictionary?,
showHouseNumber: Boolean? = null
): CharSequence? {
val locales = getLocalesForFeatureDictionary(resources.configuration)
val languages = getLanguagesForFeatureDictionary(resources.configuration)
val feature = featureDictionary
?.getFeature(element, locales)
?.getFeature(element, languages)
?.name
?.withNonBreakingSpaces()
?.inItalics()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,19 @@ import de.westnordost.osmfeatures.FeatureDictionary
import de.westnordost.osmfeatures.GeometryType
import de.westnordost.streetcomplete.data.osm.mapdata.Element
import de.westnordost.streetcomplete.data.osm.mapdata.ElementType
import java.util.Locale

fun FeatureDictionary.getFeature(
element: Element,
locales: Array<Locale?>? = null,
languages: List<String?>? = null,
): Feature? {
// only if geometry is not a node because at this point we cannot tell apart points vs vertices
val geometryType = if (element.type == ElementType.NODE) null else element.geometryType
val builder = this
.byTags(element.tags)
.isSuggestion(false) // no brands
.forGeometry(geometryType)
if (locales != null) builder.forLocale(*locales)
val features = builder.find()
val features = getByTags(
tags = element.tags,
languages = languages,
geometry = geometryType,
isSuggestion = false // no brands
)

// see comment above - we want at least only features that can either be nodes or vertices if
// our element is a node
Expand Down
Loading

0 comments on commit 23279ab

Please sign in to comment.