Skip to content

Commit 36cbda5

Browse files
committed
LocationUtils: approx determine if point is within country bounds
* If country code is unavailable, using location coords, check if position is within bounding box
1 parent 6831917 commit 36cbda5

File tree

15 files changed

+279
-132
lines changed

15 files changed

+279
-132
lines changed

app/src/main/java/com/thewizrd/simpleweather/viewmodels/WeatherNowViewModel.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ class WeatherNowViewModel(app: Application) : AndroidViewModel(app) {
231231
when (result) {
232232
is WeatherResult.Error -> {
233233
viewModelState.update { state ->
234-
if (state.locationData?.countryCode?.let { !wm.isRegionSupported(it) } == true) {
234+
if (state.locationData?.let { !wm.isRegionSupported(it) } == true) {
235235
Logger.writeLine(
236236
Log.WARN,
237237
"Location: %s; countryCode: %s",
@@ -290,7 +290,7 @@ class WeatherNowViewModel(app: Application) : AndroidViewModel(app) {
290290
val weatherData = result.data.toUiModel()
291291

292292
viewModelState.update { state ->
293-
if (state.locationData?.countryCode?.let { !wm.isRegionSupported(it) } == true) {
293+
if (state.locationData?.let { !wm.isRegionSupported(it) } == true) {
294294
Logger.writeLine(
295295
Log.WARN,
296296
"Location: %s; countryCode: %s",

common/src/main/java/com/thewizrd/common/viewmodels/LocationSearchViewModel.kt

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import com.thewizrd.shared_resources.remoteconfig.remoteConfigService
1919
import com.thewizrd.shared_resources.utils.Coordinate
2020
import com.thewizrd.shared_resources.utils.JSONParser
2121
import com.thewizrd.shared_resources.utils.Logger
22+
import com.thewizrd.shared_resources.weatherdata.model.LocationType
2223
import com.thewizrd.weather_api.weatherModule
2324
import kotlinx.coroutines.Dispatchers
2425
import kotlinx.coroutines.Job
@@ -148,10 +149,10 @@ class LocationSearchViewModel(app: Application) : AndroidViewModel(app) {
148149

149150
if (!settingsManager.isWeatherLoaded() && !BuildConfig.IS_NONGMS) {
150151
// Set default provider based on location
151-
val provider =
152-
remoteConfigService.getDefaultWeatherProvider(locQuery.locationCountry)
152+
val provider = remoteConfigService.getDefaultWeatherProvider(locQuery)
153153
settingsManager.setAPI(provider)
154154
locQuery.updateWeatherSource(provider)
155+
wm.updateAPI()
155156
}
156157

157158
if (settingsManager.usePersonalKey() && settingsManager.getAPIKey()
@@ -164,11 +165,11 @@ class LocationSearchViewModel(app: Application) : AndroidViewModel(app) {
164165
return@launch
165166
}
166167

167-
if (!wm.isRegionSupported(locQuery.locationCountry)) {
168+
if (!wm.isRegionSupported(locQuery)) {
168169
Logger.writeLine(
169170
Log.WARN,
170171
"Location: %s; countryCode: %s",
171-
JSONParser.serializer(locQuery.toLocationData()),
172+
JSONParser.serializer(locQuery.toLocationData(LocationType.GPS)),
172173
locQuery.locationCountry
173174
)
174175
postErrorMessage(R.string.error_message_weather_region_unsupported)
@@ -179,7 +180,7 @@ class LocationSearchViewModel(app: Application) : AndroidViewModel(app) {
179180
}
180181

181182
viewModelState.update {
182-
it.copy(currentLocation = currentLocation)
183+
it.copy(currentLocation = locQuery.toLocationData(LocationType.GPS))
183184
}
184185
}
185186

@@ -288,13 +289,13 @@ class LocationSearchViewModel(app: Application) : AndroidViewModel(app) {
288289

289290
if (!settingsManager.isWeatherLoaded() && !BuildConfig.IS_NONGMS) {
290291
// Set default provider based on location
291-
val provider =
292-
remoteConfigService.getDefaultWeatherProvider(queryResult.locationCountry)
292+
val provider = remoteConfigService.getDefaultWeatherProvider(queryResult)
293293
settingsManager.setAPI(provider)
294294
queryResult.updateWeatherSource(provider)
295+
wm.updateAPI()
295296
}
296297

297-
if (!wm.isRegionSupported(queryResult.locationCountry)) {
298+
if (!wm.isRegionSupported(queryResult)) {
298299
Logger.writeLine(
299300
Log.WARN,
300301
"Location: %s; countryCode: %s",

common/src/main/java/com/thewizrd/common/weatherdata/WeatherDataLoader.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ class WeatherDataLoader {
131131
coroutineContext.ensureActive()
132132

133133
// Is the timezone valid? If not try to fetch a valid zone id
134-
if (!wm.isRegionSupported(location.countryCode) && (location.tzLong == "unknown" || location.tzLong == "UTC")) {
134+
if (!wm.isRegionSupported(location) && (location.tzLong == "unknown" || location.tzLong == "UTC")) {
135135
if (location.latitude != 0.0 && location.longitude != 0.0) {
136136
val tzId =
137137
weatherModule.tzdbService.getTimeZone(location.latitude, location.longitude)
@@ -143,12 +143,12 @@ class WeatherDataLoader {
143143
}
144144
}
145145

146-
if (!wm.isRegionSupported(location.countryCode)) {
146+
if (!wm.isRegionSupported(location)) {
147147
// If location data hasn't been updated, try loading weather from the previous provider
148148
if (!location.weatherSource.isNullOrBlank()) {
149149
val provider =
150150
weatherModule.weatherManager.getWeatherProvider(location.weatherSource)
151-
if (provider.isRegionSupported(location.countryCode)) {
151+
if (provider.isRegionSupported(location)) {
152152
weather = provider.getWeather(location)
153153
}
154154
}
@@ -267,7 +267,7 @@ class WeatherDataLoader {
267267
if (weather != null && weather.source != settingsMgr.getAPI() || location.weatherSource != settingsMgr.getAPI()) {
268268
// Only update location data if location region is supported by new API
269269
// If not don't update so we can use fallback (previously used API)
270-
if (wm.isRegionSupported(location.countryCode)) {
270+
if (wm.isRegionSupported(location)) {
271271
// Update location query and source for new API
272272
val oldKey = location.query ?: null
273273

shared_resources/src/fullgms/java/com/thewizrd/shared_resources/remoteconfig/RemoteConfigServiceImpl.kt

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package com.thewizrd.shared_resources.remoteconfig
22

33
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
44
import com.thewizrd.shared_resources.appLib
5+
import com.thewizrd.shared_resources.locationdata.LocationData
6+
import com.thewizrd.shared_resources.locationdata.LocationQuery
57
import com.thewizrd.shared_resources.utils.JSONParser
68
import com.thewizrd.shared_resources.utils.LocationUtils
79
import com.thewizrd.shared_resources.weatherdata.WeatherAPI
@@ -69,14 +71,33 @@ class RemoteConfigServiceImpl : RemoteConfigService {
6971
}
7072

7173
@WeatherAPI.WeatherProviders
72-
override fun getDefaultWeatherProvider(countryCode: String?): String {
74+
override fun getDefaultWeatherProvider(location: LocationQuery): String {
7375
return when {
74-
LocationUtils.isUS(countryCode) -> {
76+
LocationUtils.isUS(location) -> {
7577
WeatherAPI.NWS
7678
}
77-
LocationUtils.isFrance(countryCode) -> {
79+
80+
LocationUtils.isFrance(location) -> {
81+
WeatherAPI.METEOFRANCE
82+
}
83+
84+
else -> {
85+
getDefaultWeatherProvider()
86+
}
87+
}
88+
}
89+
90+
@WeatherAPI.WeatherProviders
91+
override fun getDefaultWeatherProvider(location: LocationData): String {
92+
return when {
93+
LocationUtils.isUS(location) -> {
94+
WeatherAPI.NWS
95+
}
96+
97+
LocationUtils.isFrance(location) -> {
7898
WeatherAPI.METEOFRANCE
7999
}
100+
80101
else -> {
81102
getDefaultWeatherProvider()
82103
}

shared_resources/src/main/java/com/thewizrd/shared_resources/remoteconfig/RemoteConfigService.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.thewizrd.shared_resources.remoteconfig
22

3+
import com.thewizrd.shared_resources.locationdata.LocationData
4+
import com.thewizrd.shared_resources.locationdata.LocationQuery
35
import com.thewizrd.shared_resources.weatherdata.WeatherAPI
46

57
val remoteConfigService: RemoteConfigService by lazy { RemoteConfigServiceImpl() }
@@ -14,7 +16,10 @@ interface RemoteConfigService {
1416
fun getDefaultWeatherProvider(): String
1517

1618
@WeatherAPI.WeatherProviders
17-
fun getDefaultWeatherProvider(countryCode: String?): String
19+
fun getDefaultWeatherProvider(location: LocationQuery): String
20+
21+
@WeatherAPI.WeatherProviders
22+
fun getDefaultWeatherProvider(location: LocationData): String
1823

1924
fun checkConfig()
2025
suspend fun checkConfigAsync(): Boolean

shared_resources/src/main/java/com/thewizrd/shared_resources/utils/LocationUtils.java

Lines changed: 0 additions & 31 deletions
This file was deleted.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
@file:Suppress("PropertyName")
2+
3+
package com.thewizrd.shared_resources.utils
4+
5+
import com.thewizrd.shared_resources.locationdata.LocationData
6+
import com.thewizrd.shared_resources.locationdata.LocationQuery
7+
8+
object LocationUtils {
9+
// Source: https://gist.github.com/graydon/11198540
10+
private val US_BOUNDING_BOX = BoundingBox(24.9493, 49.5904, -125.0011, -66.9326)
11+
private val USCA_BOUNDING_BOX =
12+
BoundingBox(24.4825578966, 71.7611572494, -168.9184947286, -52.2436900411)
13+
private val FR_BOUNDING_BOX = BoundingBox(41.2632185, 51.268318, -5.4534286, 9.8678344)
14+
15+
fun isUS(countryCode: String?): Boolean {
16+
return if (countryCode.isNullOrBlank()) {
17+
false
18+
} else {
19+
countryCode.equals("us", ignoreCase = true) || countryCode.equals(
20+
"usa",
21+
ignoreCase = true
22+
) || countryCode.lowercase().contains("united states")
23+
}
24+
}
25+
26+
fun isUS(location: LocationData): Boolean {
27+
return if (!location.countryCode.isNullOrBlank()) {
28+
isUS(location.countryCode)
29+
} else {
30+
US_BOUNDING_BOX.intersects(location.latitude, location.longitude)
31+
}
32+
}
33+
34+
fun isUS(location: LocationQuery): Boolean {
35+
return if (!location.locationCountry.isNullOrBlank()) {
36+
isUS(location.locationCountry)
37+
} else {
38+
US_BOUNDING_BOX.intersects(location.locationLat, location.locationLong)
39+
}
40+
}
41+
42+
fun isUSorCanada(countryCode: String?): Boolean {
43+
return if (countryCode.isNullOrBlank()) {
44+
false
45+
} else {
46+
isUS(countryCode) || countryCode.equals(
47+
"CA",
48+
ignoreCase = true
49+
) || countryCode.lowercase().contains("canada")
50+
}
51+
}
52+
53+
fun isUSorCanada(location: LocationData): Boolean {
54+
return if (!location.countryCode.isNullOrBlank()) {
55+
isUSorCanada(location.countryCode)
56+
} else {
57+
USCA_BOUNDING_BOX.intersects(location.latitude, location.longitude)
58+
}
59+
}
60+
61+
fun isUSorCanada(location: LocationQuery): Boolean {
62+
return if (!location.locationCountry.isNullOrBlank()) {
63+
isUSorCanada(location.locationCountry)
64+
} else {
65+
USCA_BOUNDING_BOX.intersects(location.locationLat, location.locationLong)
66+
}
67+
}
68+
69+
fun isFrance(countryCode: String?): Boolean {
70+
return if (countryCode.isNullOrBlank()) {
71+
false
72+
} else {
73+
countryCode.equals("fr", ignoreCase = true) || countryCode.equals(
74+
"france",
75+
ignoreCase = true
76+
)
77+
}
78+
}
79+
80+
fun isFrance(location: LocationData): Boolean {
81+
return if (!location.countryCode.isNullOrBlank()) {
82+
isFrance(location.countryCode)
83+
} else {
84+
FR_BOUNDING_BOX.intersects(location.latitude, location.longitude)
85+
}
86+
}
87+
88+
fun isFrance(location: LocationQuery): Boolean {
89+
return if (!location.locationCountry.isNullOrBlank()) {
90+
isFrance(location.locationCountry)
91+
} else {
92+
FR_BOUNDING_BOX.intersects(location.locationLat, location.locationLong)
93+
}
94+
}
95+
96+
private data class BoundingBox(
97+
val lat_min: Double,
98+
val lat_max: Double,
99+
val lon_min: Double,
100+
val lon_max: Double,
101+
) {
102+
fun intersects(lat: Double, lon: Double): Boolean {
103+
return (lat in lat_min..lat_max) && (lon in lon_min..lon_max)
104+
}
105+
}
106+
}

shared_resources/src/main/java/com/thewizrd/shared_resources/weatherdata/WeatherProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ interface WeatherProvider {
2222

2323
fun needsExternalAlertData(): Boolean
2424

25-
fun isRegionSupported(countryCode: String?): Boolean
25+
fun isRegionSupported(location: LocationData): Boolean
26+
27+
fun isRegionSupported(location: LocationQuery): Boolean
2628

2729
fun getHourlyForecastInterval(): Int
2830

shared_resources/src/nongms/java/com/thewizrd/shared_resources/remoteconfig/RemoteConfigServiceImpl.kt

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.thewizrd.shared_resources.remoteconfig
22

33
import android.location.Geocoder
4+
import com.thewizrd.shared_resources.locationdata.LocationData
5+
import com.thewizrd.shared_resources.locationdata.LocationQuery
46
import com.thewizrd.shared_resources.utils.LocationUtils
57
import com.thewizrd.shared_resources.weatherdata.WeatherAPI
68
import com.thewizrd.shared_resources.weatherdata.WeatherAPI.WeatherProviders
@@ -28,13 +30,36 @@ class RemoteConfigServiceImpl : RemoteConfigService {
2830
}
2931

3032
@WeatherProviders
31-
override fun getDefaultWeatherProvider(countryCode: String?): String {
32-
return if (LocationUtils.isUS(countryCode)) {
33-
WeatherAPI.NWS
34-
} else if (LocationUtils.isFrance(countryCode)) {
35-
WeatherAPI.METEOFRANCE
36-
} else {
37-
getDefaultWeatherProvider()
33+
override fun getDefaultWeatherProvider(location: LocationQuery): String {
34+
return when {
35+
LocationUtils.isUS(location) -> {
36+
WeatherAPI.NWS
37+
}
38+
39+
LocationUtils.isFrance(location) -> {
40+
WeatherAPI.METEOFRANCE
41+
}
42+
43+
else -> {
44+
getDefaultWeatherProvider()
45+
}
46+
}
47+
}
48+
49+
@WeatherProviders
50+
override fun getDefaultWeatherProvider(location: LocationData): String {
51+
return when {
52+
LocationUtils.isUS(location) -> {
53+
WeatherAPI.NWS
54+
}
55+
56+
LocationUtils.isFrance(location) -> {
57+
WeatherAPI.METEOFRANCE
58+
}
59+
60+
else -> {
61+
getDefaultWeatherProvider()
62+
}
3863
}
3964
}
4065

wearapp/src/main/java/com/thewizrd/simpleweather/viewmodels/WeatherNowViewModel.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ class WeatherNowViewModel(private val app: Application) : AndroidViewModel(app),
254254
viewModelState.update { state ->
255255
when (result) {
256256
is WeatherResult.Error -> {
257-
if (state.locationData?.countryCode?.let { !wm.isRegionSupported(it) } == true) {
257+
if (state.locationData?.let { !wm.isRegionSupported(it) } == true) {
258258
Logger.writeLine(
259259
Log.WARN,
260260
"Location: %s; countryCode: %s",
@@ -293,7 +293,7 @@ class WeatherNowViewModel(private val app: Application) : AndroidViewModel(app),
293293
)
294294
}
295295
is WeatherResult.WeatherWithError -> {
296-
if (state.locationData?.countryCode?.let { !wm.isRegionSupported(it) } == true) {
296+
if (state.locationData?.let { !wm.isRegionSupported(it) } == true) {
297297
Logger.writeLine(
298298
Log.WARN,
299299
"Location: %s; countryCode: %s",

0 commit comments

Comments
 (0)