GroundImage ― 이미지를 지도에 붙이기
남서・북동의 2점으로 범위를 정하고, 거기에 한 장의 이미지를 깝니다. 시설의 층 도면, 고지도, 항공사진, 직접 만든 색분류도 등, 타일화되지 않은 이미지를 그대로 지도에 겹칠 수 있습니다. 범위나 투명도는 나중에 바꿔 쓸 수 있고, 이미지는 카메라 조작에 따라가며 지면에 붙어 있는 채로 표시됩니다.
01 · GroundImage의 상태
GroundImageState가 한 장의 이미지를 나타냅니다. 범위・이미지・투명도와, 탭 핸들러를 가집니다. 플랫폼마다 자연스러운 형태로 이미지를 넘길 수 있습니다(Android는 Drawable, iOS는 UIImage, Web은 URL).
val groundImageState =
GroundImageState(
id = "groundImage",
bounds =
GeoRectBounds(
southWest = southWestPosition,
northEast = northEastPosition,
),
image = drawable, // Drawable을 그대로 넘긴다
opacity = 0.5f,
onClick = this::onGroundImageClick,
)
GroundImage(groundImageState)let groundImageState = GroundImageState(
bounds: GeoRectBounds(southWest: southWest, northEast: northEast),
image: resources.image, // UIImage를 그대로 넘긴다
opacity: 0.5,
id: "groundImage"
)
content.groundImages = [GroundImage(state: groundImageState)]const image = createGroundImageState({
id: 'ground-image',
bounds: createGeoRectBounds({ southWest, northEast }),
imageUrl: GROUND_IMAGE_URL, // URL로 넘긴다
opacity,
onClick: () => showToast('Ground image clicked.'),
});
<GroundImage state={image} />02 · 범위와 투명도
범위는 사각형입니다. 이미지의 종횡비와 범위의 종횡비가 다른 경우에는 범위에 맞춰 늘어나므로, 붙이고 싶은 도면의 종횡비에 맞춘 범위를 지정하세요. 범위・투명도・이미지는 모두 나중에 바꿔 쓸 수 있고, 바꿔 쓴 시점에 렌더링에 반영됩니다.
드래그로 범위를 다시 만들기
2점의 마커를 드래그하면서 bounds를 교체하면, 이미지가 따라옵니다. 다만 연속 갱신의 추종성은 프로바이더에 따라 차이가 있습니다.
바탕 지도와 겹치기
투명도를 낮추면, 아래 지도의 도로나 지명이 비쳐 보입니다. 엔진에 따라서는 변경의 타이밍이나 듣는 방식에 제한이 있습니다.

// 모서리 마커를 드래그해 bounds를 다시 만든다
override fun onMarkerDrag(dragged: MarkerState) {
when (dragged.id) {
"south_west" -> southWestPosition = GeoPoint.from(dragged.position)
"north_east" -> northEastPosition = GeoPoint.from(dragged.position)
}
bounds = GeoRectBounds(southWest = southWestPosition, northEast = northEastPosition)
}
// 불투명도는 단순 대입
groundImageState.opacity = sliderOpacityfunc setOpacity(_ value: Double) {
opacity = value
groundImageState.opacity = value
}
// 마커를 드래그하는 동안 bounds를 다시 만든다
groundImageState.bounds = GeoRectBounds(southWest: sw, northEast: ne)// 드래그 중에는 다시 렌더링되기를 기다리지 말고 상태 객체에 직접 쓴다
const applyGeometry = (sw: GeoPoint, ne: GeoPoint) => {
image.bounds = createGeoRectBounds({ southWest: sw, northEast: ne });
polylineState.points = framePoints(sw, ne);
};
image.opacity = opacity;03 · 어느 프로바이더에서도 같게 보인다
GroundImage는 각 지도 SDK 드라이버 중에서 구현 차이가 가장 큰 기능입니다. 이미지 오버레이의 구조는 엔진마다 크게 다르고, 동등한 기능을 갖지 않는 것도 있습니다. MapConductor는 가능한 범위에서 이 차이를 흡수하지만, 프로바이더에 따라서는 동적인 변경에 제한이 남습니다.
같은 범위・같은 늘이기
이미지는 지정한 범위에 맞춰 배치됩니다. 엔진 쪽 이미지 오버레이의 좌표계 차이와 관계없이, 같은 위치・같은 크기로 표시됩니다.
먼저 네이티브의 기능으로 그린다
각 드라이버는 가능한 한 그 지도 SDK 고유의 이미지 오버레이 기능을 써서 최적으로 그립니다(ArcGIS라면 MediaLayer + ImageElement, Cesium이라면 Rectangle의 이미지 머티리얼 등). 아무래도 동등한 기능이 없는 엔진에 한해, 이미지를 잘라 타일로 공급하고 래스터 레이어로 겹칩니다. 어느 경로에서도 앱 쪽의 코드는 달라지지 않습니다.
프로바이더에 따른 제한
범위의 재구성(드래그 중의 연속 갱신)이나 투명도의 변경이, 엔진 쪽 제약으로 제한적이 되는 경우가 있습니다. 정적인 배치는 어느 프로바이더에서도 같지만, 동적으로 바꾸는 용도에서는 대상 프로바이더에서의 확인을 권합니다.
탭 판정도 공통
탭 판정은 공통 로직으로 하기 때문에, 엔진이 이미지 오버레이의 탭을 지원하는지 여부와 관계없이 onClick이 호출됩니다.
04 · 탭
onClick은 이미지의 상태와, 탭된 좌표를 받습니다. 샘플에서는 탭으로 이미지 자체를 교체해 표시를 전환합니다.
// 탭하면 이미지를 바꾼다
override fun onGroundImageClick(clicked: GroundImageEvent) {
val isBase = clicked.state.image == imageResources.image
image = if (isBase) imageResources.clickedImage else imageResources.image
}private func onGroundImageClick(_ event: GroundImageEvent) {
let isBase = groundImageState.image === resources.image
groundImageState.image = isBase ? resources.clickedImage : resources.image
}createGroundImageState({
bounds,
imageUrl,
onClick: event => showToast(event.clicked),
});탭된 이미지의 상태. 현재의 이미지나 extra를 참조할 수 있습니다.
탭된 지리 좌표. 범위 안의 어디가 눌렸는지 알 수 있습니다.
이미지의 투명한 부분도 탭 범위에 포함됩니다. 투명 부분을 무반응으로 하고 싶은 경우에는, 범위를 이미지의 실체에 맞추거나 폴리곤을 병용하세요.