Prototipizarea unei hărți mai fine

O privire asupra modului în care funcționează Google Maps

Când am lucrat pe Google Maps ca UX Engineer, unul dintre lucrurile pe care mi-am dorit cu adevărat să le pot face a fost să creez prototipuri care să poată sincroniza animațiile cu zoom-ul. Cu toate acestea, API-ul JavaScript Maps are un control limitat al zoom-ului, așa că pentru a avea un control mai mare, am experimentat folosind panza HTML5 pentru a compune plăcile într-o implementare complet personalizată.

Pentru a înțelege ce face zoom-ul fără probleme pentru aplicațiile moderne de hărți, vă ajută mai întâi să înțelegeți cum este realizată harta și cum funcționează clienții Google Maps (și majoritatea celorlalți), puteți căuta titlurile îndrăznețe pentru a merge direct acolo.

  1. Efectuarea Pământului - procesul prin care un glob 3D este transformat într-o hartă 2D.
  2. Google Maps - modul în care Google a schimbat lumea mapării în 2005 și modul în care se redă clientul.
  3. Animarea hărții rasterizate - abordarea personalizată pe care am folosit-o pentru a activa o animație lină.

1. A face pământul plat

Sper că nu va veni ca o surpriză pentru nimeni, dar lumea este rotundă - deși suficient de curios, nu este de fapt o sferă, ci mai degrabă ușor gâtuită și mai largă la mijloc.

Procesul de a lua acest obiect în mare parte sferic și de a-l reprezenta în 2D este ceva cu care cartografii s-au luptat și s-au certat de mai multe milenii. De fapt, nu există nici cel mai „bun” mod de a face acest lucru, fiecare vine cu compensări.

Latitudine și longitudine

Dacă vă imaginați lumea ca o sferă care se rotește în jurul axei sale, polul nord se află în vârf, polul sud este în partea de jos, iar ecuatorul este linia imaginară care circulă în jurul mijlocului.

reticulului

Dacă vă gândiți la ecuator ca un cerc care se așează orizontal (ca o centură), atunci puteți imagina cercuri orizontale suplimentare deasupra și de dedesubt, fiecare dintre acestea paralel cu ecuatorul. Urmând orice cerc la dreapta, vă deplasați spre est, urmând orice cerc spre stânga, spre vest. Aceste linii imaginare sunt numite cercuri ale latitudinii. Latitudinile sunt întotdeauna aceeași distanță una de cealaltă, indiferent cât de departe de est sau de vest ai călători.

Deoarece pământul este înclinat pe axa sa, soarele nu stă întotdeauna direct deasupra ecuatorului, dar pare să rătăcească ușor spre nord și sud în timpul orbitei noastre. Tropicul Cancerului este cercul cel mai nordic al latitudinii la care soarele poate apărea direct deasupra capului (pe solstițiul din iunie), iar Tropicul Capricornului este echivalentul în emisfera sudică (și solstițiul din decembrie). Acest lucru nu este de fapt important pentru Google Maps, dar este o problemă distractivă.

Latitudinea indică cât de departe de nord sau de sud sunteți (pentru că oricât de departe de est sau vest ați merge nu v-ați mutat deloc spre nord sau sud).

Alergând în cealaltă direcție, perpendicular pe ecuator, sunt meridianele sau liniile de longitudine - fiecare conectează polul nord la sud într-o linie dreaptă. După orice linie, vă deplasați spre nord, urmând spre sud.

Longitudine indică cât de îndepărtate spre est sau vest (pentru că oricât de departe te-ai îndrepta spre nord sau sud, nu te-ai mișcat deloc la est sau la vest).

Diferența crucială între latitudine și longitudine este că, în timp ce latitudinea este întotdeauna la o distanță egală de o sferă, liniile de longitudine sunt cele mai îndepărtate la mijloc (ecuatorul) și se apropie mai aproape de poli (unde se ating).

Măsurarea latitudinii și longitudinii

Atât latitudinea cât și longitudinea sunt măsurate în grade. Începând din centrul sferei, trebuie doar să măsurați unghiul de pe fiecare cerc (cercurile paralele de latitudine și cercurile perpendiculare de longitudine).

Latitudinea este unghiul format din ecuator, deci este 0 ° la ecuator și până la 90 ° nord sau sud (la poli). La jumătatea drumului spre Polul Nord s-ar afla la 45 ° N.

Longitudinea este unghiul măsurat din Prime Meridian (o linie arbitrară care trece prin Greenwich, Anglia). Se află la 0 ° în Greenwich și până la 180 ° Est sau Vest (partea opusă a lumii).

Poziționare globală

O ultimă problemă, care nu contează cu adevărat pentru Google Maps (dar mi se pare interesant), este modul în care oamenii s-au localizat înainte de GPS.

