Skip to content

Commit

Permalink
clean up old zoom-to logic
Browse files Browse the repository at this point in the history
  • Loading branch information
westnordost committed Feb 22, 2024
1 parent 971b91a commit f4c8eef
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ class MainMapFragment : LocationAwareMapFragment(), ShowsGeometryMarkers {

pinsMapComponent = PinsMapComponent(ctrl)
selectedPinsMapComponent = SelectedPinsMapComponent(requireContext(), ctrl)
geometryMapComponent = FocusGeometryMapComponent(ctrl, mapboxMap)
geometryMapComponent = FocusGeometryMapComponent(ctrl)

questPinsManager = QuestPinsManager(ctrl, pinsMapComponent!!, questTypeOrderSource, questTypeRegistry, resources, visibleQuestsSource)
viewLifecycleOwner.lifecycle.addObserver(questPinsManager!!)
Expand Down Expand Up @@ -684,7 +684,7 @@ class MainMapFragment : LocationAwareMapFragment(), ShowsGeometryMarkers {
// geometrySource?.setGeoJson(thisIsNoFeature) // nullable, but crashes maplibre (native) if null. great.
geometrySource?.setGeoJson(FeatureCollection.fromFeatures(emptyList()))
focusedGeometrySource?.setGeoJson(FeatureCollection.fromFeatures(emptyList()))
// pinsLayer?.setFilter(Expression.gte(Expression.zoom(), 14f))
pinsLayer?.setFilter(Expression.gte(Expression.zoom(), 14f))
pinsDotLayer?.setFilter(Expression.gte(Expression.zoom(), 14f))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ open class MapFragment : Fragment() {

private val binding by viewBinding(FragmentMapBinding::bind)

// private val defaultCameraInterpolator = AccelerateDecelerateInterpolator()

protected var controller: KtMapController? = null
protected var mapboxMap : MapboxMap? = null
protected var sceneMapComponent: SceneMapComponent? = null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
package de.westnordost.streetcomplete.screens.main.map.components

import android.graphics.RectF
import com.mapbox.geojson.Feature
import com.mapbox.geojson.FeatureCollection
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory
import com.mapbox.mapboxsdk.geometry.LatLngBounds
import com.mapbox.mapboxsdk.maps.MapboxMap
import de.westnordost.streetcomplete.data.maptiles.toLatLng
import de.westnordost.streetcomplete.data.osm.geometry.ElementGeometry
import de.westnordost.streetcomplete.data.osm.geometry.ElementPointGeometry
import de.westnordost.streetcomplete.screens.MainActivity
import de.westnordost.streetcomplete.screens.main.map.MainMapFragment
import de.westnordost.streetcomplete.screens.main.map.maplibre.CameraPosition
import de.westnordost.streetcomplete.screens.main.map.maplibre.toLatLon
Expand All @@ -23,7 +17,7 @@ import kotlin.math.roundToInt
/** Display element geometry and enables focussing on given geometry. I.e. to highlight the geometry
* of the element a selected quest refers to. Also zooms to the element in question so that it is
* contained in the screen area */
class FocusGeometryMapComponent(private val ctrl: KtMapController, private val mapboxMap: MapboxMap) {
class FocusGeometryMapComponent(private val ctrl: KtMapController) {

private var previousCameraPosition: CameraPosition? = null

Expand All @@ -43,43 +37,23 @@ class FocusGeometryMapComponent(private val ctrl: KtMapController, private val m
}

@Synchronized fun beginFocusGeometry(g: ElementGeometry, offset: RectF) {
val pos = ctrl.getEnclosingCameraPosition(g.getBounds(), offset) ?: return
val currentPos = ctrl.cameraPosition
val targetZoom = min(pos.zoom.toFloat(), 20f)

// do not zoom in if the element is already nicely in the view
if (ctrl.screenAreaContains(g, RectF()) && targetZoom - currentPos.zoom < 2.5) return
val targetPos = ctrl.getEnclosingCameraPosition(g, offset) ?: return

if (previousCameraPosition == null) previousCameraPosition = currentPos
val currentPos = ctrl.cameraPosition
// limit max zoom to not zoom in to the max when zooming in on points;
// also zoom in a bit less to have a padding around the zoomed-in element
val targetZoom = min(targetPos.zoom - 0.5, 21.0)

val zoomTime = max(450, (abs(currentPos.zoom - targetZoom) * 300).roundToInt())
val zoomDiff = abs(currentPos.zoom - targetZoom)
val zoomTime = max(450, (zoomDiff * 300).roundToInt())

// todo: works, but seems needlessly complicated
// and still might have some issues
MainActivity.activity!!.runOnUiThread {
val bounds = LatLngBounds.fromLatLngs(listOf(g.getBounds().max.toLatLng(), g.getBounds().min.toLatLng()))
val c = MainMapFragment.mapboxMap!!.getCameraForLatLngBounds(
bounds,
arrayOf(
offset.left.toInt(),
offset.top.toInt(),
offset.right.toInt(),
offset.bottom.toInt()
).toIntArray()
)
c?.let {
if (g is ElementPointGeometry) {
ctrl.updateCameraPosition(zoomTime) {
zoom = targetZoom.toDouble()
position = it.target?.toLatLon()
}
} else {
// TODO
// above is nice for nodes, but actually it gets the wrong position (ignores padding)
MainMapFragment.mapboxMap!!.easeCamera(CameraUpdateFactory.newCameraPosition(it), zoomTime.toInt())
}
}
ctrl.updateCameraPosition(zoomTime) {
position = targetPos.position
zoom = targetZoom
padding = targetPos.padding
}

if (previousCameraPosition == null) previousCameraPosition = currentPos
}

@Synchronized fun clearFocusGeometry() {
Expand All @@ -95,8 +69,6 @@ class FocusGeometryMapComponent(private val ctrl: KtMapController, private val m
ctrl.updateCameraPosition(zoomTime) {
position = pos.position
zoom = pos.zoom
tilt = pos.tilt
rotation = pos.rotation
}
}
previousCameraPosition = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import de.westnordost.streetcomplete.screens.main.map.maplibre.toLatLon
import de.westnordost.streetcomplete.screens.main.map.maplibre.toMapLibreCameraPosition
import de.westnordost.streetcomplete.screens.main.map.maplibre.toMapLibreCameraPosition
import de.westnordost.streetcomplete.screens.main.map.maplibre.toMapLibreCameraUpdate
import de.westnordost.streetcomplete.screens.main.map.maplibre.toMapLibreGeometry
import de.westnordost.streetcomplete.util.math.centerPointOfPolyline
import de.westnordost.streetcomplete.util.math.distanceTo
import de.westnordost.streetcomplete.util.math.enclosingBoundingBox
Expand Down Expand Up @@ -162,28 +163,18 @@ class KtMapController(
return positions.enclosingBoundingBox()
}

fun getEnclosingCameraPosition(bounds: BoundingBox, padding: RectF): CameraPosition? {
val zoom = getMaxZoomThatContainsBounds(bounds, padding) ?: return null
val boundsCenter = listOf(bounds.min, bounds.max).centerPointOfPolyline()
val pos = getLatLonThatCentersLatLon(boundsCenter, padding, zoom) ?: return null
val camera = cameraPosition
return CameraPosition(pos, camera.rotation, camera.tilt, zoom.toDouble())
}

private fun getMaxZoomThatContainsBounds(bounds: BoundingBox, padding: RectF): Float? {
val screenBounds: BoundingBox = screenAreaToBoundingBox(padding) ?: return null
val currentZoom: Float = cameraPosition.zoom.toFloat()

val screenWidth = normalizeLongitude(screenBounds.max.longitude - screenBounds.min.longitude)
val screenHeight = screenBounds.max.latitude - screenBounds.min.latitude
val objectWidth = normalizeLongitude(bounds.max.longitude - bounds.min.longitude)
val objectHeight = bounds.max.latitude - bounds.min.latitude

val zoomDeltaX = log10(screenWidth / objectWidth) / log10(2.0)
val zoomDeltaY = log10(screenHeight / objectHeight) / log10(2.0)
val zoomDelta = min(zoomDeltaX, zoomDeltaY)
return max(1.0, min(currentZoom + zoomDelta, 20.0)).toFloat()
}
fun getEnclosingCameraPosition(geometry: ElementGeometry, padding: RectF): CameraPosition? =
mapboxMap.getCameraForGeometry(
geometry.toMapLibreGeometry(),
intArrayOf(
padding.left.toInt(),
padding.top.toInt(),
padding.right.toInt(),
padding.bottom.toInt()
),
mapboxMap.cameraPosition.bearing,
mapboxMap.cameraPosition.tilt
)?.toCameraPosition()

fun getLatLonThatCentersLatLon(position: LatLon, padding: RectF, zoom: Float = cameraPosition.zoom.toFloat()): LatLon? {
val w = mapboxMap.width
Expand Down Expand Up @@ -301,21 +292,6 @@ class KtMapController(
val bottom = screenPositionToLatLon(PointF(w / 2f, h * 1f)) ?: return null
return center.distanceTo(bottom)
}

fun screenAreaContains(g: ElementGeometry, offset: RectF): Boolean {
val p = PointF()
return when (g) {
is ElementPolylinesGeometry -> g.polylines
is ElementPolygonsGeometry -> g.polygons
else -> listOf(listOf(g.center))
}.flatten().all {
// latLonToScreenPosition(it, p, false) ??
p.x >= offset.left
&& p.x <= mapboxMap.width - offset.right
&& p.y >= offset.top
&& p.y <= mapboxMap.height - offset.bottom
}
}
}

//class LoadSceneException(message: String, val sceneUpdate: SceneUpdate) : RuntimeException(message)
Expand Down

0 comments on commit f4c8eef

Please sign in to comment.