mirror of
https://github.com/ev-map/EVMap.git
synced 2026-05-19 04:16:24 -04:00
new "mini" marker variant to avoid clustering for zoom levels 11-13
This commit is contained in:
@@ -126,6 +126,7 @@ class GoingElectricApiWrapper(
|
|||||||
baseurl: String = "https://api.goingelectric.de",
|
baseurl: String = "https://api.goingelectric.de",
|
||||||
context: Context? = null
|
context: Context? = null
|
||||||
) : ChargepointApi<GEReferenceData> {
|
) : ChargepointApi<GEReferenceData> {
|
||||||
|
private val clusterThreshold = 11f
|
||||||
val api = GoingElectricApi.create(apikey, baseurl, context)
|
val api = GoingElectricApi.create(apikey, baseurl, context)
|
||||||
|
|
||||||
override fun getName() = "GoingElectric.de"
|
override fun getName() = "GoingElectric.de"
|
||||||
@@ -173,7 +174,7 @@ class GoingElectricApiWrapper(
|
|||||||
val categories = formatMultipleChoice(categoriesVal)
|
val categories = formatMultipleChoice(categoriesVal)
|
||||||
|
|
||||||
// do not use clustering if filters need to be applied locally.
|
// do not use clustering if filters need to be applied locally.
|
||||||
val useClustering = zoom < 13
|
val useClustering = zoom < clusterThreshold
|
||||||
val geClusteringAvailable = minConnectors == null || minConnectors <= 1
|
val geClusteringAvailable = minConnectors == null || minConnectors <= 1
|
||||||
val useGeClustering = useClustering && geClusteringAvailable
|
val useGeClustering = useClustering && geClusteringAvailable
|
||||||
val clusterDistance = if (useClustering) getClusterDistance(zoom) else null
|
val clusterDistance = if (useClustering) getClusterDistance(zoom) else null
|
||||||
@@ -267,7 +268,7 @@ class GoingElectricApiWrapper(
|
|||||||
val categories = formatMultipleChoice(categoriesVal)
|
val categories = formatMultipleChoice(categoriesVal)
|
||||||
|
|
||||||
// do not use clustering if filters need to be applied locally.
|
// do not use clustering if filters need to be applied locally.
|
||||||
val useClustering = zoom < 13
|
val useClustering = zoom < clusterThreshold
|
||||||
val geClusteringAvailable = minConnectors == null || minConnectors <= 1
|
val geClusteringAvailable = minConnectors == null || minConnectors <= 1
|
||||||
val useGeClustering = useClustering && geClusteringAvailable
|
val useGeClustering = useClustering && geClusteringAvailable
|
||||||
val clusterDistance = if (useClustering) getClusterDistance(zoom) else null
|
val clusterDistance = if (useClustering) getClusterDistance(zoom) else null
|
||||||
@@ -330,7 +331,7 @@ class GoingElectricApiWrapper(
|
|||||||
}.map { it.convert(apikey, false) }
|
}.map { it.convert(apikey, false) }
|
||||||
|
|
||||||
// apply clustering
|
// apply clustering
|
||||||
val useClustering = zoom < 13
|
val useClustering = zoom < clusterThreshold
|
||||||
val geClusteringAvailable = minConnectors == null || minConnectors <= 1
|
val geClusteringAvailable = minConnectors == null || minConnectors <= 1
|
||||||
val clusterDistance = if (useClustering) getClusterDistance(zoom) else null
|
val clusterDistance = if (useClustering) getClusterDistance(zoom) else null
|
||||||
if (!geClusteringAvailable && useClustering) {
|
if (!geClusteringAvailable && useClustering) {
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ class OpenChargeMapApiWrapper(
|
|||||||
baseurl: String = "https://api.openchargemap.io/v3/",
|
baseurl: String = "https://api.openchargemap.io/v3/",
|
||||||
context: Context? = null
|
context: Context? = null
|
||||||
) : ChargepointApi<OCMReferenceData> {
|
) : ChargepointApi<OCMReferenceData> {
|
||||||
|
private val clusterThreshold = 11
|
||||||
val api = OpenChargeMapApi.create(apikey, baseurl, context)
|
val api = OpenChargeMapApi.create(apikey, baseurl, context)
|
||||||
|
|
||||||
override fun getName() = "OpenChargeMap.org"
|
override fun getName() = "OpenChargeMap.org"
|
||||||
@@ -238,7 +239,7 @@ class OpenChargeMapApiWrapper(
|
|||||||
}.map { it.convert(referenceData, false) }.distinct() as List<ChargepointListItem>
|
}.map { it.convert(referenceData, false) }.distinct() as List<ChargepointListItem>
|
||||||
|
|
||||||
// apply clustering
|
// apply clustering
|
||||||
val useClustering = zoom < 13
|
val useClustering = zoom < clusterThreshold
|
||||||
if (useClustering) {
|
if (useClustering) {
|
||||||
val clusterDistance = getClusterDistance(zoom)
|
val clusterDistance = getClusterDistance(zoom)
|
||||||
Dispatchers.IO.run {
|
Dispatchers.IO.run {
|
||||||
|
|||||||
@@ -518,7 +518,8 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
|||||||
highlight = true,
|
highlight = true,
|
||||||
fault = charger.faultReport != null,
|
fault = charger.faultReport != null,
|
||||||
multi = charger.isMulti(vm.filteredConnectors.value),
|
multi = charger.isMulti(vm.filteredConnectors.value),
|
||||||
fav = fav == null
|
fav = fav == null,
|
||||||
|
mini = vm.useMiniMarkers.value == true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -585,6 +586,9 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
|||||||
updateMap(chargepoints)
|
updateMap(chargepoints)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
vm.useMiniMarkers.observe(viewLifecycleOwner) {
|
||||||
|
vm.chargepoints.value?.data?.let { updateMap(it) }
|
||||||
|
}
|
||||||
vm.favorites.observe(viewLifecycleOwner, Observer {
|
vm.favorites.observe(viewLifecycleOwner, Observer {
|
||||||
updateFavoriteToggle()
|
updateFavoriteToggle()
|
||||||
})
|
})
|
||||||
@@ -650,7 +654,8 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
|||||||
highlight = false,
|
highlight = false,
|
||||||
fault = c.faultReport != null,
|
fault = c.faultReport != null,
|
||||||
multi = c.isMulti(vm.filteredConnectors.value),
|
multi = c.isMulti(vm.filteredConnectors.value),
|
||||||
fav = c.id in vm.favorites.value?.map { it.charger.id } ?: emptyList()
|
fav = c.id in vm.favorites.value?.map { it.charger.id } ?: emptyList(),
|
||||||
|
mini = vm.useMiniMarkers.value == true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -665,10 +670,11 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
|||||||
highlight = true,
|
highlight = true,
|
||||||
fault = charger.faultReport != null,
|
fault = charger.faultReport != null,
|
||||||
multi = charger.isMulti(vm.filteredConnectors.value),
|
multi = charger.isMulti(vm.filteredConnectors.value),
|
||||||
fav = charger.id in vm.favorites.value?.map { it.charger.id } ?: emptyList()
|
fav = charger.id in vm.favorites.value?.map { it.charger.id } ?: emptyList(),
|
||||||
|
mini = vm.useMiniMarkers.value == true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
animator.animateMarkerBounce(marker)
|
animator.animateMarkerBounce(marker, vm.useMiniMarkers.value == true)
|
||||||
|
|
||||||
// un-highlight all other markers
|
// un-highlight all other markers
|
||||||
markers.forEach { (m, c) ->
|
markers.forEach { (m, c) ->
|
||||||
@@ -679,7 +685,8 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
|||||||
highlight = false,
|
highlight = false,
|
||||||
fault = c.faultReport != null,
|
fault = c.faultReport != null,
|
||||||
multi = c.isMulti(vm.filteredConnectors.value),
|
multi = c.isMulti(vm.filteredConnectors.value),
|
||||||
fav = c.id in vm.favorites.value?.map { it.charger.id } ?: emptyList()
|
fav = c.id in vm.favorites.value?.map { it.charger.id } ?: emptyList(),
|
||||||
|
mini = vm.useMiniMarkers.value == true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -823,15 +830,22 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
|||||||
map.uiSettings.setRotateGesturesEnabled(prefs.mapRotateGesturesEnabled)
|
map.uiSettings.setRotateGesturesEnabled(prefs.mapRotateGesturesEnabled)
|
||||||
map.setIndoorEnabled(false)
|
map.setIndoorEnabled(false)
|
||||||
map.uiSettings.setIndoorLevelPickerEnabled(false)
|
map.uiSettings.setIndoorLevelPickerEnabled(false)
|
||||||
|
|
||||||
map.setOnCameraIdleListener {
|
map.setOnCameraIdleListener {
|
||||||
vm.mapPosition.value = MapPosition(
|
vm.mapPosition.value = MapPosition(
|
||||||
map.projection.visibleRegion.latLngBounds, map.cameraPosition.zoom
|
map.projection.visibleRegion.latLngBounds, map.cameraPosition.zoom
|
||||||
)
|
)
|
||||||
binding.scaleView.update(map.cameraPosition.zoom, map.cameraPosition.target.latitude)
|
vm.reloadChargepoints()
|
||||||
}
|
}
|
||||||
map.setOnCameraMoveListener {
|
map.setOnCameraMoveListener {
|
||||||
|
vm.mapPosition.value = MapPosition(
|
||||||
|
map.projection.visibleRegion.latLngBounds, map.cameraPosition.zoom
|
||||||
|
)
|
||||||
|
}
|
||||||
|
vm.mapPosition.observe(viewLifecycleOwner) {
|
||||||
binding.scaleView.update(map.cameraPosition.zoom, map.cameraPosition.target.latitude)
|
binding.scaleView.update(map.cameraPosition.zoom, map.cameraPosition.target.latitude)
|
||||||
}
|
}
|
||||||
|
|
||||||
map.setOnCameraMoveStartedListener { reason ->
|
map.setOnCameraMoveStartedListener { reason ->
|
||||||
if (reason == AnyMap.OnCameraMoveStartedListener.REASON_GESTURE) {
|
if (reason == AnyMap.OnCameraMoveStartedListener.REASON_GESTURE) {
|
||||||
if (vm.myLocationEnabled.value == true) {
|
if (vm.myLocationEnabled.value == true) {
|
||||||
@@ -1034,9 +1048,11 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
|||||||
highlight = highlight,
|
highlight = highlight,
|
||||||
fault = charger.faultReport != null,
|
fault = charger.faultReport != null,
|
||||||
multi = charger.isMulti(vm.filteredConnectors.value),
|
multi = charger.isMulti(vm.filteredConnectors.value),
|
||||||
fav = charger.id in vm.favorites.value?.map { it.charger.id } ?: emptyList()
|
fav = charger.id in vm.favorites.value?.map { it.charger.id } ?: emptyList(),
|
||||||
|
mini = vm.useMiniMarkers.value == true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
marker.setAnchor(0.5f, if (vm.useMiniMarkers.value == true) 0.5f else 1f)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chargers.toSet() != markers.values) {
|
if (chargers.toSet() != markers.values) {
|
||||||
@@ -1054,7 +1070,10 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
|||||||
val multi = charger.isMulti(vm.filteredConnectors.value)
|
val multi = charger.isMulti(vm.filteredConnectors.value)
|
||||||
val fav =
|
val fav =
|
||||||
charger.id in vm.favorites.value?.map { it.charger.id } ?: emptyList()
|
charger.id in vm.favorites.value?.map { it.charger.id } ?: emptyList()
|
||||||
animator.animateMarkerDisappear(marker, tint, highlight, fault, multi, fav)
|
animator.animateMarkerDisappear(
|
||||||
|
marker, tint, highlight, fault, multi, fav,
|
||||||
|
vm.useMiniMarkers.value == true
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
animator.deleteMarker(marker)
|
animator.deleteMarker(marker)
|
||||||
}
|
}
|
||||||
@@ -1082,12 +1101,16 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
|||||||
highlight,
|
highlight,
|
||||||
fault,
|
fault,
|
||||||
multi,
|
multi,
|
||||||
fav
|
fav,
|
||||||
|
vm.useMiniMarkers.value == true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.anchor(0.5f, 1f)
|
.anchor(0.5f, if (vm.useMiniMarkers.value == true) 0.5f else 1f)
|
||||||
|
)
|
||||||
|
animator.animateMarkerAppear(
|
||||||
|
marker, tint, highlight, fault, multi, fav,
|
||||||
|
vm.useMiniMarkers.value == true
|
||||||
)
|
)
|
||||||
animator.animateMarkerAppear(marker, tint, highlight, fault, multi, fav)
|
|
||||||
markers[marker] = charger
|
markers[marker] = charger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class ChargerIconGenerator(
|
|||||||
val context: Context,
|
val context: Context,
|
||||||
val factory: BitmapDescriptorFactory?,
|
val factory: BitmapDescriptorFactory?,
|
||||||
val scaleResolution: Int = 20,
|
val scaleResolution: Int = 20,
|
||||||
|
val scaleResolutionMini: Int = 10,
|
||||||
val oversize: Float = 1f, // increase to add padding for scale > 1
|
val oversize: Float = 1f, // increase to add padding for scale > 1
|
||||||
val height: Int = 48
|
val height: Int = 48
|
||||||
) {
|
) {
|
||||||
@@ -56,16 +57,21 @@ class ChargerIconGenerator(
|
|||||||
val highlight: Boolean,
|
val highlight: Boolean,
|
||||||
val fault: Boolean,
|
val fault: Boolean,
|
||||||
val multi: Boolean,
|
val multi: Boolean,
|
||||||
val fav: Boolean
|
val fav: Boolean,
|
||||||
|
val mini: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
// 230 items: (21 sizes, 5 colors, multi on/off) + highlight + fault (only with scale = 1)
|
// 340 items:
|
||||||
private val cacheSize = (scaleResolution + 3) * 5 * 2;
|
// large: (21 sizes, 5 colors, multi on/off) + highlight + fault + fav (only with scale = 1)
|
||||||
|
// mini: (11 sizes, 5 colors) + highlight (only with scale = 1)
|
||||||
|
private val cacheSize = (scaleResolution + 8) * 5 * 2 + (scaleResolutionMini + 2) * 5;
|
||||||
private val cache = LruCache<BitmapData, BitmapDescriptor>(cacheSize)
|
private val cache = LruCache<BitmapData, BitmapDescriptor>(cacheSize)
|
||||||
private val icon = R.drawable.ic_map_marker_charging
|
private val icon = R.drawable.ic_map_marker_charging
|
||||||
private val multiIcon = R.drawable.ic_map_marker_charging_multiple
|
private val multiIcon = R.drawable.ic_map_marker_charging_multiple
|
||||||
private val highlightIcon = R.drawable.ic_map_marker_highlight
|
private val miniIcon = R.drawable.ic_map_marker_charging_mini
|
||||||
|
private val highlightIcon = R.drawable.ic_map_marker_charging_highlight
|
||||||
private val highlightIconMulti = R.drawable.ic_map_marker_charging_highlight_multiple
|
private val highlightIconMulti = R.drawable.ic_map_marker_charging_highlight_multiple
|
||||||
|
private val highlightIconMini = R.drawable.ic_map_marker_charging_highlight_mini
|
||||||
private val faultIcon = R.drawable.ic_map_marker_fault
|
private val faultIcon = R.drawable.ic_map_marker_fault
|
||||||
private val favIcon = R.drawable.ic_map_marker_fav
|
private val favIcon = R.drawable.ic_map_marker_fav
|
||||||
|
|
||||||
@@ -82,12 +88,15 @@ class ChargerIconGenerator(
|
|||||||
for (highlight in listOf(false, true)) {
|
for (highlight in listOf(false, true)) {
|
||||||
for (multi in listOf(false, true)) {
|
for (multi in listOf(false, true)) {
|
||||||
for (fav in listOf(false, true)) {
|
for (fav in listOf(false, true)) {
|
||||||
for (tint in tints) {
|
for (mini in listOf(false, true)) {
|
||||||
for (scale in 0..scaleResolution) {
|
for (tint in tints) {
|
||||||
getBitmapDescriptor(
|
val scaleRes = if (mini) scaleResolutionMini else scaleResolution
|
||||||
tint, scale.toFloat() / scaleResolution,
|
for (scale in 0..scaleRes) {
|
||||||
255, highlight, fault, multi, fav
|
getBitmapDescriptor(
|
||||||
)
|
tint, scale.toFloat() / scaleRes,
|
||||||
|
255, highlight, fault, multi, fav, mini
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,16 +112,10 @@ class ChargerIconGenerator(
|
|||||||
highlight: Boolean = false,
|
highlight: Boolean = false,
|
||||||
fault: Boolean = false,
|
fault: Boolean = false,
|
||||||
multi: Boolean = false,
|
multi: Boolean = false,
|
||||||
fav: Boolean = false
|
fav: Boolean = false,
|
||||||
|
mini: Boolean = false
|
||||||
): BitmapDescriptor? {
|
): BitmapDescriptor? {
|
||||||
val data = BitmapData(
|
val data = createBitmapData(tint, scale, alpha, highlight, fault, multi, fav, mini)
|
||||||
tint, (scale * scaleResolution).roundToInt(),
|
|
||||||
alpha,
|
|
||||||
if (scale == 1f) highlight else false,
|
|
||||||
if (scale == 1f) fault else false,
|
|
||||||
multi,
|
|
||||||
if (scale == 1f) fav else false
|
|
||||||
)
|
|
||||||
val cachedImg = cache[data]
|
val cachedImg = cache[data]
|
||||||
return if (cachedImg != null) {
|
return if (cachedImg != null) {
|
||||||
cachedImg
|
cachedImg
|
||||||
@@ -124,6 +127,26 @@ class ChargerIconGenerator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createBitmapData(
|
||||||
|
tint: Int,
|
||||||
|
scale: Float,
|
||||||
|
alpha: Int,
|
||||||
|
highlight: Boolean,
|
||||||
|
fault: Boolean,
|
||||||
|
multi: Boolean,
|
||||||
|
fav: Boolean,
|
||||||
|
mini: Boolean
|
||||||
|
) = BitmapData(
|
||||||
|
tint,
|
||||||
|
(scale * (if (mini) scaleResolutionMini else scaleResolution)).roundToInt(),
|
||||||
|
alpha,
|
||||||
|
if (scale == 1f) highlight else false,
|
||||||
|
if (scale == 1f && !mini) fault else false,
|
||||||
|
if (!mini) multi else false,
|
||||||
|
if (scale == 1f && !mini) fav else false,
|
||||||
|
mini
|
||||||
|
)
|
||||||
|
|
||||||
fun getBitmap(
|
fun getBitmap(
|
||||||
@ColorRes tint: Int,
|
@ColorRes tint: Int,
|
||||||
scale: Float = 1f,
|
scale: Float = 1f,
|
||||||
@@ -131,38 +154,40 @@ class ChargerIconGenerator(
|
|||||||
highlight: Boolean = false,
|
highlight: Boolean = false,
|
||||||
fault: Boolean = false,
|
fault: Boolean = false,
|
||||||
multi: Boolean = false,
|
multi: Boolean = false,
|
||||||
fav: Boolean = false
|
fav: Boolean = false,
|
||||||
|
mini: Boolean = false
|
||||||
): Bitmap {
|
): Bitmap {
|
||||||
val data = BitmapData(
|
val data = createBitmapData(tint, scale, alpha, highlight, fault, multi, fav, mini)
|
||||||
tint, (scale * scaleResolution).roundToInt(),
|
|
||||||
alpha,
|
|
||||||
if (scale == 1f) highlight else false,
|
|
||||||
if (scale == 1f) fault else false,
|
|
||||||
multi,
|
|
||||||
if (scale == 1f) fav else false,
|
|
||||||
)
|
|
||||||
return generateBitmap(data)
|
return generateBitmap(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateBitmap(data: BitmapData): Bitmap {
|
private fun generateBitmap(data: BitmapData): Bitmap {
|
||||||
val icon = if (data.multi) multiIcon else icon
|
val icon = if (data.mini) miniIcon else if (data.multi) multiIcon else icon
|
||||||
val vd: Drawable = ContextCompat.getDrawable(context, icon)!!
|
val vd: Drawable = ContextCompat.getDrawable(context, icon)!!
|
||||||
|
|
||||||
DrawableCompat.setTint(vd, ContextCompat.getColor(context, data.tint));
|
DrawableCompat.setTint(vd, ContextCompat.getColor(context, data.tint))
|
||||||
DrawableCompat.setTintMode(vd, PorterDuff.Mode.MULTIPLY);
|
DrawableCompat.setTintMode(vd, PorterDuff.Mode.MULTIPLY)
|
||||||
|
|
||||||
val density = context.resources.displayMetrics.density
|
val density = context.resources.displayMetrics.density
|
||||||
val markerWidth =
|
val (markerWidth, markerHeight) = if (data.mini) {
|
||||||
(height.toFloat() * density / vd.intrinsicHeight * vd.intrinsicWidth).roundToInt()
|
vd.intrinsicWidth to vd.intrinsicHeight
|
||||||
val markerHeight = (height * density).roundToInt()
|
} else {
|
||||||
val extraIconSize = (0.75 * markerWidth).roundToInt()
|
(height.toFloat() * density / vd.intrinsicHeight * vd.intrinsicWidth).roundToInt() to
|
||||||
val extraIconShift = (0.25 * markerWidth).roundToInt()
|
(height * density).roundToInt()
|
||||||
|
}
|
||||||
|
val (extraIconSize, extraIconShift) = if (data.mini) 0 to 0 else {
|
||||||
|
(0.75 * markerWidth).roundToInt() to (0.25 * markerWidth).roundToInt()
|
||||||
|
}
|
||||||
|
|
||||||
val totalWidth = markerWidth + 2 * extraIconShift
|
val totalWidth = markerWidth + 2 * extraIconShift
|
||||||
val totalHeight = markerHeight + extraIconShift
|
val totalHeight = markerHeight + extraIconShift
|
||||||
|
|
||||||
val leftPadding = ((totalWidth) * (oversize - 1) / 2).roundToInt() + extraIconShift
|
val (leftPadding, topPadding) = if (!data.mini) {
|
||||||
val topPadding = ((totalHeight) * (oversize - 1)).roundToInt() + extraIconShift
|
((totalWidth) * (oversize - 1) / 2).roundToInt() + extraIconShift to
|
||||||
|
((totalHeight) * (oversize - 1)).roundToInt() + extraIconShift
|
||||||
|
} else {
|
||||||
|
0 to 0
|
||||||
|
}
|
||||||
vd.setBounds(
|
vd.setBounds(
|
||||||
leftPadding, topPadding,
|
leftPadding, topPadding,
|
||||||
leftPadding + markerWidth,
|
leftPadding + markerWidth,
|
||||||
@@ -176,18 +201,21 @@ class ChargerIconGenerator(
|
|||||||
)
|
)
|
||||||
val canvas = Canvas(bm)
|
val canvas = Canvas(bm)
|
||||||
|
|
||||||
val scale = data.scale.toFloat() / scaleResolution
|
val scale = data.scale.toFloat() / if (data.mini) scaleResolutionMini else scaleResolution
|
||||||
canvas.scale(
|
val (originX, originY) = if (data.mini) {
|
||||||
scale,
|
canvas.width / 2f to
|
||||||
scale,
|
canvas.height / 2f
|
||||||
canvas.width / 2f,
|
} else {
|
||||||
canvas.height.toFloat()
|
canvas.width / 2f to
|
||||||
)
|
canvas.height.toFloat()
|
||||||
|
}
|
||||||
|
canvas.scale(scale, scale, originX, originY)
|
||||||
|
|
||||||
vd.draw(canvas)
|
vd.draw(canvas)
|
||||||
|
|
||||||
if (data.highlight) {
|
if (data.highlight) {
|
||||||
val hIcon = if (data.multi) highlightIconMulti else highlightIcon
|
val hIcon =
|
||||||
|
if (data.mini) highlightIconMini else if (data.multi) highlightIconMulti else highlightIcon
|
||||||
val highlightDrawable = ContextCompat.getDrawable(context, hIcon)!!
|
val highlightDrawable = ContextCompat.getDrawable(context, hIcon)!!
|
||||||
highlightDrawable.setBounds(
|
highlightDrawable.setBounds(
|
||||||
leftPadding, topPadding,
|
leftPadding, topPadding,
|
||||||
@@ -198,7 +226,7 @@ class ChargerIconGenerator(
|
|||||||
highlightDrawable.draw(canvas)
|
highlightDrawable.draw(canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.fault) {
|
if (data.fault && !data.mini) {
|
||||||
val faultDrawable = ContextCompat.getDrawable(context, faultIcon)!!
|
val faultDrawable = ContextCompat.getDrawable(context, faultIcon)!!
|
||||||
faultDrawable.setBounds(
|
faultDrawable.setBounds(
|
||||||
leftPadding + markerWidth + extraIconShift - extraIconSize,
|
leftPadding + markerWidth + extraIconShift - extraIconSize,
|
||||||
@@ -210,7 +238,7 @@ class ChargerIconGenerator(
|
|||||||
faultDrawable.draw(canvas)
|
faultDrawable.draw(canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.fav) {
|
if (data.fav && !data.mini) {
|
||||||
val favDrawable = ContextCompat.getDrawable(context, favIcon)!!
|
val favDrawable = ContextCompat.getDrawable(context, favIcon)!!
|
||||||
val favShiftY = extraIconShift
|
val favShiftY = extraIconShift
|
||||||
val favShiftX = if (data.fault) extraIconShift - extraIconSize else extraIconShift
|
val favShiftX = if (data.fault) extraIconShift - extraIconSize else extraIconShift
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ class MarkerAnimator(val gen: ChargerIconGenerator) {
|
|||||||
highlight: Boolean,
|
highlight: Boolean,
|
||||||
fault: Boolean,
|
fault: Boolean,
|
||||||
multi: Boolean,
|
multi: Boolean,
|
||||||
fav: Boolean
|
fav: Boolean,
|
||||||
|
mini: Boolean
|
||||||
) {
|
) {
|
||||||
animatingMarkers[marker]?.let {
|
animatingMarkers[marker]?.let {
|
||||||
it.cancel()
|
it.cancel()
|
||||||
@@ -57,7 +58,8 @@ class MarkerAnimator(val gen: ChargerIconGenerator) {
|
|||||||
highlight = highlight,
|
highlight = highlight,
|
||||||
fault = fault,
|
fault = fault,
|
||||||
multi = multi,
|
multi = multi,
|
||||||
fav = fav
|
fav = fav,
|
||||||
|
mini = mini
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -77,7 +79,8 @@ class MarkerAnimator(val gen: ChargerIconGenerator) {
|
|||||||
highlight: Boolean,
|
highlight: Boolean,
|
||||||
fault: Boolean,
|
fault: Boolean,
|
||||||
multi: Boolean,
|
multi: Boolean,
|
||||||
fav: Boolean
|
fav: Boolean,
|
||||||
|
mini: Boolean
|
||||||
) {
|
) {
|
||||||
animatingMarkers[marker]?.let {
|
animatingMarkers[marker]?.let {
|
||||||
it.cancel()
|
it.cancel()
|
||||||
@@ -96,7 +99,8 @@ class MarkerAnimator(val gen: ChargerIconGenerator) {
|
|||||||
highlight = highlight,
|
highlight = highlight,
|
||||||
fault = fault,
|
fault = fault,
|
||||||
multi = multi,
|
multi = multi,
|
||||||
fav = fav
|
fav = fav,
|
||||||
|
mini = mini
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -120,7 +124,7 @@ class MarkerAnimator(val gen: ChargerIconGenerator) {
|
|||||||
marker.remove()
|
marker.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun animateMarkerBounce(marker: Marker) {
|
fun animateMarkerBounce(marker: Marker, mini: Boolean) {
|
||||||
animatingMarkers[marker]?.let {
|
animatingMarkers[marker]?.let {
|
||||||
it.cancel()
|
it.cancel()
|
||||||
animatingMarkers.remove(marker)
|
animatingMarkers.remove(marker)
|
||||||
@@ -131,7 +135,7 @@ class MarkerAnimator(val gen: ChargerIconGenerator) {
|
|||||||
interpolator = BounceInterpolator()
|
interpolator = BounceInterpolator()
|
||||||
addUpdateListener { state ->
|
addUpdateListener { state ->
|
||||||
val t = max(1f - state.animatedValue as Float, 0f) / 2
|
val t = max(1f - state.animatedValue as Float, 0f) / 2
|
||||||
marker.setAnchor(0.5f, 1.0f + t)
|
marker.setAnchor(0.5f, (if (mini) 0.5f else 1.0f) + t)
|
||||||
}
|
}
|
||||||
addListener(onEnd = {
|
addListener(onEnd = {
|
||||||
animatingMarkers.remove(marker)
|
animatingMarkers.remove(marker)
|
||||||
|
|||||||
@@ -35,9 +35,7 @@ data class MapPosition(val bounds: LatLngBounds, val zoom: Float) : Parcelable
|
|||||||
internal fun getClusterDistance(zoom: Float): Int? {
|
internal fun getClusterDistance(zoom: Float): Int? {
|
||||||
return when (zoom) {
|
return when (zoom) {
|
||||||
in 0.0..7.0 -> 100
|
in 0.0..7.0 -> 100
|
||||||
in 7.0..11.5 -> 75
|
in 7.0..11.0 -> 75
|
||||||
in 11.5..12.5 -> 60
|
|
||||||
in 12.5..13.0 -> 45
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -298,6 +296,29 @@ class MapViewModel(application: Application, private val state: SavedStateHandle
|
|||||||
chargepointLoader(Triple(pos, filters, referenceData))
|
chargepointLoader(Triple(pos, filters, referenceData))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val miniMarkerThreshold = 13f
|
||||||
|
private val clusterThreshold = 11f
|
||||||
|
val useMiniMarkers: LiveData<Boolean> = MediatorLiveData<Boolean>().apply {
|
||||||
|
for (source in listOf(filteredMinPower, mapPosition)) {
|
||||||
|
addSource(source) {
|
||||||
|
val minPower = filteredMinPower.value ?: 0
|
||||||
|
val zoom = mapPosition.value?.zoom
|
||||||
|
value = when {
|
||||||
|
zoom == null -> {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
minPower >= 100 -> {
|
||||||
|
// when only showing high-power chargers we can use large markers
|
||||||
|
zoom < clusterThreshold
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
zoom < miniMarkerThreshold
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.distinctUntilChanged()
|
||||||
|
|
||||||
private var chargepointLoader =
|
private var chargepointLoader =
|
||||||
throttleLatest(
|
throttleLatest(
|
||||||
500L,
|
500L,
|
||||||
@@ -342,7 +363,7 @@ class MapViewModel(application: Application, private val state: SavedStateHandle
|
|||||||
if (connectorsVal.all) null else connectorsVal.values.map {
|
if (connectorsVal.all) null else connectorsVal.values.map {
|
||||||
GEChargepoint.convertTypeFromGE(it)
|
GEChargepoint.convertTypeFromGE(it)
|
||||||
}.toSet()
|
}.toSet()
|
||||||
filteredMinPower.value = filters.getSliderValue("minPower")
|
filteredMinPower.value = filters.getSliderValue("min_power")
|
||||||
} else if (api is OpenChargeMapApiWrapper) {
|
} else if (api is OpenChargeMapApiWrapper) {
|
||||||
val connectorsVal = filters.getMultipleChoiceValue("connectors")!!
|
val connectorsVal = filters.getMultipleChoiceValue("connectors")!!
|
||||||
filteredConnectors.value =
|
filteredConnectors.value =
|
||||||
@@ -352,7 +373,7 @@ class MapViewModel(application: Application, private val state: SavedStateHandle
|
|||||||
refData as OCMReferenceData
|
refData as OCMReferenceData
|
||||||
)
|
)
|
||||||
}.toSet()
|
}.toSet()
|
||||||
filteredMinPower.value = filters.getSliderValue("minPower")
|
filteredMinPower.value = filters.getSliderValue("min_power")
|
||||||
} else {
|
} else {
|
||||||
filteredConnectors.value = null
|
filteredConnectors.value = null
|
||||||
filteredMinPower.value = null
|
filteredMinPower.value = null
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="16dp"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,12m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0" />
|
||||||
|
</vector>
|
||||||
12
app/src/main/res/drawable/ic_map_marker_charging_mini.xml
Normal file
12
app/src/main/res/drawable/ic_map_marker_charging_mini.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<vector android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="16dp"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path
|
||||||
|
android:fillColor="#dddddd"
|
||||||
|
android:pathData="M12,12m-8.5,0a8.5,8.5 0,1 1,17 0a8.5,8.5 0,1 1,-17 0" />
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,12m-7.5,0a7.5,7.5 0,1 1,15 0a7.5,7.5 0,1 1,-15 0" />
|
||||||
|
</vector>
|
||||||
Reference in New Issue
Block a user