Latitudinea este relativ simplă, trebuie doar să măsurați unghiul dintre orizont și o stea cunoscută (sau soarele nostru) și apoi să faceți puțin matematica. Oamenii navigau pe baza stelelor de mii de ani, Polaris (Steaua Nordului) este deosebit de popular - la Polul Nord, Polaris este direct deasupra capului (la 90 ° până la orizont). La ecuator, Polaris pare să stea la orizont (la 0º). Între aceste două extreme, unghiul față de Polaris este unghiul de latitudine nord. Puteți folosi și alte stele, ele necesită doar mai multă matematică.

Longitudinea este mult, mult, mai greu - de fapt atât de greu încât nimeni nu a putut face bine până la sfârșitul anilor 1700 (și nici cu ușurință până mult mai târziu decât asta). Soluția implică timp - pământul se rotește cu aceeași viteză, 360 ° la fiecare 24 de ore (sau 15 ° pe oră), așa că dacă știți ora de unde ați început și ora în care vă aflați, puteți calcula distanța în funcție de diferență. Ușor de făcut astăzi, cu ceasuri precise, dar foarte greu de făcut anterior.

Inițial, au folosit pozițiile preconizate ale corpurilor cerești la orele cunoscute pentru a calcula ce a fost ora și apoi au comparat-o cu ora locală (folosind soarele la prânz pentru a reseta „ceasul local”). Cel mai utilizat set de predicții a fost Almanahul Nautic publicat de Observatorul Regal din Greenwich, Anglia - care, dacă te-ai întrebat vreodată de ce a fost numit GMT (Greenwich Mean Time) sau de ce Prime Meridian trece prin Greenwich, este deoarece pentru cel mai lung timp toată lumea se măsura în raport cu observatorul din Greenwich.

Cea mai mare descoperire în ceea ce privește măsurarea longitudinii a fost ceasurile mai precise, astfel încât acestea să poată înceta stargazul și să verifice doar GMT - longitudinea a fost numărul de ore înmulțit cu 15 °. De fapt, acesta este cam cât toată lumea a calculat longitudinea până la inventarea sistemului de poziționare globală (GPS), care este doar un set de ceasuri atomice super precise care stau în spațiu (și niște matematici impresionante).

Dacă sunteți pe jumătate la fel de fascinat de mine, aș recomanda cu adevărat să citiți această carte minunată despre asta.

Alegerea unei proiecții

Metoda de traducere a punctelor de pe un glob 3D într-un plan 2D se numește proiecție de hartă. Există multe proiecții diferite, fiecare cu propriile forțe și limitări și niciuna fără o formă de denaturare a geometriei reale - iată doar câteva:

Proiecții pe Wikipedia

Proiecția selectată de Google Maps este o versiune modificată a proiecției Mercator, intitulată creativ Web Mercator [diferența principală este că presupune că lumea este o sferă, în loc de elipsoid aplatizat].

Există câteva motive pentru a alege Mercator, dar cel mai bun motiv este că nordul și sudul sunt drepte în sus și în jos, iar estul și vestul sunt drepte la stânga și la dreapta - pe unele dintre celelalte proiecții, aceste linii s-ar curba sau devia pe măsură ce treceți peste harta. Acest lucru este foarte asemănător cu motivul pentru care Mercator a fost atât de larg adoptat pentru navigație - liniile de curs constant (adică dacă alegeți un rulment al busolei și rămâneți cu el) sunt complet drepte. Un avantaj secundar este faptul că, deoarece este o proiecție cilindrică, puteți înfășura orizontal și colorați harta.

Unele dintre celelalte proprietăți speciale ale proiecției Mercator sunt faptul că scara este aceeași în toate direcțiile din jurul oricărui punct localizat (adică dacă faceți zoom într-un oraș, distanțele nord și sud sunt aceleași cu estul și vestul) și toate unghiurile sunt înfățișat cu exactitate (adică estul este la 90 ° de la nord).

Cea mai mare critică a proiecției Mercator este aceea că denatura semnificativ scala țărilor cu cât ajungeți mai departe de ecuator (de exemplu, Groenlanda arată la fel de mare ca Africa, în ciuda faptului că este mai mică de o zecime din dimensiune).

Gândiți-vă la ce se întâmplă atunci când cojiți o portocală și imaginați-vă că faceți același lucru cu pământul. Dacă ar fi să tăiați de-a lungul seriei de linii longitudinale, s-ar încheia cu un sortiment de felii tăiate - pe glob, fiecare linie verticală apare drept, dar pe măsură ce o desfaceți în două dimensiuni, ea devine curbată (amintiți-vă cum sunt acele linii de longitudine ne-am apropiat împreună la stâlpi?).

Desfacerea globului

Pentru ca harta Mercator să se alăture din nou, trebuie să întindem acele segmente pe orizontală. La ecuator se ating deja, deci nu trebuie să se întindă deloc, dar la stâlpi există un decalaj foarte mare și trebuie să se întindă foarte mult (infinit din punct de vedere tehnic). Pentru a păstra distanțele și unghiurile în ambele direcții, întrucât se întinde orizontal, este întins cu aceeași cantitate pe verticală. Cu cât se apropie de poli, cu atât mai mult trebuie să se întindă.

