GeoRectBounds
GeoRectBounds representa un área geográfica rectangular definida por dos esquinas: suroeste y noreste. Se utiliza para definir regiones de mapa, límites de imágenes de tipo ground image y para cálculos de viewport.
Constructor
class GeoRectBounds( southWest: GeoPoint? = null, northEast: GeoPoint? = null)Propiedades
Esquinas
southWest: GeoPoint?: esquina suroeste (inferior izquierda) del rectángulo.northEast: GeoPoint?: esquina noreste (superior derecha) del rectángulo.isEmpty: Boolean: devuelvetruesi los límites no están definidos.
Propiedades calculadas
center: GeoPoint?: punto central de los límites.toSpan(): GeoPoint?: devuelve el “span” (ancho/alto) como unGeoPointInterface.
Pruebas con puntos y límites
Contiene un punto
fun contains(point: GeoPointInterface): Boolean
// Usageval bounds = GeoRectBounds( southWest = GeoPoint.fromLatLong(37.7649, -122.4294), northEast = GeoPoint.fromLatLong(37.7849, -122.4094))
val testPoint = GeoPoint.fromLatLong(37.7749, -122.4194)val isInside = bounds.contains(testPoint)
println("Point is inside bounds: $isInside")Intersección de límites
fun intersects(other: GeoRectBounds): Boolean
// Usageval bounds1 = GeoRectBounds(/* ... */)val bounds2 = GeoRectBounds(/* ... */)
val doIntersect = bounds1.intersects(bounds2)println("Bounds intersect: $doIntersect")Ejemplos prácticos
Cálculo de límites de viewport
@Composablefun ViewportBoundsExample() { var viewportBounds by remember { mutableStateOf<GeoRectBounds?>(null) }
// Calcular el viewport a partir de los marcadores visibles val markers = remember { listOf( GeoPoint.fromLatLong(37.7749, -122.4194), GeoPoint.fromLatLong(37.7849, -122.4094), GeoPoint.fromLatLong(37.7649, -122.4294), GeoPoint.fromLatLong(37.7949, -122.3994) ) }
LaunchedEffect(markers) { val bounds = GeoRectBounds() markers.forEach { marker -> bounds.extend(marker) } viewportBounds = bounds }
// Sustituye MapView por el proveedor de mapas que prefieras, como GoogleMapView o MapboxMapView MapView(state = mapViewState) { // Show all markers markers.forEach { position -> Marker( position = position, icon = ColorDefaultIcon(fillColor = Color.Blue) ) }
// Show viewport bounds as a polygon viewportBounds?.let { bounds -> if (!bounds.isEmpty) { val sw = bounds.southWest!! val ne = bounds.northEast!!
val boundsPolygon = listOf( sw, GeoPoint.fromLatLong(sw.latitude, ne.longitude), ne, GeoPoint.fromLatLong(ne.latitude, sw.longitude), sw // Close the polygon )
Polygon( points = boundsPolygon, strokeColor = Color.Red, strokeWidth = 2.dp, fillColor = Color.Red.copy(alpha = 0.1f) ) } } }}Carga basada en límites
@Composablefun BoundsBasedLoadingExample() { var currentBounds by remember { mutableStateOf<GeoRectBounds?>(null) } var markersInBounds by remember { mutableStateOf<List<GeoPoint>>(emptyList()) }
// Simular todos los marcadores disponibles val allMarkers = remember { List(100) { i -> GeoPoint.fromLatLong( 37.7 + (i % 10) * 0.01, -122.5 + (i / 10) * 0.01 ) } }
// Filter markers based on current bounds LaunchedEffect(currentBounds) { markersInBounds = currentBounds?.let { bounds -> if (bounds.isEmpty) { emptyList() } else { allMarkers.filter { marker -> bounds.contains(marker) } } } ?: emptyList() }
Column { Text("Markers in bounds: ${markersInBounds.size}/${allMarkers.size}")
Button( onClick = { // Simular el establecimiento de los límites del viewport currentBounds = GeoRectBounds( southWest = GeoPoint.fromLatLong(37.7, -122.5), northEast = GeoPoint.fromLatLong(37.75, -122.45) ) } ) { Text("Set Viewport") }
// Sustituye MapView por el proveedor de mapas que prefieras, como GoogleMapView o MapboxMapView MapView(state = mapViewState) { // Show only markers within bounds markersInBounds.forEach { position -> Marker( position = position, icon = ColorDefaultIcon( fillColor = Color.Green, scale = 0.8f ) ) }
// Show current bounds currentBounds?.let { bounds -> if (!bounds.isEmpty) { val sw = bounds.southWest!! val ne = bounds.northEast!!
Polygon( points = listOf( sw, GeoPoint.fromLatLong(sw.latitude, ne.longitude), ne, GeoPoint.fromLatLong(ne.latitude, sw.longitude), sw ), strokeColor = Color.Blue, strokeWidth = 3.dp, fillColor = Color.Blue.copy(alpha = 0.1f) ) } } } }}Editor interactivo de límites
@Composablefun BoundsEditorExample() { var bounds by remember { mutableStateOf( GeoRectBounds( southWest = GeoPoint.fromLatLong(37.7649, -122.4294), northEast = GeoPoint.fromLatLong(37.7849, -122.4094) ) ) }
val onCornerDrag: (MarkerState) -> Unit = { markerState -> val newPosition = markerState.position when (markerState.extra) { "SW" -> { bounds = GeoRectBounds( southWest = newPosition as GeoPoint, northEast = bounds.northEast ) } "NE" -> { bounds = GeoRectBounds( southWest = bounds.southWest, northEast = newPosition as GeoPoint ) } } }
// Sustituye MapView por el proveedor de mapas que prefieras, como GoogleMapView o MapboxMapView MapView( state = mapViewState ) { // Bounds visualization if (!bounds.isEmpty) { val sw = bounds.southWest!! val ne = bounds.northEast!!
// Bounds rectangle Polygon( points = listOf( sw, GeoPoint.fromLatLong(sw.latitude, ne.longitude), ne, GeoPoint.fromLatLong(ne.latitude, sw.longitude), sw ), strokeColor = Color.Red, fillColor = Color.Red.copy(alpha = 0.2f) )
// Corner markers Marker( position = sw, icon = ColorDefaultIcon( fillColor = Color.Green, label = "SW" ), draggable = true, extra = "SW", onDrag = onCornerDrag )
Marker( position = ne, icon = ColorDefaultIcon( fillColor = Color.Red, label = "NE" ), draggable = true, extra = "NE", onDrag = onCornerDrag ) } }}Manejo especial
Línea internacional de cambio de fecha
GeoRectBounds maneja rectángulos que cruzan la línea internacional de cambio de fecha (longitud ±180°):
// Bounds crossing the date lineval pacificBounds = GeoRectBounds( southWest = GeoPoint.fromLatLong(20, 170), // West of date line northEast = GeoPoint.fromLatLong(40, -170) // East of date line)
val pointInPacific = GeoPoint.fromLatLong(30, 175)val containsPoint = pacificBounds.contains(pointInPacific) // trueManejo de límites vacíos
val emptyBounds = GeoRectBounds()
println(emptyBounds.isEmpty) // trueprintln(emptyBounds.center) // nullprintln(emptyBounds.toSpan()) // null
// Extending empty boundsemptyBounds.extend(GeoPoint.fromLatLong(37.7749, -122.4194))println(emptyBounds.isEmpty) // falseMétodos utilitarios
Representación de cadena
// For debuggingval bounds = GeoRectBounds( southWest = GeoPoint.fromLatLong(37.7649, -122.4294), northEast = GeoPoint.fromLatLong(37.7849, -122.4094))
println(bounds.toString())// Output: ((37.7649, -122.4294), (37.7849, -122.4094))Valor de URL
fun toUrlValue(precision: Int = 6): String
// Usage for API callsval urlString = bounds.toUrlValue() // "37.764900,-122.429400,37.784900,-122.409400"val preciseString = bounds.toUrlValue(precision = 8)Comparación de igualdad
fun equalsTo(other: GeoRectBounds): Boolean
// Usoval bounds1 = GeoRectBounds(/* ... */)val bounds2 = GeoRectBounds(/* ... */)
val areEqual = bounds1.equalsTo(bounds2)Ajustar la cámara a los límites
Para mover y hacer zoom de la cámara de modo que un GeoRectBounds sea completamente visible, usa fitBounds:
val bounds = GeoRectBounds( southWest = GeoPoint.fromLatLong(37.7649, -122.4294), northEast = GeoPoint.fromLatLong(37.7849, -122.4094))mapViewState.fitBounds(bounds, padding = 32)El parámetro padding agrega un margen en píxeles en todos los lados del espacio de pantalla. Cada proveedor calcula el nivel de zoom resultante de forma nativa, por lo que el zoom exacto puede diferir ligeramente entre proveedores.
Un patrón común es construir límites desde un conjunto de puntos y luego ajustar:
val bounds = GeoRectBounds()markers.forEach { bounds.extend(it.position) }if (!bounds.isEmpty) { mapViewState.fitBounds(bounds, padding = 48)}