폴리라인
좌표의 나열과 선의 스타일을 가진 상태 객체를 하나 만들어 지도 뷰에 놓기만 하면 됩니다. 선의 모습・휘는 방식・탭 판정은 프로바이더마다 제각각이 되기 쉽지만, MapConductor는 어느 프로바이더에서도 같은 결과가 되도록 맞춰 놓았습니다.
01 · 폴리라인의 상태
PolylineState가 선 하나를 나타냅니다. 좌표열과 스타일, 그리고 탭 핸들러를 가집니다. 프로퍼티를 바꿔 쓰면 선은 그 자리에서 갱신되며, 경로의 형태가 바뀌는 앱에서도 다시 만들 필요가 없습니다.
val polylineState =
PolylineState(
points = routePoints,
strokeColor = Color.Red,
strokeWidth = 4.dp,
geodesic = true,
onClick = { event -> viewModel.onPolylineClick(event) },
)
// 지도 안에 놓기만 하면 된다
Polyline(polylineState)
// 형태를 바꾸는 것은 points를 다시 쓰는 것뿐
polylineState.points = nextPointslet polylineState = PolylineState(
points: routePoints,
strokeColor: .red,
strokeWidth: 4.0,
geodesic: true,
onClick: { event in viewModel.onPolylineClick(event) }
)
content.polylines = [Polyline(state: polylineState)]const polylineState = createPolylineState({
id: 'demo-polyline',
points,
strokeColor: '#e74c3c',
strokeWidth,
geodesic: true,
onClick: event => showToast(event.clicked),
});
<Polyline state={polylineState} />02 · 직선과 대권 코스
geodesic을 true로 하면, 두 점을 지구 위의 최단 경로(대권 코스)로 잇습니다. 장거리 비행 경로 등은 직선이 아니라 호를 그립니다. false일 때는 지도 투영 위의 직선입니다. 같은 좌표열로 양쪽을 나란히 그려 비교할 수 있습니다.
Polyline(polylineState) // geodesic = true(곡선)
Polyline(
polylineState.copy(
id = "${polylineState.id}-straight",
geodesic = false,
strokeColor = Color.Blue,
),
)content.polylines = [
Polyline(state: polylineState), // 곡선
Polyline(state: polylineState.copy(geodesic: false)) // 직선
]const straightPolyline = polyline.copy({
id: `${polyline.id}-straight`,
geodesic: false,
strokeColor: '#0000ff',
});
<Polyline state={polyline} />
<Polyline state={straightPolyline} />03 · 어느 프로바이더에서도 같게 보인다
지도 엔진에는 대권 코스를 그릴 수 있는 것과 없는 것, 선 굵기의 단위가 다른 것, 날짜변경선을 넘는 선의 취급이 다른 것이 섞여 있습니다. 순수한 SDK를 쓰면 같은 좌표열이라도 겉모습이 달라집니다.
MapConductor는 이 차이를 내부에서 흡수합니다. 앱 쪽은 PolylineState를 하나 넘기기만 하면, 어느 프로바이더에서도 같은 호・같은 굵기・같은 탭 범위가 됩니다.
곡선을 그릴 수 없는 엔진에도 같은 호를
대권 코스를 직접 그릴 수 없는 엔진에는, 선을 잘게 나눈 좌표열로 변환한 뒤 넘깁니다. 그 결과, 어느 엔진에서도 같은 호가 표시됩니다.
날짜변경선을 넘는 선
경도 180도를 넘는 선은, 그대로 넘기면 지구를 반대로 횡단해 버리는 엔진이 있습니다. 경계에서 선을 분할해 넘김으로써, 어느 엔진에서도 최단 쪽을 지나갑니다.
굵기의 기준을 맞추기
선의 굵기는 플랫폼의 논리 단위로 지정합니다. 단말의 화소 밀도나 엔진마다의 단위 차이를 내부에서 환산하기 때문에, 실기에서 보이는 굵기가 일치합니다.
탭 판정도 공통
탭 판정은 엔진에 맡기지 않고, 공통 로직으로 합니다. 손가락 크기만큼의 허용 범위를 줌에 맞춰 미터로 환산해 판정하기 때문에, 배율이 달라도 누르기 쉬운 정도가 변하지 않습니다. 곡선의 경우는 호를 따라 판정합니다.
04 · 탭
onClick은 선의 상태와, 선 위에서 탭된 위치를 받습니다. 탭 위치는 선에 가장 가까운 점으로 보정된 좌표이므로, 그 자리에 마커를 놓거나 거리를 계산할 수 있습니다.
탭된 선의 상태 그 자체. 색이나 extra에서 데이터를 꺼낼 수 있습니다.
선 위의 좌표. 손가락의 위치가 아니라, 선에 가장 가까운 점입니다.
손가락 크기만큼의 여유를 두고 판정합니다. 줌에 맞춰 미터로 환산됩니다.
// 탭한 위치에 마커를 놓는다
fun onPolylineClick(event: PolylineEvent) {
markers = markers + MarkerState(
position = event.clicked,
animation = MarkerAnimation.Drop,
icon = DefaultMarkerIcon(fillColor = event.state.strokeColor),
)
}func onPolylineClicked(_ event: PolylineEvent) {
markerState = MarkerState(
position: GeoPoint.from(position: event.clicked),
icon: DefaultMarkerIcon(fillColor: event.state.strokeColor)
)
}onClick: event => {
setClickMarkers(prev => [...prev, createMarkerState({
id: `polyline-click-${prev.length}`,
position: event.clicked,
animation: MarkerAnimation.Drop,
icon: new ColorDefaultIcon({ fillColor: event.state.strokeColor }),
})]);
},표시 범위 밖의 선분은 판정에서 제외되므로, 긴 경로를 여러 개 놓아도 반응이 둔해지지 않습니다. 여러 선이 겹쳐 있는 경우는 가장 가까운 선이 선택됩니다.