Acest lucru este ușor de vizualizat dacă încercați să desenați un cerc de aceeași dimensiune în diferite puncte de pe hartă. Pentru proiecția Mercator puteți vedea că cercurile apar mult mai mari spre poli (deși în realitate au aceeași dimensiune). Acest lucru se datorează faptului că a trebuit să întindem harta mai mult la stâlpi pentru a o face să se conecteze.

Deformarea scării

Acest lucru contează doar atunci când este foarte mare, deoarece scara este aceeași în orice regiune localizată, astfel încât pentru un anumit oraș sau chiar o țară totul rămâne proporțional. Și este într-adevăr o problemă semnificativă pentru stâlpi, dar din moment ce pinguinii și urșii polari nu folosesc Google Maps nu au fost prea multe plângeri.

Dacă ne gândim înapoi la sfera - unde liniile de latitudine erau paralele între ele și întotdeauna aceeași distanță între ele, în timp ce liniile de longitudine se apropiau mai mult la poli - cu Mercator, latitudinea rămâne perfect paralelă, iar longitudinea devine perfect perpendiculară. Totul este drept.

Acest video fantastic de Grafonaut demonstrează întreaga transformare:

2. Google Maps

În 2005, Google Maps a lansat folosind o inovație care încă stă la baza fiecărui serviciu de mapare astăzi - harta cu gresie. În 2013 a existat o actualizare majoră pentru a utiliza WebGL și pentru a adăuga randarea la nivel de client, dar abordarea cu gresie rămâne.

Trebuie să notez că Google Maps nu a inventat conceptul de hartă cu gresie, dar a fost probabil prima aplicație principală care a utilizat-o, iar combinarea acesteia cu AJAX și web a ajutat cu siguranță la popularizarea abordării.

Hărți cu gresie

În loc să încerce să redea o singură imagine, Google descompun harta în plăci mai mici, apoi le așează una lângă alta pentru a alcătui o singură imagine mai mare - la fel ca un mozaic.

Harta cu gresie

Motivul principal pentru aceasta este dimensiunea imaginii. La cel mai înalt nivel de zoom al Google Maps, imaginea ar fi de peste 500 de milioane de pixeli pătrați (dublu decât cea de pe ecranele HDPI), ceea ce înseamnă peste 25.000 de terabyți (cred?) Chiar și cu o compresie generoasă a imaginii. Presupunând că browserul dvs. ar putea reda imaginea respectivă, este nevoie de mai mult de 6 ani pentru a descărca utilizând Google Fiber.

Al doilea motiv este încărcarea pe server. În loc să folosească plăci, serverul ar putea genera o hartă perfect dimensionată pentru fiecare utilizator, la nivelul de zoom exact, latitudine și longitudine și dimensiunea potrivită pentru a umple fereastra. Dar asta ar însemna probabil că fiecare utilizator are nevoie de o hartă complet personalizată și, cu peste 1 miliard de utilizatori lunari, aceasta este o mulțime de hărți personalizate! De asemenea, ar însemna că de fiecare dată când ați afișat harta chiar și câțiva pixeli, va trebui să descărcați o hartă complet nouă.

Lucrul frumos despre plăci este că toată lumea le poate împărtăși, serverul le poate cache (și chiar le poate genera în prealabil), iar clientul le poate muta cu ușurință. Unii utilizatori pot descărca câteva plăci suplimentare dacă fereastra lor este mai mare, dar plăcile trebuie să fie redate o singură dată.

Notă: pentru implementarea WebGL bazată pe vector 2013, în loc să trimită plăci de imagine, serverul trimite informațiile vectorului (fiecare cale și poligon), iar clientul redă imaginile. Totuși, aceste informații vectoriale sunt încă grupate în „plăci” - din aceleași motive (permite memorarea în cache a serverului și oferă un mod curat pentru ca clientul să subdivizeze solicitările de date). În timp ce următoarele secțiuni discută despre implementarea raster (care este încă utilizată de API-ul JavaScript JavaScript), abordarea generală se aplică și versiunii WebGL.

Niveluri de zoom

Google Maps are un număr diferit de niveluri de zoom în funcție de locație - dar de obicei este aproximativ 21. La cel mai mare zoom (nivel 0), întreaga hartă este reprezentată de o singură placă pătrată de 256 și 256 pixeli. La fiecare nivel de zoom incremental, harta se dublează în dimensiune în fiecare direcție - fiecare țiglă este înlocuită cu 4 mai detaliate (2x2) la zoom. Fiecare țiglă are încă 256 x 256 pixeli, iar atunci când le combinați obțineți aceeași hartă (doar mai detaliată).

