10 Commits

8 changed files with 366 additions and 231 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ android {
applicationId 'net.helcel.beans'
minSdk = 28
targetSdk = 37
versionName "1.4"
versionName "1.5"
versionCode project.hasProperty('VERSION_CODE') ? project.property('VERSION_CODE').toInteger() : 1
}
signingConfigs {
@@ -14,7 +14,6 @@ import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
@@ -22,10 +21,11 @@ import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Percent
import androidx.compose.material.icons.filled.Settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
@@ -43,8 +43,8 @@ import net.helcel.beans.svg.SVGWrapper
class MainScreen : ComponentActivity() {
private lateinit var psvg: SVGWrapper
private lateinit var css: CSSWrapper
private var psvg by mutableStateOf<SVGWrapper?>(null)
private var css by mutableStateOf<CSSWrapper?>(null)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -53,22 +53,29 @@ class MainScreen : ComponentActivity() {
Data.loadData(this, Int.MIN_VALUE)
GeoLocImporter.importStates(this)
refreshProjection()
setContent {
SysTheme {
Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.primary).statusBarsPadding(),) {
AppNavHost(psvg, css)
AppNavHost()
}
}
}
refreshProjection()
}
@Composable
fun AppNavHost(psvg: SVGWrapper, css: CSSWrapper) {
fun AppNavHost() {
val navController = rememberNavController()
NavHost(navController, startDestination = "main") {
composable("main") { MainScreenC(psvg,css, navController) }
composable("settings") { SettingsMainScreen { navController.navigate("main")} }
composable("main") {
val currentPsvg = psvg
val currentCss = css
if (currentPsvg != null && currentCss != null) {
MainScreenC(currentPsvg, currentCss, navController)
}
}
composable("settings") { SettingsMainScreen { navController.navigate("main") } }
composable("edit") { EditScreen { navController.navigate("main") } }
composable("stats") { StatsScreen { navController.navigate("main") } }
}
@@ -105,19 +112,25 @@ class MainScreen : ComponentActivity() {
@Composable
fun MapScreen(psvg: SVGWrapper, css: CSSWrapper) {
Box {
val cssContent = css.get()
val drawable = remember(psvg, css, cssContent) {
val opt: RenderOptions = RenderOptions.create()
opt.css(css.get())
val drawable = remember(psvg, css) {
opt.css(cssContent)
PictureDrawable(psvg.get()?.renderToPicture(opt))
}
AndroidView(factory = { ctx ->
AndroidView(
factory = { ctx ->
PhotoView(ctx).apply {
setLayerType(ImageView.LAYER_TYPE_SOFTWARE, null)
setImageDrawable(drawable)
maximumScale = 32f
maximumScale = 64f
scaleType = ImageView.ScaleType.FIT_CENTER
}
}, modifier = Modifier.fillMaxSize())
},
update = { view ->
view.setImageDrawable(drawable)
},
modifier = Modifier.fillMaxSize()
)
}
}
@@ -48,23 +48,22 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.preference.PreferenceManager
import net.helcel.beans.R
import net.helcel.beans.countries.GeoLocImporter
import net.helcel.beans.helper.Settings
import androidx.core.content.edit
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.preference.PreferenceManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.helcel.beans.R
import net.helcel.beans.activity.sub.AboutScreen
import net.helcel.beans.activity.sub.EditPlaceColorDialog
import net.helcel.beans.activity.sub.EditPlaceDialog
import net.helcel.beans.activity.sub.LicenseScreen
import net.helcel.beans.countries.GeoLocImporter
import net.helcel.beans.helper.Data
import net.helcel.beans.helper.Settings
@Composable
fun SysTheme(
@@ -156,154 +155,39 @@ fun SettingsScreen(navController: NavHostController = settingsNav()) {
val defaultTheme = stringResource(R.string.system)
val keyProjection = stringResource(R.string.key_projection)
val keyGroup = stringResource(R.string.key_group)
val keyRegional = stringResource(R.string.key_regional)
val keyCascadeStats = stringResource(R.string.key_cascade_stats)
val offString = stringResource(R.string.off)
var showEdit by remember { mutableStateOf(false) }
val onString = stringResource(R.string.on)
var theme by remember { mutableStateOf(prefs.getString(keyTheme, defaultTheme)!!) }
var projection by remember { mutableStateOf(prefs.getString(keyProjection, "default")!!) }
var groups by remember { mutableStateOf(prefs.getString(keyGroup,offString)!!) }
var groups by remember { mutableStateOf(prefs.getString(keyGroup, offString)!!) }
var regional by remember { mutableStateOf(prefs.getString(keyRegional, offString)!!) }
var cascadeStats by remember { mutableStateOf(prefs.getString(keyCascadeStats, offString)!!) }
if(showEdit)
EditPlaceDialog(true) {
showEdit = false
val g = Data.selected_group
if (it && g != null) {
Data.visits.reassignAllVisitedToGroup(g.key)
Data.saveData()
}
}
var showGroupDialog by remember { mutableStateOf(false) }
var showRegionalDialog by remember { mutableStateOf(false) }
var showLoad by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
.background(MaterialTheme.colors.background)
) {
item {
Text(
"Theme", style = MaterialTheme.typography.h6,
color = MaterialTheme.colors.onBackground,
)
MultiPreference(arrayOf(stringResource(R.string.system),stringResource(R.string.light),stringResource(R.string.dark)), theme) { newTheme ->
theme = newTheme
prefs.edit { putString(keyTheme, newTheme) }
}
HorizontalDivider()
}
item {
Text(
"Map Projection",
style = MaterialTheme.typography.h6,
color = MaterialTheme.colors.onBackground,
modifier = Modifier.padding(top = 16.dp)
)
MultiPreference(arrayOf(stringResource(R.string.mercator), stringResource(R.string.azimuthalequidistant)), projection) { newProj ->
projection = newProj
prefs.edit { putString(keyProjection, newProj) }
Settings.refreshProjection()
}
HorizontalDivider()
}
item {
Text(
"Groups",
style = MaterialTheme.typography.h6,
color = MaterialTheme.colors.onBackground,
modifier = Modifier.padding(top = 16.dp)
)
var showDialog by remember{mutableStateOf(false)}
if(showDialog){
if (showGroupDialog) {
EditPlaceColorDialog(
deleteMode = true,
onDismiss = {
val g = Data.selected_group
if (g != null) {
Data.visits.reassignAllVisitedToGroup(g.key)
Data.groups.keepOnly(g.key)
Data.saveData()
groups = offString
prefs.edit { putString(keyGroup, offString) }
}
showDialog = false
showGroupDialog = false
})
}
MultiPreference(
arrayOf(stringResource(R.string.on), stringResource(R.string.off)),
groups
) { key ->
if (key == offString) {
showDialog=true
}
groups = key
prefs.edit { putString(keyGroup, key) }
}
HorizontalDivider()
}
item {
Text(
text = "Regional",
style = MaterialTheme.typography.h6,
color = MaterialTheme.colors.onBackground,
modifier = Modifier
.padding(top = 16.dp)
.clickable(onClick = {}),
)
RegionalScreen()
HorizontalDivider()
}
item{
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.OpenDocument(),
onResult = { uri -> Data.doImport(context, uri) }
)
Row(
modifier = Modifier.fillMaxWidth()
) {
Button(onClick = {
launcher.launch(arrayOf("*/*"))
}, modifier = Modifier
.fillMaxWidth(fraction = 0.4f)
.padding(vertical = 8.dp)) {
Text("Import")
}
Spacer(
modifier = Modifier.fillMaxWidth(0.4f)
)
Button(onClick = {
Data.doExport(context)
}, modifier = Modifier
.fillMaxWidth(fraction = 1f)
.padding(vertical = 8.dp)) {
Text("Export")
}
}
HorizontalDivider()
}
item {
PreferenceButton("Licenses") {
if (navController.currentDestination?.route != "licenses")
navController.navigate("licenses")
}
PreferenceButton("About") {
if (navController.currentDestination?.route != "about")
navController.navigate("about")
}
}
}
}
@Composable
fun RegionalScreen() {
val context = LocalContext.current
val keyRegional = stringResource(R.string.key_regional)
val offString = stringResource(R.string.off)
val onString = stringResource(R.string.on)
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
var selected by remember { mutableStateOf(prefs.getString(keyRegional, offString)!!)}
var regional by remember{ mutableStateOf(prefs.getString(keyRegional, offString)!!)}
var showDialog by remember{mutableStateOf(false)}
var showLoad by remember{mutableStateOf(false)}
if(showDialog)
if (showRegionalDialog) {
Dialog(
content = {
Column(
@@ -312,26 +196,27 @@ fun RegionalScreen() {
MaterialTheme.colors.background,
RoundedCornerShape(corner = CornerSize(16.dp))
)
.padding(16.dp),){
Text(style=MaterialTheme.typography.caption, text= stringResource(R.string.delete_regions))
.padding(16.dp),
) {
Text(
style = MaterialTheme.typography.caption,
text = stringResource(R.string.delete_regions)
)
Button(onClick = {
GeoLocImporter.clearStates()
regional= selected
prefs.edit {
putString(
keyRegional,
regional
)
}
showDialog=false
}){
regional = offString
prefs.edit { putString(keyRegional, offString) }
showRegionalDialog = false
}) {
Text(stringResource(R.string.ok))
}
}
},
onDismissRequest = { showDialog=false }
onDismissRequest = { showRegionalDialog = false }
)
if(showLoad){
}
if (showLoad) {
Dialog(
content = {
CircularProgressIndicator(
@@ -343,41 +228,155 @@ fun RegionalScreen() {
onDismissRequest = {}
)
}
val scope = rememberCoroutineScope()
MultiPreference(arrayOf(stringResource(R.string.on),stringResource(R.string.off)),regional) { key ->
when (key) {
offString -> { showDialog=true
selected=key
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
.background(MaterialTheme.colors.background)
) {
item {
Text(
stringResource(R.string.key_theme),
style = MaterialTheme.typography.h6,
color = MaterialTheme.colors.onBackground,
)
MultiPreference(
arrayOf(
stringResource(R.string.system),
stringResource(R.string.light),
stringResource(R.string.dark)
), theme
) { newTheme ->
theme = newTheme
prefs.edit { putString(keyTheme, newTheme) }
}
onString -> {
regional = key
prefs.edit { putString(keyRegional, key) }
showLoad=true
HorizontalDivider()
}
item {
Text(
stringResource(R.string.key_projection),
style = MaterialTheme.typography.h6,
color = MaterialTheme.colors.onBackground,
modifier = Modifier.padding(top = 16.dp)
)
MultiPreference(
arrayOf(stringResource(R.string.mercator), stringResource(R.string.azimuthalequidistant)),
projection
) { newProj ->
projection = newProj
prefs.edit { putString(keyProjection, newProj) }
Settings.refreshProjection()
}
HorizontalDivider()
}
item {
SettingSwitch(
label = stringResource(R.string.key_group),
subtitle = stringResource(R.string.key_group_desc),
isChecked = groups == onString,
onCheckedChange = { isChecked ->
if (!isChecked) {
Data.selected_group = null
showGroupDialog = true
} else {
groups = onString
prefs.edit { putString(keyGroup, onString) }
}
}
)
HorizontalDivider()
}
item {
SettingSwitch(
label = stringResource(R.string.key_regional),
subtitle = stringResource(R.string.key_regional_desc),
isChecked = regional == onString,
onCheckedChange = { isChecked ->
if (isChecked) {
regional = onString
prefs.edit { putString(keyRegional, onString) }
showLoad = true
scope.launch {
withContext(Dispatchers.IO) {
GeoLocImporter.importStates(context, true)
}
showLoad = false
}
} else {
showRegionalDialog = true
}
}
)
HorizontalDivider()
}
item {
SettingSwitch(
label = stringResource(R.string.pref_cascade_stats),
subtitle = stringResource(R.string.pref_cascade_stats_desc),
isChecked = cascadeStats == onString,
onCheckedChange = { isChecked ->
cascadeStats = if (isChecked) onString else offString
prefs.edit { putString(keyCascadeStats, cascadeStats) }
}
)
HorizontalDivider()
}
item {
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.OpenDocument(),
onResult = { uri -> Data.doImport(context, uri) }
)
Row(
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp)
) {
Button(
onClick = {
launcher.launch(arrayOf("*/*"))
}, modifier = Modifier
.weight(1f)
) {
Text(stringResource(R.string.action_import))
}
Spacer(modifier = Modifier.size(16.dp))
Button(
onClick = {
Data.doExport(context)
}, modifier = Modifier
.weight(1f)
) {
Text(stringResource(R.string.action_export))
}
}
HorizontalDivider()
}
item {
PreferenceButton(stringResource(R.string.licenses)) {
if (navController.currentDestination?.route != "licenses")
navController.navigate("licenses")
}
PreferenceButton(stringResource(R.string.about)) {
if (navController.currentDestination?.route != "about")
navController.navigate("about")
}
}
}
}
@Composable
fun MultiPreference(list: Array<String>, selected: String, onSelected: (String) -> Unit) {
Column(Modifier.padding(2.dp)) {
Column(Modifier.padding(vertical = 2.dp)) {
list.forEach { value ->
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.height(36.dp)
.height(42.dp)
.clickable { onSelected(value) }) {
RadioButton(selected = selected == value, onClick = { onSelected(value) })
Text(
value, modifier = Modifier.padding(start = 8.dp),
value, modifier = Modifier.padding(start = 12.dp),
style = MaterialTheme.typography.body1,
color = MaterialTheme.colors.onBackground,
)
}
@@ -393,3 +392,38 @@ fun PreferenceButton(text: String, onClick: () -> Unit) {
Text(text)
}
}
@Composable
fun SettingSwitch(
label: String,
subtitle: String? = null,
isChecked: Boolean,
onCheckedChange: (Boolean) -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { onCheckedChange(!isChecked) }
.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Column(modifier = Modifier.weight(1f)) {
Text(
text=label,
style = MaterialTheme.typography.h6,
color = MaterialTheme.colors.onBackground,
)
if (subtitle != null) {
Text(
text = subtitle,
style = MaterialTheme.typography.body2,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f)
)
}
}
androidx.compose.material.Switch(
checked = isChecked,
onCheckedChange = onCheckedChange
)
}
}
@@ -20,6 +20,7 @@ import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
@@ -32,12 +33,15 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import net.helcel.beans.R
import net.helcel.beans.countries.Country
import net.helcel.beans.countries.GeoLoc
import net.helcel.beans.countries.GeoLoc.LocType
import net.helcel.beans.countries.World
import net.helcel.beans.helper.AUTO_GROUP
import net.helcel.beans.helper.Data
import net.helcel.beans.helper.Groups
import net.helcel.beans.helper.Settings.isRegional
import net.helcel.beans.helper.NO_GROUP
import net.helcel.beans.helper.Settings
import net.helcel.beans.helper.Theme.getContrastColor
private val MODE_LIST = listOf(LocType.WORLD, LocType.COUNTRY, LocType.STATE)
@@ -46,7 +50,7 @@ private val MODE_LIST = listOf(LocType.WORLD, LocType.COUNTRY, LocType.STATE)
fun StatsScreen(
onExit: ()-> Unit
) {
val modes = if (isRegional(LocalContext.current)) MODE_LIST else MODE_LIST.take(2)
val modes = if (Settings.isRegional(LocalContext.current)) MODE_LIST else MODE_LIST.take(2)
var selectedTab by remember { mutableIntStateOf(0) }
var countMode by remember { mutableStateOf(true) }
@@ -115,26 +119,83 @@ fun StatsList(activeMode: LocType, countMode: Boolean) {
@Composable
fun StatsRow(group: Groups.Group, activeMode: LocType, countMode: Boolean) {
val context = LocalContext.current
val isCascadeStats = remember { Settings.isCascadeStats(context) }
val visits by Data.visits.visitsFlow.collectAsState()
val visited = remember(group, activeMode) {
Data.visits.getVisitedByValue(group.key)
val continents = remember { World.WWW.children.toList() }
val countries = remember {
World.WWW.children.flatMap {
if (it is Country) listOf(it) else it.children
}
}
val count = when (activeMode) {
LocType.WORLD -> World.WWW.children.filter { it.code in visited }.size
LocType.COUNTRY -> World.WWW.children.flatMap { it.children.filter { c -> c.code in visited } }.size
LocType.STATE -> World.WWW.children.flatMap { a->a.children.flatMap { b->b.children.filter { c->c.code in visited } } }.size
else -> 0
val visitedItems = remember(group, activeMode, isCascadeStats, visits) {
val list = when (activeMode) {
LocType.WORLD -> continents
LocType.COUNTRY -> countries
LocType.STATE -> countries.flatMap { it.children }
else -> emptyList()
}
val area = when (activeMode) {
LocType.WORLD -> World.WWW.children.filter { it.code in visited }.sumOf { it.area }
LocType.COUNTRY -> World.WWW.children.flatMap { it.children.filter { c -> c.code in visited } }.sumOf { it.area }
LocType.STATE -> World.WWW.children.flatMap { a->a.children.flatMap { b->b.children.filter { c->c.code in visited } } }.sumOf { it.area }
else -> 0
fun getExplicitColor(l: GeoLoc): Int? {
val c = visits.getOrDefault(l.code, 0)
return if (c != NO_GROUP && c != AUTO_GROUP) c else null
}
val displayValue = if (countMode) count.toString() else stringResource(R.string.number_with_unit, area, "km²")
fun getEffectiveGroup(l: GeoLoc): Int {
getExplicitColor(l)?.let { return it }
if (isCascadeStats) {
// Parent -> Child (closest explicit ancestor)
val country = countries.find { it.children.contains(l) }
val countryColor = country?.let { getExplicitColor(it) }
if (countryColor != null) return countryColor
val continent = continents.find {
it.children.contains(l) || (country != null && it.children.contains(country))
}
val continentColor = continent?.let { getExplicitColor(it) }
if (continentColor != null) return continentColor
val worldColor = getExplicitColor(World.WWW)
if (worldColor != null) return worldColor
// Child -> Parent (most common explicit descendant)
val descendants = mutableListOf<GeoLoc>()
fun collect(curr: GeoLoc) {
curr.children.forEach {
descendants.add(it)
collect(it)
}
}
collect(l)
val descendantColors = descendants.mapNotNull { getExplicitColor(it) }
if (descendantColors.isNotEmpty()) {
return descendantColors.groupBy { it }.maxByOrNull { it.value.size }?.key ?: AUTO_GROUP
}
} else {
if (l.type == LocType.WORLD || l.type == LocType.COUNTRY) {
val descendants = mutableListOf<GeoLoc>()
fun collect(curr: GeoLoc) {
curr.children.forEach { descendants.add(it); collect(it) }
}
collect(l)
val dc = descendants.mapNotNull { getExplicitColor(it) }
if (dc.isNotEmpty()) return dc.groupBy { it }.maxByOrNull { it.value.size }?.key ?: AUTO_GROUP
}
}
return AUTO_GROUP
}
list.filter { getEffectiveGroup(it) == group.key }
}
val count = visitedItems.size
val area = visitedItems.sumOf { it.area.toLong() }
val displayValue = if (countMode) count.toString() else stringResource(R.string.number_with_unit, area.toInt(), "km²")
val backgroundColor = group.color.color
val textColor = getContrastColor(backgroundColor)
@@ -43,6 +43,13 @@ class Groups(val id: Int, @SerialName("grps") private val groups: HashMap<Int, G
updateFlow()
}
fun keepOnly(key: Int) {
val keep = groups[key] ?: return
groups.clear()
groups[key] = keep
updateFlow()
}
private fun updateFlow() {
_groupsFlow.value = groups.values.toList()
}
@@ -29,6 +29,13 @@ object Settings {
)
}
fun isCascadeStats(ctx: Context): Boolean {
return getBooleanValue(
ctx,
sp.getString(ctx.getString(R.string.key_cascade_stats), ctx.getString(R.string.off))
)
}
fun getStatPref(ctx: Context): String? {
return sp.getString(
ctx.getString(R.string.key_stats),
+14 -1
View File
@@ -15,8 +15,21 @@
<string name="percentages">Prefer percentages (%)</string>
<string name="licenses">Licenses</string>
<string name="key_group">Groups</string>
<string name="key_regional">Regional</string>
<string name="key_regional">Regional mode</string>
<string name="key_regional_desc">Enable tracking of states and provinces</string>
<string name="key_cascade_stats">key_cascade_stats</string>
<string name="pref_cascade_stats">Cascade statistics</string>
<string name="pref_cascade_stats_desc">Include parents and children in the counts</string>
<string name="key_group_desc">Enable multiple colors</string>
<string name="about">About</string>
<string name="pref_category_appearance">Appearance</string>
<string name="pref_category_features">Features</string>
<string name="pref_category_data">Data</string>
<string name="action_import">Import</string>
<string name="action_export">Export</string>
<string name="import_desc">Load data from a previously exported file.</string>
<string name="export_desc">Save your current data to a file for backup or transfer.</string>
<string name="key_projection_desc">Select how the map is projected onto a 2D surface.</string>
<string name="beans_is_foss">Beans is free and open source software, licensed under the GNU General Public License (version 3 or later)</string>
<string name="beans_repo_uri">https://github.com/helcel-net/beans</string>
<string name="beans_repo">Project repository: %1$s\n Feel free to report issues or contribute.</string>
+22 -22
View File
@@ -2698,9 +2698,9 @@ lodash@4.18.1:
integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==
lru-cache@^11.3.5:
version "11.5.1"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.5.1.tgz#f3daa3540847b9737ebc02499ddb36765e54db4a"
integrity sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==
version "11.5.2"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.5.2.tgz#00e16665c90c620fba14a3c368732a976493f760"
integrity sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==
map-stream@0.0.7:
version "0.0.7"
@@ -2708,9 +2708,9 @@ map-stream@0.0.7:
integrity sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==
mapshaper@^0.7.22:
version "0.7.41"
resolved "https://registry.yarnpkg.com/mapshaper/-/mapshaper-0.7.41.tgz#c03fa2aed375abbc92af47008dc81695a604ed23"
integrity sha512-TC1KL8OtYZPRuq/Wl0AW2LQOic/q/AQbEEiPkCXlN5dGFXPIRj8+Qllc3EWBhN7aViHStI2IIthP2/L7BDzobQ==
version "0.7.45"
resolved "https://registry.yarnpkg.com/mapshaper/-/mapshaper-0.7.45.tgz#8af5e9df2e262e2b70ffe3d7a6576ed497f4f2e8"
integrity sha512-ohTTOAKmhrdSy2Fcc/lhnwzs+J5t9Kt+duX8eUJCH7cBrga8S6flcGRzRMbUFhbzyU6HTS7a1vT0gpcvGnUmXQ==
dependencies:
"@bokuweb/zstd-wasm" "^0.0.27"
"@ngageoint/geopackage" "^4.2.6"
@@ -2737,7 +2737,7 @@ mapshaper@^0.7.22:
idb-keyval "^6.2.0"
jpeg-js "^0.4.4"
kdbush "^3.0.0"
mproj "0.1.3"
mproj "0.1.5"
msgpackr "^1.11.12"
open "^11.0.0"
pngjs "^7.0.0"
@@ -2772,10 +2772,10 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
mproj@0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/mproj/-/mproj-0.1.3.tgz#295b990970a80380dac85b29d605a5f4f66b25ff"
integrity sha512-XPxty3HPv1j5eXfxId1FSGX7JB8+KgozVOyCu5l40u/I0pW7gpWTaxouA8MNKHfEXN5oE8r9eowkM+0LuQduIQ==
mproj@0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/mproj/-/mproj-0.1.5.tgz#b6e9353bf394abe7cd9263082b45bed5ed5ea747"
integrity sha512-HgUQtpYqzZzYD4gQXDiTFu2KgMJP7AAQCfYFR9lQpulx9hcspmnMOuCiMMs1bUUj9gPg9uAGnHQYU3We/5yipQ==
dependencies:
geographiclib "1.48.0"
rw "~1.3.2"
@@ -3293,17 +3293,17 @@ tinyqueue@^2.0.0, tinyqueue@^2.0.3:
resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08"
integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==
tldts-core@^7.4.6:
version "7.4.6"
resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.4.6.tgz#4cc8ccdae554742f09e5999708cac5a7d83cb287"
integrity sha512-TkQNGJIhlEphpHCjKodMTSe23egUZr/g+flI2qkLgiJ/maAzSgXypSLRTNH3nCmqgayEmtcJBiLcfODSAr1xoA==
tldts-core@^7.4.8:
version "7.4.8"
resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.4.8.tgz#c729aee4ff9d3670741193682a98e25a3d780dd9"
integrity sha512-c1P7u0EhACHj7lPy4MJm8iTFEU8+nB0LCtddH0fhP7noaVoXAqafMtOOeX+ulpuPBqnrRgRhw494RICT3mbhnw==
tldts@^7.0.5:
version "7.4.6"
resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.4.6.tgz#3af5da04b35afd0e00cf0fe2827af30393a28202"
integrity sha512-rbP0Gyx8b3Ae9yO//CU2wbSnQNoQ66m1nJdSbSHmnwKwzkkz/u8mERYU8T2rmlmy+bJvRNn84yNCW8gYqox44Q==
version "7.4.8"
resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.4.8.tgz#f2edc0d81483ea76c45827d642ccd535a3a7a4f7"
integrity sha512-htwgN/8KRB3z3vnC0BOETVh2m499g5GmyTK9Wq5JBLX3FNz6tSBveAd+fQhzy9hkjif8vy2jwDMR1sGhLtZl2A==
dependencies:
tldts-core "^7.4.6"
tldts-core "^7.4.8"
token-types@^4.1.1:
version "4.2.1"
@@ -3328,9 +3328,9 @@ topojson-server@3.x:
commander "2"
tough-cookie@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-6.0.1.tgz#a495f833836609ed983c19bc65639cfbceb54c76"
integrity sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==
version "6.0.2"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-6.0.2.tgz#7b1f22fcf2daf06c4ff9d53ec1845f44c6627062"
integrity sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==
dependencies:
tldts "^7.0.5"