@@ -256,6 +256,8 @@ private fun RenderResolved(node: ViewNode) {
256256
257257 " LinearGradient" -> RenderGradient (node)
258258
259+ " Map" -> RenderMap (node)
260+
259261 " ProgressView" -> {
260262 val value = node.double(" value" )
261263 if (value != null ) {
@@ -611,6 +613,55 @@ private fun RenderShape(node: ViewNode) {
611613 Box (modifier = node.composeModifiers().background(fill, shape))
612614}
613615
616+ // A schematic map: the visible region maps linearly onto the box, markers sit
617+ // at their proportional position, and the center coordinate is captioned.
618+ // Real tiles come from a registered composable (maps SDKs need an API key).
619+ @Composable
620+ private fun RenderMap (node : ViewNode ) {
621+ val centerLat = node.double(" centerLatitude" ) ? : 0.0
622+ val centerLon = node.double(" centerLongitude" ) ? : 0.0
623+ val spanLat = (node.double(" spanLatitude" ) ? : 1.0 ).coerceAtLeast(1e- 6 )
624+ val spanLon = (node.double(" spanLongitude" ) ? : 1.0 ).coerceAtLeast(1e- 6 )
625+ Box (
626+ modifier = node.composeModifiers()
627+ .fillMaxWidth()
628+ .height(220 .dp)
629+ .clip(RoundedCornerShape (8 .dp))
630+ .background(Color (0xFFDCE8DC .toInt()))
631+ .border(1 .dp, Color (0xFFB8CCB8 .toInt()), RoundedCornerShape (8 .dp)),
632+ ) {
633+ for (marker in node.children) {
634+ if (marker.type != " MapMarker" ) continue
635+ val lat = marker.double(" latitude" ) ? : continue
636+ val lon = marker.double(" longitude" ) ? : continue
637+ // normalized region position → alignment bias (north at the top)
638+ val fx = ((lon - (centerLon - spanLon / 2 )) / spanLon).coerceIn(0.0 , 1.0 )
639+ val fy = (((centerLat + spanLat / 2 ) - lat) / spanLat).coerceIn(0.0 , 1.0 )
640+ val tint = marker.long(" tint" )?.let { Color (it.toInt()) } ? : Color (0xFFD32F2F .toInt())
641+ Column (
642+ horizontalAlignment = Alignment .CenterHorizontally ,
643+ modifier = Modifier .align(
644+ androidx.compose.ui.BiasAlignment ((2 * fx - 1 ).toFloat(), (2 * fy - 1 ).toFloat())
645+ ),
646+ ) {
647+ Box (modifier = Modifier .size(12 .dp).clip(CircleShape ).background(tint))
648+ Text (marker.string(" title" ) ? : " " , fontSize = 11 .sp)
649+ }
650+ }
651+ Text (
652+ " ${formatCoordinate(centerLat)} , ${formatCoordinate(centerLon)} " ,
653+ fontSize = 11 .sp,
654+ color = Color (0xFF667066 .toInt()),
655+ modifier = Modifier .align(Alignment .BottomCenter ).padding(4 .dp),
656+ )
657+ }
658+ }
659+
660+ private fun formatCoordinate (value : Double ): String {
661+ val thousandths = kotlin.math.round(value * 1000 ) / 1000
662+ return thousandths.toString()
663+ }
664+
614665// Horizontal/vertical when the endpoints share an axis, else a diagonal linear
615666// gradient across the frame.
616667@Composable
0 commit comments