La nivelul zoomului 0 harta lumii este o singură țiglă, la zoomul 1 harta are 2 plăci în fiecare direcție, la zoom 2 are 4 plăci lățime, la zoom 3 are 8 țiglă lățime și așa mai departe (dublarea de fiecare dată) . Așadar, în timp ce lățimea și înălțimea totală se dublează la fiecare nivel, zona crește mai rapid (1 țiglă, 4 plăci, 16 plăci, 64 plăci, etc ...). Până la atingerea nivelului de zoom 21, harta are o lățime de 2 milioane de țiglă și conține peste 4 trilioane de plăci în total.

Placile de hartă la fiecare nivel de zoom

Fiecare nivel de zoom primește propriile reguli de stil pentru a decide ce informații ar trebui afișate. Există puțină valoare pentru adăugarea informațiilor rutiere pe harta lumii, nici pentru construirea informațiilor pe harta țării, etc ... Există o echipă foarte harnică, care echilibrează constant etichetele și funcțiile prezentate și stilate la fiecare nivel.

Ca regulă generală, primele câteva niveluri sunt aproape doar harta mondială. La zoom-ul 5, continentele și măștile sunt principalele caracteristici. La nivelul 10 apar detaliile orașului. La nivelul 15, străzile sunt clar vizibile. Și prin zoom 20, clădirile sunt redate.

Putem estima cu ușurință scala de pixeli pentru aceste zoom-uri - am putea să o facem exact cu o matematică mai complexă - dar la ecuator (unde proiecția Mercator nu întinde harta) este un calcul simplu:

La zoom 1 fiecare pixel reprezintă 78 km (48 miles), zoom 5 este 5 km (3 miles), zoom 10 este 150m (164 yards), 15 este 5m (5,5 yard), iar prin zoom 20 fiecare pixel este echivalentul 15cm ( 6 inch) - este impresionant de detaliat!

poziţionarea

Google Maps are trei concepte de coordonate pe lângă latitudine și longitudine: coordonate mondiale, coordonate pixeli și coordonate plăci.

Coordonatele lumii sunt independente de nivelul zoom-ului și sunt folosite pentru a traduce între latitudini și lungimi și poziția curentă pe hartă (sau invers). Acestea sunt calculate în raport cu o singură țiglă la nivel de zoom 0. Latitudinea și longitudinea sunt asortate la pixelul fracționat x și y pe acea singură țiglă (un număr între 0 și 256 - lățimea și înălțimea).

Conversia este foarte ușoară, deși nu pot pretinde să înțeleg matematica. Maparea longitudinală este ușor de înțeles, deoarece se traduce direct, dar latitudinea este mai complicată datorită variației pe măsură ce se apropie de poli.

Coordonatele pixelilor se referă la poziția exactă a pixelului a latitudinii și longitudinii la un nivel de zoom specificat. Acestea pot fi calculate luând coordonatele lumii și înmulțind cu suma totală de scalare pentru nivelul de zoom - care este ușor de calculat, deoarece scala dublează fiecare zoom.

Coordonatele plăcilor sunt modul în care clientul solicită serverului imagini.

Coordonatele plăcilor

Sunt poziționate în rânduri și coloane, cu rândul 0 coloana 0 în partea stângă sus, rândurile crescând la dreapta și coloanele în timp ce cobori. Similar cu coordonatele de pixeli, plăcile depind de nivelul de zoom, de fapt este o simplă mapare de la pixel la țiglă, împărțind coordonatele de pixeli la dimensiunea plăcii și luând numărul integral.

Clientul poate calcula cu ușurință care sunt plăcile de care are nevoie calculând coordonatele plăcilor pentru fiecare colț al ecranului. De obicei, adaugă un pic de umplere, ca mijloc de preîncărcare, doar în cazul în care utilizatorul plătește harta cu câțiva pixeli.

Clienții pot genera cu ușurință URL-urile de țiglă folosind aceste coordonate - de exemplu, nivelul de zoom 1, rândul 0, coloana 0, este această grilă care conține America de Nord. Este banal să se construiască în cod, deși API-ul Maps se ocupă de dvs. (împreună cu stilul și alte avantaje).

panning

Interacțiunea cu harta este locul în care plăcile își dovedesc cu adevărat valoarea. Înainte de Google Maps, lucrurile funcționau ceva mai mult ca un Atlas - dacă ai ajuns la marginea hărții, trebuia să întorci pagina pentru a vedea ceva mai mult. Marele lucru despre plăci este faptul că le permite utilizatorilor să exploreze liber harta fără întreruperi, în timp ce au tras pe ecran și au făcut zoom.

Fiecare țiglă este poziționată absolut într-un container, iar atunci când faceți pană pe hartă în loc să mutați fiecare țiglă, doar containerul trebuie să se miște (iar plăcile se schimbă cu ea). Acest lucru permite clientului să minimizeze numărul de modificări DOM.

