เอกสาร / มุมมองแผนที่ / กล้อง

กล้อง — ส่วนที่ยากที่สุดของการนำมาใช้ซ้ำ

ใน MapConductor การรวมกล้องเป็นสิ่งที่ยากที่สุดและมีคุณค่ามากที่สุด รุ่นกล้องหนึ่งที่ประกอบด้วย position, zoom, bearing, tilt และ visibleRegion ถูกทำให้สอดคล้องกันในความหมายเดียวกันในมากกว่า 10 รุ่นของเครื่องมือแผนที่ หากส่ง MapCameraPosition เดียวกัน ผู้ให้บริการรายใดก็ตามจะแสดงภาพเดียวกัน

POSITION
lat / lng
ZOOM
0 – 22
BEARING
0 – 360°
TILT
−60 – 60°

01 · ทำไมการรวมจึงมีความหมาย

แต่ละเครื่องมือแผนที่มีรุ่นกล้องของตัวเอง บางรายพูดถึงการซูม บางรายพูดถึงความสูงของกล้อง บางรายหมุนรอบจุดโฟกัสพื้นดิน และบางรายเคลื่อนย้ายจุดมองเอง หากใช้ SDK เนทีฟโดยตรง เมื่อเปลี่ยนผู้ให้บริการจะทำให้เกิด "ตัวเลขเดียวกันแต่ภาพต่างกัน" ทันที และคุณจะต้องเขียนตรรกะหน้าจอใหม่ทั้งหมด

MapConductor กักเก็บความแตกต่างนี้ไว้ในเลเยอร์อะแดปเตอร์ และแสดงเพียงรุ่นกล้องเดียวให้แอปเห็น ผลลัพธ์คือตำแหน่งกล้องจะกลายเป็นค่าที่สามารถถ่ายทอดข้ามผู้ให้บริการได้ ในหน้า Camera Sync ของแอปตัวอย่าง คุณสามารถตรวจสอบได้จริงว่าตำแหน่งกล้องหนึ่งถูกส่งไปยังผู้ให้บริการทุกราย และจัดเรียงในตำแหน่ง สเกล และความเอียงเดียวกัน

DIFFERENCE 01

ซูมหรือระดับความสูง

ระบบไทล์ 2D แสดงสเกลด้วยระดับซูม ส่วนระบบ 3D ใช้ความสูงของกล้องหรือระยะการมองเห็น หน่วยและเกณฑ์ก็แตกต่างกันด้วย

DIFFERENCE 02

จุดโฟกัสหรือจุดวิว

มีทั้งแบบจำลองที่หมุนรอบจุดเดียวบนพื้นผิวโลก (โอร์บิต) และแบบจำลองที่อธิบายด้วยตำแหน่งและทิศทางของกล้องเองผสมกันอยู่

DIFFERENCE 03

ช่วงที่อนุญาตของการเอียง

มุมเอียงสูงสุดและการเอียงขึ้นด้านบนได้หรือไม่ก็แตกต่างกันไปในแต่ละเอ็นจิ้น ไม่มีเอ็นจิ้นใดที่มีการเอียงแบบลบ

ภาพ · moveCameraTo ครั้งเดียวให้มุมมองเดียวกันในทุกเอนจินได้อย่างไร
val camera = MapCameraPosition(
    position = geoPoint,
    zoom = 18.0,
    tilt = 70.0,
)
moveCameraTo(camera)
MapConductor
โมเดลกล้องแบบรวมศูนย์
position / zoom · bearing / tilt
แยกตามผู้ให้บริการ
อะแดปเตอร์ SDK แผนที่
แยกตามผู้ให้บริการ
อะแดปเตอร์ SDK แผนที่
อะแดปเตอร์แปลงเป็นรูปแบบกล้องเนทีฟของแต่ละราย ความต่างระหว่างแบบอิงซูม อิงความสูง และแบบที่เงยขึ้นไม่ได้ ถูกรองรับตรงนี้
เอนจินที่อิงซูม (Google Maps · Mapbox · MapLibre …)
เอนจินที่อิงความสูง (ArcGIS · Cesium · MapKit …)
ทางเข้าเดียว ปลายทางเป็นรูปแบบเนทีฟที่ต่างกัน แต่สิ่งที่เห็นบนจอคือตำแหน่ง มาตราส่วน และมุมเอียงเดียวกัน
วิดีโอตัวอย่าง · Camera Sync (ซิงค์ตำแหน่งกล้องหนึ่งไปยังผู้ให้บริการทุกราย)

