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

Only show lit quests during night-time #2872

Merged
merged 1 commit into from
Aug 22, 2021
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
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ dependencies {

// opening hours parser
implementation("ch.poole:OpeningHoursParser:0.25.0")

// sunset-sunrise parser for lit quests
implementation("com.luckycatlabs:SunriseSunsetCalculator:1.2")
TurnrDev marked this conversation as resolved.
Show resolved Hide resolved
}

/** Localizations that should be pulled from POEditor etc. */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package de.westnordost.streetcomplete.data.quest

enum class DayNightCycle { DAY_AND_NIGHT, ONLY_DAY, ONLY_NIGHT }
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ interface QuestType<T> {

/** The quest type can clean it's metadata that is older than the given timestamp here, if any */
fun deleteMetadataOlderThan(timestamp: Long) {}

/** if the quest should only be shown during day-light os night-time hours */
val dayNightCycle: DayNightCycle get() = DayNightCycle.DAY_AND_NIGHT
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import de.westnordost.streetcomplete.data.osm.osmquests.OsmQuest
import de.westnordost.streetcomplete.data.osm.osmquests.OsmQuestSource
import de.westnordost.streetcomplete.data.osmnotes.notequests.OsmNoteQuest
import de.westnordost.streetcomplete.data.osmnotes.notequests.OsmNoteQuestSource
import de.westnordost.streetcomplete.data.visiblequests.DayNightQuestFilter
import de.westnordost.streetcomplete.data.visiblequests.TeamModeQuestFilter
import de.westnordost.streetcomplete.data.visiblequests.VisibleQuestTypeSource
import java.util.concurrent.CopyOnWriteArrayList
Expand All @@ -17,7 +18,8 @@ import javax.inject.Singleton
private val osmQuestSource: OsmQuestSource,
private val osmNoteQuestSource: OsmNoteQuestSource,
private val visibleQuestTypeSource: VisibleQuestTypeSource,
private val teamModeQuestFilter: TeamModeQuestFilter
private val teamModeQuestFilter: TeamModeQuestFilter,
private val dayNightQuestFilter: DayNightQuestFilter,
) {
interface Listener {
/** Called when given quests in the given group have been added/removed */
Expand Down Expand Up @@ -82,7 +84,7 @@ import javax.inject.Singleton
}

private fun isVisible(quest: Quest): Boolean =
visibleQuestTypeSource.isVisible(quest.type) && teamModeQuestFilter.isVisible(quest)
visibleQuestTypeSource.isVisible(quest.type) && teamModeQuestFilter.isVisible(quest) && dayNightQuestFilter.isVisible(quest)

fun addListener(listener: Listener) {
listeners.add(listener)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package de.westnordost.streetcomplete.data.visiblequests

import de.westnordost.streetcomplete.data.quest.DayNightCycle.*
import de.westnordost.streetcomplete.data.quest.Quest
import de.westnordost.streetcomplete.util.isDay
import javax.inject.Inject

class DayNightQuestFilter @Inject internal constructor() {
/*
Might be an idea to add a listener so this is reevaluated occasionally, or something like that.
However, I think it's reevaluated everytime the displayed quests are updated?
TurnrDev marked this conversation as resolved.
Show resolved Hide resolved
*/
fun isVisible(quest: Quest): Boolean {
return when (quest.type.dayNightCycle) {
DAY_AND_NIGHT -> true
ONLY_DAY -> isDay(quest.position)
ONLY_NIGHT -> !isDay(quest.position)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,11 @@ import javax.inject.Singleton
// ↓ 1. notes
OsmNoteQuestType,

// ↓ 2. important data that is used by many data consumers
// ↓ 2. time-specific quests, such as lit quests which are moved to only show ar night
AddWayLit(), // used by OsmAnd if "Street lighting" is enabled. (Configure map, Map rendering, Details)
AddBusStopLit(),

// ↓ 3. important data that is used by many data consumers
AddRoadName(),
AddPlaceName(featureDictionaryFuture),
AddOneway(),
Expand All @@ -152,7 +156,7 @@ import javax.inject.Singleton
AddReligionToPlaceOfWorship(), // icons on maps are different - OSM Carto, mapy.cz, OsmAnd, Sputnik etc
AddParkingAccess(), //OSM Carto, mapy.cz, OSMand, Sputnik etc

//3. useful data that is used by some data consumers
//4. useful data that is used by some data consumers
AddRecyclingType(),
AddRecyclingContainerMaterials(),
AddSport(),
Expand Down Expand Up @@ -191,15 +195,14 @@ import javax.inject.Singleton
AddFerryAccessMotorVehicle(),
AddAcceptsCash(featureDictionaryFuture),

//4. definitely shown as errors in QA tools
//5. definitely shown as errors in QA tools

//5. may be shown as missing in QA tools
//6. may be shown as missing in QA tools
DetermineRecyclingGlass(), // because most recycling:glass=yes is a tagging mistake

//6. may be shown as possibly missing in QA tools
//7. may be shown as possibly missing in QA tools

// ↓ 7. data useful for only a specific use case
AddWayLit(), // used by OsmAnd if "Street lighting" is enabled. (Configure map, Map rendering, Details)
// ↓ 8. data useful for only a specific use case
AddToiletsFee(), // used by OsmAnd in the object description
AddBabyChangingTable(), // used by OsmAnd in the object description
AddBikeParkingCover(), // used by OsmAnd in the object description
Expand Down Expand Up @@ -237,10 +240,10 @@ import javax.inject.Singleton
AddBollardType(), // useful for first responders
AddCameraType(),

//8. defined in the wiki, but not really used by anyone yet. Just collected for
//9. defined in the wiki, but not really used by anyone yet. Just collected for
// the sake of mapping it in case it makes sense later
AddPitchSurface(),
AddPitchLit(),
AddPitchLit(), // Not affected by new DayNight cycle because the lights are usually only on during games
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this has been requested by someone before, but I don't completely follow that logic.

The lights are usually only on during games, but only if it is dark outside, right? So, that should not change the fact that whether it is lit or not will only be possible to see when it is dark.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but given how infrequent games are- It would be possible to pass by on the street when there isn't a game, see no light on and assume there aren't any.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And during the daytime, it will be easier to see if there are lights at all.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm okay, if you agree with each other there..

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't the same argument apply to all the lit quests though, well in the positive direction at least, I can see street lights during the day so I should be able to tag them during the daytime. Although I appreciate marking no during the day is vaguer.

AddIsDefibrillatorIndoor(),
AddSummitRegister(),
AddCyclewayPartSurface(),
Expand All @@ -256,7 +259,6 @@ import javax.inject.Singleton
AddCarWashType(),
AddBenchStatusOnBusStop(),
AddBinStatusOnBusStop(),
AddBusStopLit(),
AddBenchBackrest(),
AddTrafficSignalsButton(),
AddPostboxRoyalCypher()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.data.meta.updateWithCheckDate
import de.westnordost.streetcomplete.data.osm.edits.update_tags.StringMapChangesBuilder
import de.westnordost.streetcomplete.data.osm.osmquests.OsmFilterQuestType
import de.westnordost.streetcomplete.data.quest.DayNightCycle.ONLY_NIGHT
import de.westnordost.streetcomplete.ktx.arrayOfNotNull
import de.westnordost.streetcomplete.ktx.containsAnyKey
import de.westnordost.streetcomplete.ktx.toYesNo
Expand All @@ -29,6 +30,7 @@ class AddBusStopLit : OsmFilterQuestType<Boolean>() {
override val commitMessage = "Add whether a bus stop is lit"
override val wikiLink = "Key:lit"
override val icon = R.drawable.ic_quest_bus_stop_lit
override val dayNightCycle = ONLY_NIGHT

override fun getTitle(tags: Map<String, String>): Int {
val hasName = tags.containsAnyKey("name", "ref")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package de.westnordost.streetcomplete.quests.way_lit
import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.data.meta.MAXSPEED_TYPE_KEYS
import de.westnordost.streetcomplete.data.meta.updateWithCheckDate
import de.westnordost.streetcomplete.data.osm.osmquests.OsmFilterQuestType
import de.westnordost.streetcomplete.data.osm.edits.update_tags.StringMapChangesBuilder
import de.westnordost.streetcomplete.data.osm.osmquests.OsmFilterQuestType
import de.westnordost.streetcomplete.data.quest.DayNightCycle.ONLY_NIGHT

class AddWayLit : OsmFilterQuestType<WayLit>() {

Expand Down Expand Up @@ -44,6 +45,7 @@ class AddWayLit : OsmFilterQuestType<WayLit>() {
override val wikiLink = "Key:lit"
override val icon = R.drawable.ic_quest_lantern
override val isSplitWayEnabled = true
override val dayNightCycle = ONLY_NIGHT

override fun getTitle(tags: Map<String, String>): Int {
val type = tags["highway"]
Expand Down
41 changes: 41 additions & 0 deletions app/src/main/java/de/westnordost/streetcomplete/util/CheckIfDay.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package de.westnordost.streetcomplete.util

import com.luckycatlabs.sunrisesunset.Zenith
import com.luckycatlabs.sunrisesunset.calculator.SolarEventCalculator
import com.luckycatlabs.sunrisesunset.dto.Location
import de.westnordost.streetcomplete.data.osm.mapdata.LatLon
import java.time.LocalDate
import java.time.LocalTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.util.*

fun localDateToCalendar(localDate: LocalDate): Calendar {
val calendar = Calendar.getInstance()
calendar.set(localDate.year, localDate.month.value, localDate.dayOfMonth)
return calendar
}

fun isDay(pos: LatLon): Boolean {
/* This functions job is to check if it's currently light out.
It will use the location of the node and check the civil sunrise/sunset time.

Sometimes sunset is after midnight. This would actually cause sunset to be before sunrise (as it's checking 00:00 to 23:59, it'll catch the day before!), so to gnt around this, we check the next day if that's the case.
*/

val timezone = TimeZone.getDefault().id
val location = Location(pos.latitude, pos.longitude)
val calculator = SolarEventCalculator(location, timezone)
val now = ZonedDateTime.now(ZoneId.of(timezone))
val today = now.toLocalDate()

val sunrise = ZonedDateTime.of(today, LocalTime.parse(calculator.computeSunriseTime(Zenith.CIVIL, localDateToCalendar(today))), ZoneId.of(timezone))
val sunset = ZonedDateTime.of(today, LocalTime.parse(calculator.computeSunsetTime(Zenith.CIVIL, localDateToCalendar(today))), ZoneId.of(timezone))
return if (sunset < sunrise) {

val sunset = ZonedDateTime.of(today.plusDays(1), LocalTime.parse(calculator.computeSunsetTime(Zenith.CIVIL, localDateToCalendar(today.plusDays(1)))), ZoneId.of(timezone))
now in sunrise..sunset
} else {
now in sunrise..sunset
}
}