Containerul se mișcă, nu plăcile

Este ușor de calculat poziția în care ar trebui să se afle fiecare țiglă, prin simpla înmulțire a coordonatelor plăcii cu dimensiunea plăcii.

Ultimul truc de poziționare este minimizarea numărului total de plăci pe care trebuie să le redea browserul.

Menținerea unui DOM de dimensiuni constante

Pe măsură ce utilizatorul plătește harta, clientul verifică ce plăci ar trebui să fie vizibile și le încarcă pe cele noi sau le elimină pe cele care nu mai sunt vizibile.

Acest lucru se face atât de repede încât utilizatorul observă foarte rar (iar în loc să decupeze limitele dure ale ecranului, adesea se adaugă câteva plăci de o parte și de alta ca un tampon). [Această abordare nu este chiar similară cu modul în care funcționează acum Google Photos]

Panoramare

Panoramarea este perfectă, dar zoom-ul este una dintre zonele provocatoare cu o hartă cu gresie.

Din punct de vedere tehnic, provocarea este mai puțin cum să poziționezi plăcile, ci mai mult cum să tranzițiezi între niveluri. Fiecare nivel de zoom are o dublare a scării și nu există plăci interioare care să vă ajute.

Făcând între niveluri

Zoom-ul a fost inițial foarte simplu, doar a înlocuit harta cu următorul set de țiglă, dar asta a fost un pic zarvă, deoarece se va „bloca” brusc între niveluri.

O modalitate de compensare a acesteia este în loc de a mări în centrul hărții, păstrează orice locație se află sub staționarea cursorului (fixată sub cursor). Acest lucru permite utilizatorilor să „puncteze” literalmente funcția de care sunt interesați și să se concentreze (controlează punctul de referință).

Animare la scară rapidă (API JavaScript JavaScript)

O adaptare mai recentă a făcut-o să se simtă mai receptivă. Când faceți zoom, păstrează temporar ambele niveluri de zoom în valoare de plăci (cele vechi și cele noi) și face o animație la scară foarte rapidă între ele - noile plăci încep să fie reduse la jumătate, iar plăcile vechi sunt animate până la dimensiuni duble.

Încă „se fixează” între straturi când se termină animația, dar totul se întâmplă atât de repede încât felul de ochi își imaginează starea interioară.

Această abordare la scară și snap este cea care este încă folosită astăzi de API-ul JavaScript Maps Google, Bing Maps, Here Maps, Yahoo Maps, MapQuest și OpenStreetMap (LeafletJS).

Tranziția la scară în timpul zoomului

Posibilitatea cea mai mare limitare cu scale și snap este că utilizatorul nu are control asupra animației, odată ce declanșează zoom-ul va rula până la finalizare, nu există posibilitatea de a controla viteza sau de a face o pauză într-o stare intermediară.

Hărți vectoriale

În 2013, Google Maps a lansat o actualizare majoră la maps.google.com care a încetat utilizarea plăcilor PNG pentru imagini și a început să descarce plăci vectoriale. Aceste plăci vectoriale sunt în continuare menționate prin coordonatele de țiglă, se comportă în mod similar cu plăcile raster, dar în loc să fie o imagine, ele conțin toate etichetele, căile și poligoanele - și sunt desenate pe client.

Există o serie de motive pentru care acest lucru este important - datele vectorului comprimă mai bine decât imaginile (astfel economisim lățimea de bandă), face posibile actualizări dinamice și stilizare (de exemplu, dacă un utilizator face clic pe un traseu de tranzit) și permite un zoom substanțial îmbunătățit .

Zoom neted

Cu PNG-urile rasterizate, pe măsură ce scalați harta nu vă puteți spune ce este un drum (și ar trebui să rămâneți desenat la aceeași lățime) sau ce este un parc (și ar trebui să mărească mai mult), ceea ce înseamnă că totul este întins și pixelat. Cu informațiile vectoriale, clientul poate păstra etichetele poziționate corespunzător, menține lățimile drumului și totodată scalează toate poligoanele - rezultatul este o experiență de zoom incredibil de lină. Și mai bine, răspunde complet, astfel încât utilizatorul să poată controla viteza și chiar să se oprească la niveluri de zoom fracționate.

Cu toate acestea, dacă te uiți atent (sau mergi să încerci singur) în timp ce scalează foarte bine plăcile, nu există informații noi până la oprire. Imediat ce utilizatorul întrerupe zoom-ul, harta încarcă rapid plăcile vectoriale la noul nivel de zoom și le schimbă. Este într-adevăr netedă și optimă.

MapBox este unul dintre ceilalți clienți care utilizează plăci vectoriale pe web și, de asemenea, folosește această abordare lină și snap, deși încarcă mai agresiv plăci noi în timpul tranzițiilor zoom.

3. Animarea hărții rasterizate