02 · รุ่นกล้องที่รวมกัน

กล้องแสดงด้วย MapCameraPosition เดียว ไม่มีประเภทเฉพาะสำหรับผู้ให้บริการ คุณเพียงสร้างค่านี้และส่งไปที่ moveCameraTo

คุณสมบัติ
คำอธิบาย
position
พิกัดศูนย์กลางพื้นที่กล้องมองเห็น
zoom
สเกล ระดับการซูมของ Google Maps ถูกใช้เป็นมาตรฐานในการทำให้เป็นปกติสำหรับผู้ให้บริการทุกราย
bearing
ทิศทาง (มุมการหมุนจากทิศเหนือจริง)
tilt
ความเอียง 0 คือจากด้านบนตรง ค่าบวกคือมุมมองเอียงไปด้านหน้า ค่าลบคือมุมมองยกขึ้นดูเหนือเส้นขอบฟ้า
paddings
พื้นที่ที่ซ่อนอยู่โดย UI การคำนวณจุดศูนย์กลางและการปรับให้พอดีจะทำโดยหักพื้นที่นี้ออก
visibleRegion
ช่วงพื้นดินที่แสดงจริงบนหน้าจอ มีขอบเขตสี่เหลี่ยมและพิกัดที่สี่มุม

สามารถแทนที่เฉพาะส่วนด้วย copy() และการเปรียบเทียบจะดำเนินการโดยมีความคลาดเคลื่อนอนุญาตของการซูม 0.01 และความเอียง 0.01 เพื่อป้องกันการตัดสินผิดว่า "เคลื่อนที่" เนื่องจากความแตกต่างเล็กน้อยของค่าที่ส่งคืนจากผู้ให้บริการ

แพลตฟอร์ม

03 · การรวมระดับการซูม = ความสูง

ภายในระบบ ซูมจะถูกจัดการเหมือนกับ “ความสูงของกล้อง” ใน `core` มีตัวแปลงที่สามารถแปลงซูมของไทล์ 2D และความสูง/ระยะมุมมอง 3D ซึ่งกันและกัน ค่าจากทุกผู้ให้บริการจะถูกแปลงเป็นซูมร่วม (มาตรฐาน Google Maps) ก่อนที่จะเผยแพร่

มาตรฐานนี้ไม่ได้ถูกกำหนดด้วยเหตุผลเพียงอย่างเดียว ด้วยเครื่องมือสอบเทียบในแอปตัวอย่าง พื้นที่ที่มองเห็นได้จริงสำหรับแต่ละเอ็นจิ้นถูกวัดเมื่อระบุซูมเดียวกัน และความสูงมาตรฐานถูกสอบเทียบให้พื้นที่ตรงกัน ดังนั้น ซูม 14 จึงมีความกว้างเท่ากันในทุกผู้ให้บริการ

ภาพ · ระดับซูมกับมาตราส่วนที่เห็น (โดยประมาณว่ากี่กิโลเมตรพอดีกับความกว้างจอ)
0
≈ 40,000 km
ทั้งโลก
เห็นทั้งโลกในมุมมองเดียว มีเพียงเค้าโครงทวีป
3
≈ 5,000 km
ทวีป
ทวีปและมหาสมุทร พรมแดนยังไม่มีความหมาย
6
≈ 600 km
ประเทศ
ทั้งประเทศและตำแหน่งของเมืองต่าง ๆ
10
≈ 40 km
เขตเมือง
พื้นที่เมืองและถนนสายหลัก เป็นมุมมองเริ่มต้นที่ใช้บ่อย
14
≈ 2.5 km
ย่าน
อ่านโครงข่ายถนนได้ เหมาะกับการปักหมุดรายชื่อร้าน
17
≈ 300 m
บล็อก
ทางแยกและรูปร่างอาคาร ใช้กับการนำทางแบบเดินเท้า
20
≈ 40 m
อาคาร
ทางเข้าและตำแหน่งแบบเจาะจง
ทุกขั้นซูมมาตราส่วนเพิ่มเป็นสองเท่า ตัวเลขอ้างอิงที่เส้นศูนย์สูตรต่อความกว้างจอ 256 พิกเซล ระยะจริงบนพื้นเปลี่ยนตามละติจูด
LATITUDE

