กล้อง — ส่วนที่ยากที่สุดของการนำมาใช้ซ้ำ
ใน MapConductor การรวมกล้องเป็นสิ่งที่ยากที่สุดและมีคุณค่ามากที่สุด รุ่นกล้องหนึ่งที่ประกอบด้วย position, zoom, bearing, tilt และ visibleRegion ถูกทำให้สอดคล้องกันในความหมายเดียวกันในมากกว่า 10 รุ่นของเครื่องมือแผนที่ หากส่ง MapCameraPosition เดียวกัน ผู้ให้บริการรายใดก็ตามจะแสดงภาพเดียวกัน
01 · ทำไมการรวมจึงมีความหมาย
แต่ละเครื่องมือแผนที่มีรุ่นกล้องของตัวเอง บางรายพูดถึงการซูม บางรายพูดถึงความสูงของกล้อง บางรายหมุนรอบจุดโฟกัสพื้นดิน และบางรายเคลื่อนย้ายจุดมองเอง หากใช้ SDK เนทีฟโดยตรง เมื่อเปลี่ยนผู้ให้บริการจะทำให้เกิด "ตัวเลขเดียวกันแต่ภาพต่างกัน" ทันที และคุณจะต้องเขียนตรรกะหน้าจอใหม่ทั้งหมด
MapConductor กักเก็บความแตกต่างนี้ไว้ในเลเยอร์อะแดปเตอร์ และแสดงเพียงรุ่นกล้องเดียวให้แอปเห็น ผลลัพธ์คือตำแหน่งกล้องจะกลายเป็นค่าที่สามารถถ่ายทอดข้ามผู้ให้บริการได้ ในหน้า Camera Sync ของแอปตัวอย่าง คุณสามารถตรวจสอบได้จริงว่าตำแหน่งกล้องหนึ่งถูกส่งไปยังผู้ให้บริการทุกราย และจัดเรียงในตำแหน่ง สเกล และความเอียงเดียวกัน
ซูมหรือระดับความสูง
ระบบไทล์ 2D แสดงสเกลด้วยระดับซูม ส่วนระบบ 3D ใช้ความสูงของกล้องหรือระยะการมองเห็น หน่วยและเกณฑ์ก็แตกต่างกันด้วย
จุดโฟกัสหรือจุดวิว
มีทั้งแบบจำลองที่หมุนรอบจุดเดียวบนพื้นผิวโลก (โอร์บิต) และแบบจำลองที่อธิบายด้วยตำแหน่งและทิศทางของกล้องเองผสมกันอยู่
ช่วงที่อนุญาตของการเอียง
มุมเอียงสูงสุดและการเอียงขึ้นด้านบนได้หรือไม่ก็แตกต่างกันไปในแต่ละเอ็นจิ้น ไม่มีเอ็นจิ้นใดที่มีการเอียงแบบลบ
val camera = MapCameraPosition(
position = geoPoint,
zoom = 18.0,
tilt = 70.0,
)
moveCameraTo(camera)02 · รุ่นกล้องที่รวมกัน
กล้องแสดงด้วย MapCameraPosition เดียว ไม่มีประเภทเฉพาะสำหรับผู้ให้บริการ คุณเพียงสร้างค่านี้และส่งไปที่ moveCameraTo
สามารถแทนที่เฉพาะส่วนด้วย copy() และการเปรียบเทียบจะดำเนินการโดยมีความคลาดเคลื่อนอนุญาตของการซูม 0.01 และความเอียง 0.01 เพื่อป้องกันการตัดสินผิดว่า "เคลื่อนที่" เนื่องจากความแตกต่างเล็กน้อยของค่าที่ส่งคืนจากผู้ให้บริการ
03 · การรวมระดับการซูม = ความสูง
ภายในระบบ ซูมจะถูกจัดการเหมือนกับ “ความสูงของกล้อง” ใน `core` มีตัวแปลงที่สามารถแปลงซูมของไทล์ 2D และความสูง/ระยะมุมมอง 3D ซึ่งกันและกัน ค่าจากทุกผู้ให้บริการจะถูกแปลงเป็นซูมร่วม (มาตรฐาน Google Maps) ก่อนที่จะเผยแพร่
มาตรฐานนี้ไม่ได้ถูกกำหนดด้วยเหตุผลเพียงอย่างเดียว ด้วยเครื่องมือสอบเทียบในแอปตัวอย่าง พื้นที่ที่มองเห็นได้จริงสำหรับแต่ละเอ็นจิ้นถูกวัดเมื่อระบุซูมเดียวกัน และความสูงมาตรฐานถูกสอบเทียบให้พื้นที่ตรงกัน ดังนั้น ซูม 14 จึงมีความกว้างเท่ากันในทุกผู้ให้บริการ
ซูมระดับเดียวกันต้องใช้ความสูงต่างกันตามละติจูด
Web Mercator ขยายแผนที่มากขึ้นเมื่อเข้าใกล้ขั้วโลก ความสูงกล้องสำหรับซูมระดับเดียวกันจึงต่างกันหลายเท่าระหว่างเส้นศูนย์สูตรกับละติจูดสูง การแปลงซูม ⇄ ความสูงคำนวณเรื่องนี้ไว้แล้ว
สิ่งที่เป็นค่าคงที่คือความสัมพันธ์ “หนึ่งขั้นต่อการเพิ่มเป็นสองเท่า” และความสูงของซูม 0 เป็นค่าอ้างอิง การแปลงยังรวมถึงข้อเท็จจริงที่ว่าระยะทางบนพื้นดินที่ซูมเดียวกันจะลดลงตามละติจูดที่สูงขึ้น ดังนั้นสเกลจึงไม่คลาดเคลื่อนระหว่างโตเกียวและโฮโนลูลู
04 · การรองรับจำลองสำหรับ tilt < 0
tilt รับค่าตั้งแต่ −60 ถึง 60 องศา ค่าบวกคือมุมมองเฉียงลงมาตามปกติ ส่วนค่าลบคือมุมมองยกขึ้นดูเหนือขอบฟ้า ไม่มีเอ็นจิ้นแผนที่ตัวใดสามารถแสดงนี้ได้โดยตรง ทุก SDK ยอมรับเฉพาะ pitch ≥ 0 เท่านั้น
MapConductor จึงแทนที่สิ่งนี้โดยรักษาตำแหน่งและทิศทางของกล้องไว้ แต่ส่งค่าความเอียงเป็นบวกให้กับเอ็นจิ้น จากแอปสามารถใช้ tilt ติดลบได้ตามปกติ และตำแหน่งกล้องที่ได้รับก็จะถูกส่งกลับมาเป็น tilt ติดลบเช่นกัน
แผนผังที่มองจากด้านบนโดยตรง สี่เหลี่ยมผืนผ้าบนพื้นผิวโลกจะกลายเป็นสี่เหลี่ยมผืนผ้าบนหน้าจอโดยตรง
มุมมองแนวเอียงไปทางด้านหน้า บนพื้นผิวโลกจะปรากฏเป็นสี่เหลี่ยมด้านขนานที่ด้านใกล้แคบและด้านไกลกว้าง
มุมมองยกขึ้นเหนือขอบฟ้า มุมมองที่รวมท้องฟ้าเข้าไปด้วย ซึ่งไม่มีอยู่โดยพื้นฐาน
ข้อยกเว้นเดียวคือ Google Maps 2D บน React ซึ่งเป็นแผนที่แบบราสเตอร์ของ JavaScript API มุมเอียงขึ้นกับเบราว์เซอร์และไทล์ จึงควบคุมความสูงและทิศทางที่การจำลองต้องใช้ไม่ได้ ส่วน Google Maps 3D (Map3DElement) รองรับ tilt ค่าติดลบ
// ส่งค่าสไลเดอร์ −60 ถึง 60 ไปตรง ๆ ค่าติดลบก็เรียกแบบเดียวกัน currentPosition = currentPosition.copy(tilt = tilt) mapViewState.value?.moveCameraTo(currentPosition)
func setTilt(_ angle: Double, state: any MapViewStateProtocol) {
currentPosition = currentPosition.copy(tilt: angle)
state.moveCameraTo(cameraPosition: currentPosition)
}<SliderControl
label="Tilt" value={tilt} min={-60} max={60} step={1}
onChange={value => {
setTilt(value);
const next = cameraPositionRef.current.copy({ tilt: value });
cameraPositionRef.current = next;
mapViewState.moveCameraTo(next, 400);
}}
/>05 · VisibleRegion — พื้นที่ที่มองเห็นได้จริง
เมื่อกล้องเอียง สี่เหลี่ยมจัตุรัสของหน้าจอจะกลายเป็นสี่เหลี่ยมคางหมูบนพื้นดิน สี่เหลี่ยมผืนผ้าที่มีเพียงตะวันออกเฉียงเหนือและตะวันตกเฉียงใต้ (bounds) ไม่สามารถแสดงความแตกต่างของพื้นที่ระหว่างพื้นที่ใกล้และไกลได้ VisibleRegion มีพิกัดพื้นดินของสี่มุมหน้าจอนอกเหนือจากสี่เหลี่ยมผืนผ้า ทำให้สามารถจัดการ “พื้นที่ที่แสดงจริง” ได้แม้กล้องจะเอียง
near คือขอบล่างของหน้าจอ นั่นคือด้านที่ใกล้กล้อง ส่วน far คือขอบบนซึ่งเป็นด้านที่ไกลกว่า left / right อิงตามหน้าจอ ดังนั้นความหมายจะไม่เปลี่ยนแม้จะหมุนแผนที่ด้วย bearing ด้วยจุดทั้งสี่นี้ ทำให้สามารถกำหนดช่วงการรับข้อมูลและการระบุแหล่งที่มาได้อย่างถูกต้องแม้แผนที่จะเอียง
mapViewState.cameraPosition.visibleRegion?.let { region ->
val sw = region.bounds.southWest
val ne = region.bounds.northEast
// สี่มุมของหน้าจอที่เอียง ใช้เป็นรูปสี่เหลี่ยมคางหมูได้
val corners = listOf(region.nearLeft, region.nearRight, region.farLeft, region.farRight)
}if let region = cameraPosition.visibleRegion {
let bounds = region.bounds
let corners = [region.nearLeft, region.nearRight, region.farLeft, region.farRight]
}<MapViewContainer initialCamera={INIT_CAMERA} onCameraMove={setCameraPosition}>
{/* แสดงเนื้อหาของ cameraPosition.visibleRegion เรียงกัน */}
</MapViewContainer>
const visibleRegion = cameraPosition?.visibleRegion ?? null;
visibleRegion?.nearLeft?.toUrlValue(5);
visibleRegion?.farRight?.toUrlValue(5);