Pentru a putea anima fără probleme harta, elementul cel mai critic este capacitatea de a seta niveluri de zoom fracționate (de exemplu, la jumătatea distanței dintre două dintre nivelurile de zoom integrale la care sunt redate plăcile). Cu plăci vectoriale, acestea pot fi redate personalizat pe client, dar cu plăcile raster, va trebui să devenim mai creativi.

Pentru a-l susține, am scris o versiune complet personalizată a API-ului JavaScript Maps, care reutilizează plăcile de imagine de pe serverul Maps, dar apoi le poziționează și gestionează interacțiunea de unul singur. Aceasta a permis un control complet asupra scalării și poziționării fiecărei plăci, precum și controlul asupra zoomului și animației. Totul a spus că este 4.442 de linii de cod comentat - este remarcabil cât de puțin cod are nevoie de client, dar dacă te gândești la asta, cea mai mare parte a muncii se lucrează cu greu pe server (care se rezolvă pe care drumuri, lacuri, clădiri etc.) sunt vizibile în fiecare țiglă, decizând stilurile și culorile, apoi redându-le ca imagini).

Restul acestui articol se referă la codul prototipului meu și nu la versiunea normală a Google Maps.

Crearea nivelurilor de zoom fracționate

În mod similar cu abordarea de pe Google Photos pentru a estompa opacitatea dintre imaginile cu rezoluție scăzută și cu rezoluție înaltă pentru a amesteca detaliile la încărcare, teoria mea a fost că am putea amesteca plăcile de la diferite niveluri de zoom pentru a crea un stat intermediar.

Am putea să luăm niveluri de zoom mai mici și să le scalăm ca la zoom, sau să facem opusul și să scăzem nivelurile de zoom mai mari atunci când mărim. Prin scalarea și suprapunerea plăcilor de la mai multe niveluri de zoom, am putea trece fără probleme între zoom-urile integrale și o putem face într-o manieră previzibilă matematic (perfectă pentru sincronizarea animațiilor).

Acest lucru este posibil deoarece Google folosește Proiecția Mercator - scala este uniformă pentru regiunile localizate, astfel încât scalarea liniară a plăcilor păstrează formele.

Calcularea opacității pentru încrucișare este simplă și liniară. Când treceți între două niveluri de zoom, următoarea țiglă ar trebui să fie complet transparentă (opacitate 0) la primul pas de zoom și complet opacă (opacitate 1) la următoarea. Deci opacitatea este de doar 1 minus distanța față de zoomul de țiglă de la zoom-ul hărții (deși în practică se fixează la valori între 0 și 1).

Scala nu este nici ea atât de complicată. Având în vedere că scala se dublează la fiecare nivel de zoom, este posibil să se calculeze suma la scară pentru un nivel intermediar folosind puteri de 2.

Dacă mapZoom este un nivel complet mai mare decât țiglă (mapZoom - tileZoom = 1), atunci matematica ar rezulta în 2¹ sau 2. Dacă mapZoom este același nivel ca țiglă (mapZoom - tileZoom = 0) matematica ar fi 2⁰ sau 1. Marele lucru despre calculul puterilor este că funcționează pentru fracții și negativ. Dacă mapZoom este un nivel complet mai mic decât țiglă (mapZoom - tileZoom = -1) veți obține 2⁻¹ sau 0,5. La jumătatea distanței dintre nivelurile de zoom (mapZoom - tileZoom = 0,5) obțineți 2 ^ (0,5) sau 1.414 și puteți face acest lucru pentru a scala lin între fiecare stare.

Următoarea ilustrație arată cum se aplică acest lucru în timp ce faceți zoom-ul. Placa monocromă este nivelul de zoom de pornire, iar plăcile colorate sunt următorul nivel de zoom (desenat într-un tablou de verificare, astfel încât să puteți observa diferența). Puteți vedea că, cu cât ajungem mai aproape de următorul nivel de zoom, cu atât domină mai mult plăcile colorate și detaliile (de exemplu, etichetele) încep să se estompeze.

Zoom in

Aici este direcția opusă, micșorând. Monochrome reprezintă din nou nivelul de zoom inițial și a colorat nivelul de zoom precedent - în acest caz pierdem detaliile odată cu micșorarea.

Zoom out12fps

Funcționează foarte bine, oferind un zoom relativ neted (mult mai bun decât scala și snapul) și, deși nu este la fel de neted ca reprezentarea vectorului (smooth & snap), elimină efectiv aspectul de snap.

Am lăsat suprapunerea de depanare în GIF, astfel încât să puteți vedea unde se încarcă noile plăci. Este mai ușor în practică (60 de cadre pe secundă), dar am limitat GIF-ul la 12fps. Iată videoclipul. Alternativ, aici este fără linii de depanare.