ซูมระดับเดียวกันต้องใช้ความสูงต่างกันตามละติจูด

Web Mercator ขยายแผนที่มากขึ้นเมื่อเข้าใกล้ขั้วโลก ความสูงกล้องสำหรับซูมระดับเดียวกันจึงต่างกันหลายเท่าระหว่างเส้นศูนย์สูตรกับละติจูดสูง การแปลงซูม ⇄ ความสูงคำนวณเรื่องนี้ไว้แล้ว

ZOOM 10 · ใกล้เส้นศูนย์สูตร
≈ 5,200 m
ZOOM 10 · ราว 70° เหนือ
≈ 1,700 m
NATIVE
ปรับค่าเนทีฟให้ตรงกับสเกลนี้
±0.0
Google Maps · Mapbox 2D — ใช้เป็นค่าอ้างอิง สเกลซูมแบบรวมศูนย์ยึดตามกลุ่มนี้
+1.0
MapLibre · MapTiler · TomTom — ซูมเนทีฟต่ำกว่าหนึ่งขั้น จึงชดเชยด้วยค่า +1.0
m
MapKit · ArcGIS · Cesium · Google 3D — แปลงระหว่างซูมกับความสูง/ระยะมอง โดยคิดละติจูดและมุมเอียงด้วย
ZOOM RANGE
0.0 – 22.0
ZOOM FACTOR
2.0
ZOOM 0 ALTITUDE
171,319,879 m
MAPLIBRE OFFSET
+1.0

สิ่งที่เป็นค่าคงที่คือความสัมพันธ์ “หนึ่งขั้นต่อการเพิ่มเป็นสองเท่า” และความสูงของซูม 0 เป็นค่าอ้างอิง การแปลงยังรวมถึงข้อเท็จจริงที่ว่าระยะทางบนพื้นดินที่ซูมเดียวกันจะลดลงตามละติจูดที่สูงขึ้น ดังนั้นสเกลจึงไม่คลาดเคลื่อนระหว่างโตเกียวและโฮโนลูลู

04 · การรองรับจำลองสำหรับ tilt < 0

tilt รับค่าตั้งแต่ −60 ถึง 60 องศา ค่าบวกคือมุมมองเฉียงลงมาตามปกติ ส่วนค่าลบคือมุมมองยกขึ้นดูเหนือขอบฟ้า ไม่มีเอ็นจิ้นแผนที่ตัวใดสามารถแสดงนี้ได้โดยตรง ทุก SDK ยอมรับเฉพาะ pitch ≥ 0 เท่านั้น

MapConductor จึงแทนที่สิ่งนี้โดยรักษาตำแหน่งและทิศทางของกล้องไว้ แต่ส่งค่าความเอียงเป็นบวกให้กับเอ็นจิ้น จากแอปสามารถใช้ tilt ติดลบได้ตามปกติ และตำแหน่งกล้องที่ได้รับก็จะถูกส่งกลับมาเป็น tilt ติดลบเช่นกัน

tilt = 0

แผนผังที่มองจากด้านบนโดยตรง สี่เหลี่ยมผืนผ้าบนพื้นผิวโลกจะกลายเป็นสี่เหลี่ยมผืนผ้าบนหน้าจอโดยตรง

tilt > 0

มุมมองแนวเอียงไปทางด้านหน้า บนพื้นผิวโลกจะปรากฏเป็นสี่เหลี่ยมด้านขนานที่ด้านใกล้แคบและด้านไกลกว้าง

tilt < 0

มุมมองยกขึ้นเหนือขอบฟ้า มุมมองที่รวมท้องฟ้าเข้าไปด้วย ซึ่งไม่มีอยู่โดยพื้นฐาน

