GeoRectBounds
GeoRectBounds represents a rectangular geographic area defined by southwest and northeast corner points. It is used for defining map regions, ground image bounds, and viewport calculations.
Constructor
Section titled “Constructor”class GeoRectBounds( southWest: GeoPointImpl? = null, northEast: GeoPointImpl? = null)Properties
Section titled “Properties”Corner Points
Section titled “Corner Points”southWest: GeoPointImpl?: Southwest corner (bottom-left) of the rectanglenorthEast: GeoPointImpl?: Northeast corner (top-right) of the rectangleisEmpty: Boolean: Returnstrueif the bounds are not defined
Computed Properties
Section titled “Computed Properties”center: GeoPointImpl?: Center point of the boundstoSpan(): GeoPointImpl?: Returns the span (width/height) as a GeoPoint
Basic Usage
Section titled “Basic Usage”Creating Bounds
Section titled “Creating Bounds”// Define a rectangular areaval bounds = GeoRectBounds( southWest = GeoPointImpl.fromLatLong(37.7649, -122.4294), northeast = GeoPointImpl.fromLatLong(37.7849, -122.4094))
// Empty bounds (to be extended later)val bounds = GeoRectBounds()Using with GroundImage
Section titled “Using with GroundImage”@Composablefun GroundImageExample() { val imageBounds = GeoRectBounds( southWest = GeoPointImpl.fromLatLong(37.7649, -122.4294), northEast = GeoPointImpl.fromLatLong(37.7849, -122.4094) )
// Replace MapView with your chosen map provider, such as GoogleMapView, MapboxMapView MapView(state = mapViewState) { val context = LocalContext.current AppCompatResources.getDrawable(context, R.drawable.overlay_image)?.let { drawable -> GroundImage( bounds = imageBounds, image = drawable, opacity = 0.7f ) } }}Dynamic Bounds Construction
Section titled “Dynamic Bounds Construction”Extending Bounds
Section titled “Extending Bounds”The bounds can be dynamically extended to include new points:
fun extend(point: GeoPoint)
// Usageval bounds = GeoRectBounds() // Start empty
val points = listOf( GeoPointImpl.fromLatLong(37.7749, -122.4194), GeoPointImpl.fromLatLong(37.7849, -122.4094), GeoPointImpl.fromLatLong(37.7649, -122.4294))
points.forEach { point -> bounds.extend(point) // Bounds grow to include each point}
println("Final bounds: $bounds")Union of Bounds
Section titled “Union of Bounds”fun union(other: GeoRectBounds): GeoRectBounds
// Usageval bounds1 = GeoRectBounds( southWest = GeoPointImpl.fromLatLong(37.7649, -122.4294), northEast = GeoPointImpl.fromLatLong(37.7749, -122.4194))
val bounds2 = GeoRectBounds( southWest = GeoPointImpl.fromLatLong(37.7749, -122.4194), northEast = GeoPointImpl.fromLatLong(37.7849, -122.4094))
val combinedBounds = bounds1.union(bounds2) // Contains both areasPoint and Bounds Testing
Section titled “Point and Bounds Testing”Contains Point
Section titled “Contains Point”fun contains(point: GeoPoint): Boolean
// Usageval bounds = GeoRectBounds( southWest = GeoPointImpl.fromLatLong(37.7649, -122.4294), northEast = GeoPointImpl.fromLatLong(37.7849, -122.4094))
val testPoint = GeoPointImpl.fromLatLong(37.7749, -122.4194)val isInside = bounds.contains(testPoint)
println("Point is inside bounds: $isInside")Bounds Intersection
Section titled “Bounds Intersection”fun intersects(other: GeoRectBounds): Boolean
// Usageval bounds1 = GeoRectBounds(/* ... */)val bounds2 = GeoRectBounds(/* ... */)
val doIntersect = bounds1.intersects(bounds2)println("Bounds intersect: $doIntersect")Practical Examples
Section titled “Practical Examples”Viewport Bounds Calculation
Section titled “Viewport Bounds Calculation”@Composablefun ViewportBoundsExample() { var viewportBounds by remember { mutableStateOf<GeoRectBounds?>(null) }
// Simulate calculating viewport from visible markers val markers = remember { listOf( GeoPointImpl.fromLatLong(37.7749, -122.4194), GeoPointImpl.fromLatLong(37.7849, -122.4094), GeoPointImpl.fromLatLong(37.7649, -122.4294), GeoPointImpl.fromLatLong(37.7949, -122.3994) ) }
LaunchedEffect(markers) { val bounds = GeoRectBounds() markers.forEach { marker -> bounds.extend(marker) } viewportBounds = bounds }
// Replace MapView with your chosen map provider, such as GoogleMapView, MapboxMapView MapView(state = mapViewState) { // Show all markers markers.forEach { position -> Marker( position = position, icon = DefaultIcon(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, GeoPointImpl.fromLatLong(sw.latitude, ne.longitude), ne, GeoPointImpl.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) ) } } }}Bounds-based Loading
Section titled “Bounds-based Loading”@Composablefun BoundsBasedLoadingExample() { var currentBounds by remember { mutableStateOf<GeoRectBounds?>(null) } var markersInBounds by remember { mutableStateOf<List<GeoPointImpl>>(emptyList()) }
// Simulate all available markers val allMarkers = remember { List(100) { i -> GeoPointImpl.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 = { // Simulate setting viewport bounds currentBounds = GeoRectBounds( southWest = GeoPointImpl.fromLatLong(37.70, -122.50), northEast = GeoPointImpl.fromLatLong(37.75, -122.45) ) } ) { Text("Set Viewport") }
// Replace MapView with your chosen map provider, such as GoogleMapView, MapboxMapView MapView(state = mapViewState) { // Show only markers within bounds markersInBounds.forEach { position -> Marker( position = position, icon = DefaultIcon( 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, GeoPointImpl.fromLatLong(sw.latitude, ne.longitude), ne, GeoPointImpl.fromLatLong(ne.latitude, sw.longitude), sw ), strokeColor = Color.Blue, strokeWidth = 3.dp, fillColor = Color.Blue.copy(alpha = 0.1f) ) } } } }}Interactive Bounds Editor
Section titled “Interactive Bounds Editor”@Composablefun BoundsEditorExample() { var bounds by remember { mutableStateOf( GeoRectBounds( southWest = GeoPointImpl.fromLatLong(37.7649, -122.4294), northEast = GeoPointImpl.fromLatLong(37.7849, -122.4094) ) ) }
// Replace MapView with your chosen map provider, such as GoogleMapView, MapboxMapView MapView( state = mapViewState, onMarkerDrag = { markerState -> val newPosition = markerState.position when (markerState.extra) { "SW" -> { bounds = GeoRectBounds( southWest = newPosition as GeoPointImpl, northEast = bounds.northEast ) } "NE" -> { bounds = GeoRectBounds( southWest = bounds.southWest, northEast = newPosition as GeoPointImpl ) } } } ) { // Bounds visualization if (!bounds.isEmpty) { val sw = bounds.southWest!! val ne = bounds.northEast!!
// Bounds rectangle Polygon( points = listOf( sw, GeoPointImpl.fromLatLong(sw.latitude, ne.longitude), ne, GeoPointImpl.fromLatLong(ne.latitude, sw.longitude), sw ), strokeColor = Color.Red, fillColor = Color.Red.copy(alpha = 0.2f) )
// Corner markers Marker( position = sw, icon = DefaultIcon( fillColor = Color.Green, label = "SW" ), draggable = true, extra = "SW" )
Marker( position = ne, icon = DefaultIcon( fillColor = Color.Red, label = "NE" ), draggable = true, extra = "NE" ) } }}Special Handling
Section titled “Special Handling”International Date Line
Section titled “International Date Line”GeoRectBounds handles rectangles that cross the International Date Line (longitude ±180°):
// Bounds crossing the date lineval pacificBounds = GeoRectBounds( southWest = GeoPointImpl.fromLatLong(20.0, 170.0), // West of date line northEast = GeoPointImpl.fromLatLong(40.0, -170.0) // East of date line)
val pointInPacific = GeoPointImpl.fromLatLong(30.0, 175.0)val containsPoint = pacificBounds.contains(pointInPacific) // trueEmpty Bounds Handling
Section titled “Empty Bounds Handling”val emptyBounds = GeoRectBounds()
println(emptyBounds.isEmpty) // trueprintln(emptyBounds.center) // nullprintln(emptyBounds.toSpan()) // null
// Extending empty boundsemptyBounds.extend(GeoPointImpl.fromLatLong(37.7749, -122.4194))println(emptyBounds.isEmpty) // falseUtility Methods
Section titled “Utility Methods”String Representation
Section titled “String Representation”// For debuggingval bounds = GeoRectBounds( southWest = GeoPointImpl.fromLatLong(37.7649, -122.4294), northEast = GeoPointImpl.fromLatLong(37.7849, -122.4094))
println(bounds.toString())// Output: ((37.7649, -122.4294), (37.7849, -122.4094))URL Value
Section titled “URL Value”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)Equality Comparison
Section titled “Equality Comparison”fun equalsTo(other: GeoRectBounds): Boolean
// Usageval bounds1 = GeoRectBounds(/* ... */)val bounds2 = GeoRectBounds(/* ... */)
val areEqual = bounds1.equalsTo(bounds2)Best Practices
Section titled “Best Practices”- Coordinate Order: Always use southwest (bottom-left) and northeast (top-right) for consistency
- Empty Bounds: Check
isEmptybefore using bounds in calculations - Date Line Crossing: The class handles international date line crossing automatically
- Dynamic Construction: Use
extend()to build bounds from a collection of points - Performance: Cache bounds calculations for frequently accessed regions
- Validation: Ensure southwest is actually southwest of northeast
- Precision: Use appropriate precision for URL values based on your use case