În oricare extremă, harta este aproape imperceptibil diferită de zoom-ul integral, deși la mijloc poate ajunge într-o stare penibilă, cu etichete care concurează între ele. Acest lucru nu ar fi un efect deosebit dacă rămâneți la un nivel de zoom fracționat, dar nu este o problemă în timpul tranzițiilor - și pentru a evita zoom-urile fracționate, este ușor pentru client să „se stabilească” la un zoom integral când utilizatorul termină zoom-ul, așteptând până să se dea drumul, ca să rămână între timp sub control complet.

Zoom între nivelurile integrale 3 și 4

Trebuie să numim această scală de efect și amestec? Nu doar că face zoom fără probleme, dar răspunde la intrarea utilizatorului în orice moment.

Zoom animativ la 24fps

HTML5 Canvas

Pentru a obține cele mai bune performanțe, am abandonat abordarea web normală a utilizării elementelor HTML standard. După cum este descris în secțiunea anterioară, API-ul Maps folosește o combinație de elemente DIV și IMG pentru a reda harta. Fiecare imagine este plasată într-un container părinte și acel container este mutat pe măsură ce hartile se fixează. Pentru aplicațiile web normale, permițând browserului să gestioneze lucrurile are o mulțime de avantaje, browserul gestionează toate deciziile legate de redirecționarea ecranului, modul de dispunere și poziționare a elementelor, precum și eficientizarea interacțiunii (cum ar fi evenimentele de derulare și clic). Utilizarea browserului pentru aceasta este aproape întotdeauna cea mai bună abordare.

Cu toate acestea, uneori - în special pentru desenul foarte personalizat - este avantajos să faci acest lucru manual. Pentru a sprijini acest lucru, browserele au creat elementul canvas. Spre deosebire de HTML-ul normal în care creați elemente și le adăugați la pagină, cu o pânză puteți executa comenzi de desen pe ea. Puteți desena linii, arcuri, cercuri, dreptunghiuri și chiar curbe complexe. Pânza nu va face nimic în plus pentru dvs. și tratează rezultatul la fel ca o imagine. Dacă atrageți o imagine în pânză, trebuie să-i spuneți exact unde și ce dimensiune, și dacă decideți să mutați acea imagine câțiva pixeli, trebuie să ștergeți întreaga pânză și să repetați totul pe ea (inclusiv celelalte linii și imagini). Dacă parcurgeți un site web normal, browserul recalculează pozițiile și redescrie. Dacă doriți să lăsați utilizatorii să deruleze o pânză, trebuie să recalculați manual fiecare poziție, apoi să redirecționați singuri fiecare piesă.

Dacă sună ca multă muncă, este într-adevăr și acesta este unul dintre numeroasele motive pentru care panza nu este folosită mai des; dar există anumite situații în care o pânză este cea mai bună modalitate de a realiza ceva, iar un randator de hartă complet personalizat este unul dintre ele (întrucât scalarea și poziționarea fiecărei țiglă erau oricum personalizate, folosind o pânză adăugând o oarecare complexitate, dar a redus capătul general) .

O modalitate de a vizualiza diferența în modul în care este construită pagina este analizând HTML-ul rezultat. Abordarea normală creează zeci de elemente și le adaugă la pagină, dar pentru abordarea pânzei există foarte puțin acolo, totul a fost o comandă de tragere în cadrul pânzei.

Diferența de structură a paginii între abordarea bazată pe element și pânza

Placi de cădere

Un caz de margine pe care să-l manevrezi la scalarea și amestecarea plăcilor este ceea ce se întâmplă dacă nu există plăci de amestecat. Dacă zoom-ul inferior nu s-a încărcat încă, atunci în loc să animăm opacitatea la nivelul următor, este mai bine să-l pornească pe deplin opac - altfel vor exista decalaje neuniforme pe hartă.

Calcularea acoperirii plăcilor de întoarcere este o zonă în care strălucește într-adevăr compoziția pânzei personalizate - fiecare operațiune de tragere poate seta opacitate personalizată și, deoarece panza este într-adevăr eficientă pentru operațiuni de desenare mici (de exemplu, dimensiunea plăcii de hartă), putem seta în mod eficient niveluri de opacitate personalizate pentru fiecare țiglă individuală. Dacă ne bazăm pe browser pentru a face acest lucru cu elemente personalizate, toate animațiile individuale ar putea cauza încetinirea.

Clientul atrage întotdeauna plăcile fallback (cele de bază) la opacitate deplină și schimbă doar opacitatea plăcilor primare (nivel de zoom țintă) pe care se suprapune deasupra. Înainte de ciclul de vopsea, se verifică dacă placa primară are o acoperire completă a defecțiunii - adică dacă s-a desenat tot ceea ce se află sub placa respectivă.

De exemplu, dacă am mări aici, de la 0 la 1 - acele plăci colorate au o acoperire completă, iar acea singură gresie gri de sub ele se suprapune complet. Acest lucru înseamnă că își pot anima opacitatea în siguranță, fără a lăsa goluri.