การจำลองทำงานอย่างไร
01
ตำแหน่งตาและความสูงของกล้องอยู่กับที่ มีเพียงจุดเป้าหมายบนพื้นที่เลื่อนไปข้างหน้าตามทิศ bearing ด้วยระยะที่คำนวณจากความสูงและมุมเอียง
02
จากนั้นส่งค่า abs(tilt) ให้เอนจินเป็นมุมก้มลง เนื่องจากตำแหน่งตาและทิศทางตรงกัน ภาพที่ได้จึงเหมือนมุมมองเงยขึ้น ส่วนเอนจินตระกูล MapLibre จะได้ค่าชดเชยคงที่สำหรับระยะเลื่อนและซูมเพื่อให้กรอบภาพตรงกันด้วย
03
เมื่ออ่านค่ากล้องกลับมา ระบบจะคำนวณย้อนกลับด้วยวิธีเดียวกัน หักล้างระยะเลื่อนและค่าชดเชยซูม เพื่อคืนค่า tilt ติดลบเชิงตรรกะและจุดศูนย์กลางเดิม ค่าจึงไม่เพี้ยนเมื่อไป-กลับ
การรองรับ tilt ค่าติดลบ
Android · ทุกผู้ให้บริการ
OK
iOS · ทุกผู้ให้บริการ
OK
React · ทุกรายยกเว้น Google Maps 2D
OK
React · Google Maps 2D
N/A

ข้อยกเว้นเดียวคือ Google Maps 2D บน React ซึ่งเป็นแผนที่แบบราสเตอร์ของ JavaScript API มุมเอียงขึ้นกับเบราว์เซอร์และไทล์ จึงควบคุมความสูงและทิศทางที่การจำลองต้องใช้ไม่ได้ ส่วน Google Maps 3D (Map3DElement) รองรับ tilt ค่าติดลบ

TiltMapPageViewModel.kt
// ส่งค่าสไลเดอร์ −60 ถึง 60 ไปตรง ๆ ค่าติดลบก็เรียกแบบเดียวกัน
currentPosition = currentPosition.copy(tilt = tilt)
mapViewState.value?.moveCameraTo(currentPosition)
วิดีโอตัวอย่าง · Tilt (เลื่อน −60〜60 อย่างต่อเนื่อง)

05 · VisibleRegion — พื้นที่ที่มองเห็นได้จริง

เมื่อกล้องเอียง สี่เหลี่ยมจัตุรัสของหน้าจอจะกลายเป็นสี่เหลี่ยมคางหมูบนพื้นดิน สี่เหลี่ยมผืนผ้าที่มีเพียงตะวันออกเฉียงเหนือและตะวันตกเฉียงใต้ (bounds) ไม่สามารถแสดงความแตกต่างของพื้นที่ระหว่างพื้นที่ใกล้และไกลได้ VisibleRegion มีพิกัดพื้นดินของสี่มุมหน้าจอนอกเหนือจากสี่เหลี่ยมผืนผ้า ทำให้สามารถจัดการ “พื้นที่ที่แสดงจริง” ได้แม้กล้องจะเอียง

near คือขอบล่างของหน้าจอ นั่นคือด้านที่ใกล้กล้อง ส่วน far คือขอบบนซึ่งเป็นด้านที่ไกลกว่า left / right อิงตามหน้าจอ ดังนั้นความหมายจะไม่เปลี่ยนแม้จะหมุนแผนที่ด้วย bearing ด้วยจุดทั้งสี่นี้ ทำให้สามารถกำหนดช่วงการรับข้อมูลและการระบุแหล่งที่มาได้อย่างถูกต้องแม้แผนที่จะเอียง

farLeft
farRight
nearLeft
nearRight
สี่เหลี่ยมบนจอ → สี่เหลี่ยมคางหมูบนพื้น
ขอบไกลกว้างกว่า ขอบใกล้แคบกว่า ส่วน bounds คือกรอบที่ล้อมสี่เหลี่ยมคางหมูนี้
bounds
กรอบตะวันตกเฉียงใต้/ตะวันออกเฉียงเหนือที่ล้อมสี่เหลี่ยมคางหมู ใช้ตรวจแบบคร่าว ๆ และเข้ากันได้กับ API เดิม
nearLeft / nearRight
พิกัดบนพื้นของมุมล่างจอ ซึ่งเป็นด้านใกล้
farLeft / farRight
พิกัดบนพื้นของมุมบนจอ ซึ่งเป็นด้านไกลและกางกว้างขึ้นเมื่อเอียงมากขึ้น
ใช้ทำอะไร
ดึงข้อมูลของพื้นที่ที่มองเห็น, fitBounds และการสลับกฎแสดงที่มาของไทล์
VisibleRegionViewModel.kt
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)
}

หน้าที่เกี่ยวข้อง