Polygon
An area defined by an outer ring plus any number of holes. Add a fill and a stroke, place it in the map view, and you have area highlighting or a mask. Holes are where map SDKs get quirky — MapConductor makes the result identical on every provider.
01 · Polygon state
PolygonState represents one area: outer ring, holes, fill, stroke and a tap handler. Assign to a property and the shape updates in place, so vertex-dragging editors are just coordinate swaps.
val polygonState =
PolygonState(
points = outerRing,
holes = listOf(holeA, holeB),
strokeColor = Color.Red,
strokeWidth = 3.dp,
fillColor = Color(0x4D0064E6),
onClick = { event -> viewModel.onPolygonClick(event) },
)
Polygon(polygonState)
// Vertices and holes update by simply being rewritten
polygonState.holes = nextHoleslet polygonState = PolygonState(
points: outerRing,
strokeColor: .red,
strokeWidth: 3.0,
fillColor: UIColor(red: 0, green: 0.39, blue: 0.9, alpha: 0.3),
holes: [holeA, holeB],
onClick: { event in viewModel.onPolygonClicked(event) }
)
content.polygons = [Polygon(state: polygonState)]const polygonState = createPolygonState({
id: 'demo-polygon',
points: vertices,
holes,
strokeColor: '#e74c3c',
strokeWidth,
fillColor: `rgba(0, 100, 230, ${fillOpacity})`,
onClick: () => showToast('Polygon clicked'),
});
<Polygon state={polygonState} />02 · Holes and merging overlaps
Holes cut regions out of the outer ring. In mask-style designs you often place several holes for the places you want to reveal — and those holes naturally end up overlapping.
Given overlapping holes, most map SDKs draw an outline where they overlap, exposing the seam between them. MapConductor unions the holes before drawing, so an overlap becomes a single hole and no extra lines appear.


// Overlapping holes can be passed as they are; they are merged into one internally
PolygonState(
points = outerRing,
holes =
listOf(
triangleA, // ← these overlap
triangleB,
),
fillColor = Color(0xCC787880),
)// Overlapping holes can be passed as they are; they are merged into one internally
let state = PolygonState(
points: outerRing,
fillColor: UIColor(white: 0.47, alpha: 0.8),
holes: [triangleA, triangleB]
)// Overlapping holes can be passed as they are; they are merged into one internally
const state = createPolygonState({
id: 'world-hole',
points: OUTER_POINTS,
holes, // Two triangles that overlap
fillColor: 'rgba(120, 120, 128, 0.8)',
strokeColor: '#ef4444',
strokeWidth: 2,
});03 · The same result on every provider
Beyond hole overlaps, filled areas expose plenty of per-engine differences. MapConductor absorbs them so one PolygonState gives one picture.
Overlapping holes are merged
Overlaps are unioned into a single region as the state is built — no outline across the overlap and no doubled fill.
The same shape without curve support
Engines that cannot draw great circles receive a densified ring instead, so wide areas match everywhere.
Areas crossing the date line
Rings crossing 180° are split at the boundary, so engines that would fill the long way round still fill the right side.
Shared tap detection
Tap detection is shared logic and holes do not respond, whether or not the underlying engine accounts for them.
04 · Tapping
onClick receives the polygon state and the tapped coordinate — read the fill or the extra payload to show details.
The tapped polygon state; read its fill colour or extra payload.
The tapped geographic coordinate.
Tapping inside a hole fires nothing for that polygon; only the visible fill counts.
fun onPolygonClicked(event: PolygonEvent) {
markerState = MarkerState(
position = event.clicked,
icon = DefaultMarkerIcon(fillColor = event.state.fillColor),
)
}func onPolygonClicked(_ event: PolygonEvent) {
let color = event.state.fillColor.withAlphaComponent(1.0)
markerState = MarkerState(
position: GeoPoint.from(position: event.clicked),
id: "clicked",
icon: DefaultMarkerIcon(fillColor: color)
)
}onClick: event => {
setMarker(createMarkerState({
id: 'clicked',
position: event.clicked,
icon: new ColorDefaultIcon({ fillColor: event.state.fillColor }),
}));
},Where polygons overlap, the higher zIndex wins. Polygons outside the visible region are skipped, so many wide areas stay responsive.