Totuși, contrariul nu este adevărat. Dacă am mări, de la 1 la 0 (deci plăcile colorate ar fi sub acoperirea) nu putem anima opacitatea pe faianța gri, deoarece ar lăsa anumite lacune.

O posibilă soluție în acest sens ar fi să pictezi plăcile primare fără acoperire o singură dată ca un strat de bază complet opac, apoi să pictezi plăcile în fund, apoi să schimbi în siguranță opacitatea plăcilor primare (în cel mai rău caz, ar fi amestecat cu ea însăși) - dar asta ar crește numărul operațiunilor de tragere pentru o îmbunătățire notabilă.

Un alt lucru pe care îl putem face cu opacitatea este să animăm rapid în plăci nou încărcate, astfel încât, în loc să se fixeze în loc, se produce o decolorare rapidă.

Direcția și viteza zoomului

De asemenea, clientul urmărește direcția în care utilizatorii fac zoom (în sau în afara), precum și cu cât de rapid fac zoom, și îl folosește pentru a determina dacă ar trebui să încarce plăci noi.

De exemplu, dacă măriți de la 14 la 15, clientul nu se va deranja să încarce alte plăci de la nivelul zoom-ului 14, dar în schimb va da prioritate preluării celor noi de la 15. În cazul în care s-a produs opusul, mărirea de la 15 până la 14, clientul ar încerca să încarce doar noile plăci de la 14.

Utilizează viteza zoomului pentru a decide dacă există vreun punct pentru încărcarea plăcilor sau dacă este posibil ca stratul să fie amplasat înainte ca imaginile să aibă șansa de a se încărca. De exemplu, atunci când fac zoom rapid, utilizatorul poate parcurge până la zoom 4 până la 15 în doar o secundă - și este puțin punct de încărcare 5, 6, 7, 8 ... pentru că ar fi doar o pierdere de plăci. Din fericire, pentru zoom-in putem escalada doar câteva plăci (amintiți-vă la zoom 0, țiglă reprezintă întreaga lume) pentru a păstra culorile / formele reprezentative vag.

Evitarea reducerii

Având în vedere că harta devine din ce în ce mai detaliată la niveluri de zoom mai mari, am presupus naiv că ar fi de preferat să folosim nivelurile mai înalte și să le redimensionăm la micșorare.

Redimensionarea prea multor plăci

Cu toate acestea, în practică, acest lucru nu a funcționat foarte bine - în timp ce scara tuturor poligoanelor (de exemplu, apă și parcuri) este bine păstrată, toate etichetele și toate pictogramele sunt de asemenea dimensionate, făcând o hartă care arată cu adevărat înghesuită, în special la mijloc. Spectacolul a avut și un succes uriaș, pentru că în loc să deseneze câteva zeci de plăci, au fost brusc sute disponibile pentru desen.

Pentru a evita acest lucru, am dezactivat plăcile de scădere cu mai mult de 1 diferență de zoom. Adică atunci când faceți zoom pe nivelurile fracționale ale anilor 14 (de exemplu, 14.2), poate utiliza plăci de la zoom 15, dar niciodată, vreodată, plăci de la 16 sau mai mari.

Animații

Intenția mea declarată la începutul proiectului a fost de a putea sincroniza programatic animațiile cu zoom-ul, deși are și avantajul secundar de a mă simți mai bine - este mai ușor și mai sensibil la intrarea utilizatorului. Când la niveluri de zoom integrale, se comportă la fel ca implementarea normală. Totuși cred că este destul de eficient pentru animare - poți judeca pentru tine.

Iată un exemplu care vizualizează o posibilă cale de zbor din San Francisco în Australia. [alternativ video complet, sau unul cu suprapunerea de depanare]

Zborul (și animarea lină) din San Francisco în Australia

Desigur, nu există niciun motiv să fie limitat la animații cu zoom. Un beneficiu suplimentar de a picta totul pe o pânză este că ne poate lăsa să facem unele lucruri cu adevărat creative, dacă vrem.

destination-out globalCompositeOperation

Arătat este un exemplu care utilizează modul de compoziție de destinație de pe pânză pentru a „tăia” formele din hartă - aici se suprapune o hartă monocromă peste una colorată, apoi se animă dezvăluirea. [video complet]

Utilizarea viitoare

Pe cât de amuzant a fost să scriu și la fel de util pentru acest lucru pentru prototiparea mea (a alimentat zeci de prototipuri în timpul meu în echipă) Google și MapBox au ambele redări de vectori care depășesc această abordare la scară și amestec. Îi încurajez cu bucurie pe toți ceilalți, care nu trec la upgrade-ul vectorului, pentru a considera implementarea a ceva similar. Este o tehnică destul de simplă, care nu adaugă multă complexitate clientului (cu siguranță mai puțin de lucru decât redarea vectorială), dar permite o experiență mult mai lină și mai receptivă.