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)
// 頂点や穴は書き換えるだけで更新される
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.


// 重なり合う穴をそのまま渡してよい。内部で1つの穴に結合される
PolygonState(
points = outerRing,
holes =
listOf(
triangleA, // ← 互いに重なっている
triangleB,
),
fillColor = Color(0xCC787880),
)// 重なり合う穴をそのまま渡してよい。内部で1つの穴に結合される
let state = PolygonState(
points: outerRing,
fillColor: UIColor(white: 0.47, alpha: 0.8),
holes: [triangleA, triangleB]
)// 重なり合う穴をそのまま渡してよい。内部で1つの穴に結合される
const state = createPolygonState({
id: 'world-hole',
points: OUTER_POINTS,
holes, // 互いに重なる三角形2つ
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.
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.