문서 / 기본 / 폴리라인

폴리라인

좌표의 나열과 선의 스타일을 가진 상태 객체를 하나 만들어 지도 뷰에 놓기만 하면 됩니다. 선의 모습・휘는 방식・탭 판정은 프로바이더마다 제각각이 되기 쉽지만, MapConductor는 어느 프로바이더에서도 같은 결과가 되도록 맞춰 놓았습니다.

POINTS
GeoPoint[]
STROKE
color · width
GEODESIC
true / false
EVENT
onClick
플랫폼

01 · 폴리라인의 상태

PolylineState가 선 하나를 나타냅니다. 좌표열과 스타일, 그리고 탭 핸들러를 가집니다. 프로퍼티를 바꿔 쓰면 선은 그 자리에서 갱신되며, 경로의 형태가 바뀌는 앱에서도 다시 만들 필요가 없습니다.

PolylinePageViewModel.kt · Jetpack Compose
val polylineState =
    PolylineState(
        points = routePoints,
        strokeColor = Color.Red,
        strokeWidth = 4.dp,
        geodesic = true,
        onClick = { event -> viewModel.onPolylineClick(event) },
    )

// 지도 안에 놓기만 하면 된다
Polyline(polylineState)

// 형태를 바꾸는 것은 points를 다시 쓰는 것뿐
polylineState.points = nextPoints
프로퍼티
설명
points
선을 이루는 좌표의 나열. 바꿔 쓰면 형태가 변합니다.
strokeColor
선의 색. 반투명도 지정할 수 있습니다.
strokeWidth
선의 굵기. 플랫폼의 논리 단위로 지정하며, 단말의 해상도에 관계없이 같은 굵기로 보입니다.
geodesic
대권 코스(측지선)로 이을지 여부. 기본은 false.
zIndex
겹침 순서. 다른 오버레이와의 앞뒤 관계를 정합니다.
extra
임의의 부수 데이터. 탭 시에 받을 수 있습니다.
onClick
탭 핸들러. 선의 상태와 탭된 좌표를 받습니다.

02 · 직선과 대권 코스

geodesic을 true로 하면, 두 점을 지구 위의 최단 경로(대권 코스)로 잇습니다. 장거리 비행 경로 등은 직선이 아니라 호를 그립니다. false일 때는 지도 투영 위의 직선입니다. 같은 좌표열로 양쪽을 나란히 그려 비교할 수 있습니다.

geodesic = false
메르카토르 도법 위의 직선. 근거리에서는 차이가 거의 없지만, 장거리에서는 실제 최단 경로에서 벗어납니다.
geodesic = true
지구의 곡률을 따르는 최단 경로. 고위도 쪽으로 부푼 호로 그려집니다.
같은 좌표로 두 줄 그리기
Polyline(polylineState) // geodesic = true(곡선)
Polyline(
    polylineState.copy(
        id = "${polylineState.id}-straight",
        geodesic = false,
        strokeColor = Color.Blue,
    ),
)

03 · 어느 프로바이더에서도 같게 보인다

지도 엔진에는 대권 코스를 그릴 수 있는 것과 없는 것, 선 굵기의 단위가 다른 것, 날짜변경선을 넘는 선의 취급이 다른 것이 섞여 있습니다. 순수한 SDK를 쓰면 같은 좌표열이라도 겉모습이 달라집니다.

MapConductor는 이 차이를 내부에서 흡수합니다. 앱 쪽은 PolylineState를 하나 넘기기만 하면, 어느 프로바이더에서도 같은 호・같은 굵기・같은 탭 범위가 됩니다.

GEODESIC

곡선을 그릴 수 없는 엔진에도 같은 호를

대권 코스를 직접 그릴 수 없는 엔진에는, 선을 잘게 나눈 좌표열로 변환한 뒤 넘깁니다. 그 결과, 어느 엔진에서도 같은 호가 표시됩니다.

ANTIMERIDIAN

날짜변경선을 넘는 선

경도 180도를 넘는 선은, 그대로 넘기면 지구를 반대로 횡단해 버리는 엔진이 있습니다. 경계에서 선을 분할해 넘김으로써, 어느 엔진에서도 최단 쪽을 지나갑니다.

STROKE WIDTH

굵기의 기준을 맞추기

선의 굵기는 플랫폼의 논리 단위로 지정합니다. 단말의 화소 밀도나 엔진마다의 단위 차이를 내부에서 환산하기 때문에, 실기에서 보이는 굵기가 일치합니다.

HIT TEST

탭 판정도 공통

탭 판정은 엔진에 맡기지 않고, 공통 로직으로 합니다. 손가락 크기만큼의 허용 범위를 줌에 맞춰 미터로 환산해 판정하기 때문에, 배율이 달라도 누르기 쉬운 정도가 변하지 않습니다. 곡선의 경우는 호를 따라 판정합니다.

그림 · 날짜변경선을 넘을 때
180°
그대로 넘긴 경우. 경도 180도를 넘지 못하고 지구를 반대로 가로지르는 선이 되는 엔진이 있습니다.
180°
MapConductor 경유. 경도 180도에서 선을 나눠 넘기므로, 어느 엔진에서도 경계를 넘어 최단 쪽을 지나갑니다.
샘플 영상 · SwiftUI로 쓴 같은 코드를 Google Maps와 MapLibre에서

04 · 탭

onClick은 선의 상태와, 선 위에서 탭된 위치를 받습니다. 탭 위치는 선에 가장 가까운 점으로 보정된 좌표이므로, 그 자리에 마커를 놓거나 거리를 계산할 수 있습니다.

event.state

탭된 선의 상태 그 자체. 색이나 extra에서 데이터를 꺼낼 수 있습니다.

event.clicked

선 위의 좌표. 손가락의 위치가 아니라, 선에 가장 가까운 점입니다.

허용 범위

손가락 크기만큼의 여유를 두고 판정합니다. 줌에 맞춰 미터로 환산됩니다.

PolylineClickPageViewModel.kt
// 탭한 위치에 마커를 놓는다
fun onPolylineClick(event: PolylineEvent) {
    markers = markers + MarkerState(
        position = event.clicked,
        animation = MarkerAnimation.Drop,
        icon = DefaultMarkerIcon(fillColor = event.state.strokeColor),
    )
}

표시 범위 밖의 선분은 판정에서 제외되므로, 긴 경로를 여러 개 놓아도 반응이 둔해지지 않습니다. 여러 선이 겹쳐 있는 경우는 가장 가까운 선이 선택됩니다.

관련 페이지