O monitorizare tehnică: modul în care am construit cele mai frumoase hărți de tranzit generată automat de lume

de Anton Dubrau

În urmă cu șase săptămâni, am lansat Transit Maps și am scris această postare pe blog despre motivul pentru care am preluat sarcina mamă de a crea hărți generate automat dar totuși estetic. Am fost respinși de reacția publicului la eforturile noastre, deși nu ne-a surprins cu totul, având în vedere perioada de timp, gândirea și inspirația necesară pentru a le crea. Astăzi, ne-am îndeplinit promisiunea de a publica o monitorizare tehnică de la Anton, expertul nostru de cartograf rezident, care explică în detaliu mult ce a intrat în construirea acestor hărți.

Când vă gândiți la Transit, s-ar putea să credeți o interfață elegantă și plină de culoare. Având în vedere că suntem extrem de particulari în ceea ce privește crearea aplicației cât mai frumoasă și utilizabilă, nu este o surpriză mare. Dar UI nu este singurul lucru despre care facem parte: echipa noastră se extinde cu mult peste designeri experți, iar aplicația noastră este mult mai mult decât simplă. Sub suprafață, există o mulțime de tehnologii „dure” care o conduc liniștit.

În primul rând, calitatea noastră de backend puternic verifică sute de fluxuri de date de tranzit, rezolvă automat datele stricate și semnalează problemele care necesită investigare. Acest sistem ne permite să gestionăm 350 de fluxuri de tranzit în 125 de orașe cu o echipă mică.

Apoi există algoritmul nostru de compresie. Reduce programele de tranzit de până la 100 de ori mai mici decât furnizează agențiile de tranzit cu fișiere zip. Acest lucru permite Transit să descarce programele din întreaga regiune a utilizatorului, să stocheze datele de pe dispozitivul utilizatorului și să returneze rezultatele căutării ... în tot ceea ce durează alte aplicații pentru a solicita și încărca o singură programare. Și în timp ce utilizatorii noștri s-ar putea obișnui acum cu viteza aplicației noastre, atunci când a fost introdusă pentru prima dată funcția, aceasta a redus efectiv timpul de răspuns de la câteva secunde la 0,1 secunde. Este rapid.

Dar există o tehnologie particulară la care lucram de ani buni. Spre marea noastră bucurie (și ușurare), am lansat-o în sfârșit în această vară: hărți de tranzit generate automat.

Hărți de tranzit: Aplicația de tranzit (stânga), Apple Maps (mijloc) și Google Maps (dreapta)

Ideea în sine este aproape nouă: Google a lansat hărțile de tranzit în urmă cu aproape șase ani. Dar se dovedește că este destul de problema de rezolvat și Google (chiar și după toți acești ani) încă nu poate genera hărți de tranzit foarte frumoase sau chiar foarte utile.

Deci cum am reușit-o? Cu transpirație, lacrimi și gândire creativă.

1. Formele pe o hartă

Ideea de a crea hărți generate automat este ceva care m-a captivat încă de dinainte de a mă alătura Transit App în urmă cu trei ani. La vremea respectivă, Google era singurul jucător din industrie și, pentru a fi sincer, hărțile lor de tranzit erau cam năprasnice. Tocmai terminasem algoritmul de super-compresie menționat anterior și ne simțim pregătiți să abordăm o nouă problemă. Ne-am gândit, mai degrabă naiv, că va dura aproximativ trei luni. Nu știam prea puțin ...

Primul pas a fost să afișăm datele sursă pe o hartă. Multe dintre călătoriile din datele de tranzit subiacente conțineau deja forme reprezentând rutele pe care le-au luat vehiculele de tranzit. Dacă am desenat pur și simplu toate formele definite de toate călătoriile, am obține un fel simplist de hartă de tranzit:

Tehnologia noastră Transit Maps, aproximativ noiembrie 2014

A face acest lucru a fost relativ simplu: am creat o conductă de procesare pentru a extrage formele din datele sursă; stocarea formelor într-un format de schimb de date; s-a asigurat că datele au fost disponibile pentru dispozitiv; și a folosit bibliotecile de mapare ale dispozitivului pentru a desena un nou strat cu datele.

Uşor. Am avut acest lucru în funcțiune în câteva săptămâni.

În timp ce este aproape, nu este o hartă de tranzit reală. Toate liniile erau trasate una peste alta. Nu puteți spune în ce linii se merge unde și singura linie vizibilă a fost cea care a fost trasată ultima dată. Pentru hărți diagrama corespunzătoare în care puteți urmări liniile cu degetul, aveți nevoie de ele pentru a fi desenate în paralel și pentru a se intersecta cât mai puțin.

2. Potrivirea cu OpenStreetMap

Construirea hărților noastre cu forme a ridicat alte probleme: ce ar trebui să facem atunci când o agenție nu furnizează date privind formele sau dacă o agenție furnizează forme foarte slabe? Singurele date geografice pe care le-am avea în aceste cazuri ar fi locațiile de oprire. Sigur, am putea trasa linii drepte între opriri, dar este urât, dezordonat și confuz.

Este și problema cu Hărțile de tranzit Google. La Berlin, Google realizează conexiuni liniare între opriri; la Londra, ei folosesc un fel de interpolare spline care nu urmărește piesele reale; și în LA, folosesc formele oferite de agenție, chiar dacă calitatea formelor este într-adevăr destul de proastă.

Google Maps din Berlin (stânga) și Londra (dreapta)

Ce este amuzant este că, atunci când faceți zoom pe hărți, veți vedea că Google are adesea date pentru liniile de cale ferată subiacente, ceea ce pune întrebarea: de ce nu combină traseele cu datele de formă? Au decis că nu este important?

Deși Google ar putea să nu creadă că este important, cu siguranță, da. Sigur, nu avem acces la bogatele date hărți de bază ale Google, dar putem utiliza următorul lucru: OpenStreetMap (OSM). Și datorită comunității lor de harti geeki voluntari, OSM are practic toate piesele pentru liniile de transport feroviar pe care le folosim în aplicația noastră.

Datele feroviare ale OpenStreetMap

Prin crearea unei forme de-a lungul grilei de strada OSM care conectează toate punctele de-a lungul unei rute date, am putea genera formele noastre de tranzit! Prin urmare, am creat un algoritm de programare dinamic care urmează drumuri sau trasee care pot fi utilizate de liniile de tranzit. Algoritmul de creare a formei consideră ce tip de vehicul circulă pe linie și minimizează erorile de potrivire (adică distanțele dintre forma generată și locațiile reale ale punctelor sursă).

Iată un exemplu. În diagrama de mai jos, avem o călătorie cu trei opriri și nu avem nicio informație de formă. Extragem setul de piese pe care călătoria le folosește de pe OSM (linii gri). Algoritmul nostru de potrivire găsește apoi o traiectorie (linie neagră) care urmărește OSM, reducând în același timp lungimea și erorile la opriri (e1, e2, e3).

Procesul de potrivire a formei-OSM

Este greu să facem ca acest algoritm să funcționeze pentru toate cazurile, așa că, uneori, trebuie să furnizăm parametri pentru ca liniile specifice să funcționeze. În general, ne oferă forme de înaltă calitate pentru toate liniile de tranzit public de care avem nevoie astăzi și pentru majoritatea celor de care vom avea nevoie în viitor - chiar și în țările în curs de dezvoltare unde OSM este adesea cele mai bune date disponibile.

Un exemplu care motivează potrivirea OSM chiar și atunci când formele sunt disponibile și de o calitate decentă: în Montreal-Vest, formele furnizate nu urmează pista (imaginea din stânga), deci la nivel de stradă pare îngrozitor. După potrivirea OSM (dreapta), liniile sunt mult mai netede.

3. Procesarea în spațiul pixelilor: scheletizarea

OSM ne-a oferit formele, dar liniile erau încă trase una peste alta. Hărțile de tranzit real au linii trase în paralel. Ceea ce trebuia să facem a fost să identificăm segmente comune, unde călătoresc pe aceeași stradă, apoi să „prindem” liniile respective.

Deci, cum face Google? Se pare că calculează segmente partajate uitându-se la opriri. Atâta timp cât două linii împărtășesc aceleași opriri, acestea sunt „blocate” împreună. Dar atunci când următoarea oprire nu este partajată, liniile „desființează”:

Rândurile Google uită că se cunosc imediat la ultima oprire unde se opresc împreună.

Dar nu în felul acesta călătoresc cu adevărat trenurile și autobuzele! Liniile stau împreună pentru o anumită distanță înainte de a se diverge. Ceea ce aveam nevoie era un algoritm care să găsească locul în care liniile încep să se ramifice în viața reală.

Am încercat să calculăm separațiile de rută în spațiul vectorial, ceea ce părea simplu la început: luăm două linii care călătoresc îndeaproape și apoi găsim linia centrală a segmentului partajat. Totuși, acest lucru s-a dovedit a fi surprinzător de complicat, întrucât am continuat să rulăm în exemple simple care să ne rupă algoritmul. O buclă mică în traseu ar arunca linia centrală la infinit și, de asemenea, a trebuit să ne ocupăm de mai multe linii, mai multe ramuri, diferite modele de oprire ...

După două luni de care ne-am legat de creierele de pe tastaturi, am aruncat în cele din urmă prosopul. Pur și simplu nu am găsit o soluție generală stabilă, care să funcționeze în mod fiabil, până ...

Spațiu pixel pentru salvare!

În loc să prelucrăm liniile în spațiul vectorial, am decis să încercăm ceva nebun. Am folosit spațiu pentru pixeli.

De obicei, procesarea pe bază de pixeli se face pentru date bazate pe imagini. Este foarte neobișnuit pentru procesarea GIS, deoarece la o rezoluție de 1px / metru, imaginea noastră ar fi de 64 de terabyți. Memoria este ieftină în aceste zile ... dar nu chiar atât de ieftină!

Deci cum am făcut-o? Am implementat o bibliotecă specială de imagini slabe, care ar putea trata aceste imagini monocromatice foarte mari, cu relativ puține zone albe.

Am creat apoi un algoritm pentru a desena forme de tranzit pe o pânză uriașă alb-negru reprezentând întreaga lume, unde fiecare pixel este echivalent cu un metru pătrat. Fiecare linie a fost desenată gros pe pânză, așa că oriunde liniile erau apropiate, pixelii lor s-au contopit.

Odată ce toate liniile au fost desenate, am folosit un proces de „scheletizare” pentru a subția succesiv liniile până când fiecare a avut doar un pixel grosime. Deci, în timp ce liniile nu mai erau contopite, ele au rămas conectate, menținând aceeași topologie. Aceste linii subțiri reprezintă locul în care liniile de tranzit călătoresc împreună și dezvăluie structura rețelei.

Albul reprezintă liniile de tranzit trase. „Scheletul” este acoperit cu roșu.

Deși acum aveam liniile centrale ale rețelei, am distrus mai multe informații decât am obținut. Ce aveam acum, erau o grămadă de pixeli care denotau scheletul, ceea ce însemna că știam că fiecare linie trebuie să călătorească de-a lungul acestui schelet, dar tot trebuia să ne dăm seama ce linii călătoreau.

Folosind scheletul, acum am reconstruit liniile, spre deosebire de formele pe care le aveam anterior. Am prelucrat apoi rețeaua rezultată pentru a scăpa de gleturile introduse de scheletizare.

Acest pas a fost lung și obositor. În total, desenul, scheletizarea, construirea rețelei și eliminarea glitch-ului au avut undeva în jur de șase luni pentru a se dezvolta. (Atât de mult pentru că am făcut totul în trei!)

Dar rezultatele finale au fost satisfăcătoare. Am avut o reprezentare internă a liniilor care călătoresc împreună și se divergeau. Arăta astfel:

Când am redat liniile în paralel, am obținut acest lucru:

Succes!

Destul de bine pentru o versiune 1. Mult mai bine decât Google, văzând că poți să tegolești mai mult sau mai puțin unde merge fiecare linie. Eram gata să extindem Hărți de tranzit! Și apoi… Apple Maps s-a întâmplat.

În vara anului 2015, după ce am lucrat la hărțile noastre pentru o bună parte a unui an, eram în sfârșit gata să lansăm prima noastră versiune de Transit Maps. Apoi Apple și-a extins hărțile de tranzit și au fost foarte drăguțe.

Hărțile frumoase de tranzit ale Apple

Au ridicat instantaneu bara pentru cum ar trebui să arate hărțile de tranzit. În desenele și desenele noastre, obiectivul final a fost ceva similar cu (sau mai bine decât) a lansat ulterior Apple, dar intenționam să ajungem acolo după lansarea versiunii noastre 1.

În comparație cu Apple, versiunea noastră 1 propusă a fost un fel de mediocră. Designerul nostru-CEO a decretat că învingerea Google nu este suficient de bună - trebuie să jucăm și noi cel puțin în aceeași ligă ca Apple.

După o examinare mai atentă, am emis ipoteza că Apple și-a desenat hărțile manual. Au existat decalaje uriașe între eliberarea de noi orașe și s-a întâmplat ceva ciudat despre felul în care arătau hărțile - de parcă ar fi fost desenate de oameni, nu de computere. Aceasta a însemnat că, deși hărțile noastre nu erau chiar la fel de frumoase, algoritmul nostru era încă înaintea lor.

În acest moment, știam și că partea grea se afla în spatele nostru. Ne-am gândit o rețea care să ne permită să tragem linii în paralel. Acum, nu trebuia decât să-l facem să pară bine.

4. Comandarea liniilor de tranzit folosind programare liniară integrală

Înainte de a ne publica hărțile, trebuia să scăpăm de urcarea, inutilă a traversării de linii, ceea ce le transforma într-o groază de spagheți.

Dacă am putea sorta liniile pentru a minimiza dezordinea vizuală în apropierea intersecțiilor, am avea o hartă publicabilă. Pentru a face acest lucru, a trebuit să decidem ce linii vor merge la stânga și care vor merge dreapta, pentru a minimiza traversările acestora.

Google a avut (și mai are) o problemă similară - cu excepția liniilor lor care se încrucișează reciproc, chiar dacă există doar opriri și nu există intersecții.

Oh, vino pe Google! Liniile ar trebui să rămână organizate.

Pentru noi, încrucișarea criss-ului s-a întâmplat doar acolo unde liniile s-au alăturat și divergent, așa că deja ne descurcam mai bine decât algoritmul Google. Acest lucru se datorează faptului că stocam secțiuni partajate pe bază de geografie.

Deci cum am scăpat de spaghete? În primul rând, am încercat o soluție euristică - sortarea liniilor în funcție de locul unde se termină - dar acest lucru a eșuat adesea, lucrând în unele locuri, dar nu în altele.

Pentru a îmbunătăți soluția euristică, am creat un model matematic care să „puncteze” o anumită ordonare a liniilor, penalizând încrucișarea liniilor, precum și alte dezordini vizuale.

Mai multe scenarii posibile de intersecție, marcând surse de penalizări folosind cercuri roșii

După cum vă puteți aștepta, evitarea unei încrucișări într-un loc de pe hartă ar putea crea alta în altă parte. Totul este conectat! Deci, ce am făcut? Am găsit ordonarea liniilor care au cel mai mic punctaj penal penal global.

Programarea liniară integrală a fost ceea ce ne-a permis să explorăm toate posibilitățile și să găsim o soluție care să minimizeze global funcția de penalizare. Dar timpul de procesare a programării întregi-liniare este exponențial în dimensiunea problemei: rezolvarea unei probleme poate dura o secundă; o altă problemă mai dificilă poate dura un an! Acest lucru a făcut să fie riscant de utilizat, chiar și în pre-procesare „offline” în backend.

Eram îngrijorați. Prelucrarea datelor din Chicago ne-a luat ore întregi. O zonă mai mare precum Coridorul de Nord-Est (Boston până la Washington) ar putea dura săptămâni! Din fericire, am găsit un alt plan de atac: unul care a permis soluționării integrale-liniare-programare să exploreze spațiul cu probleme mai eficient și să găsească soluții optime mai rapid. Ceea ce a avut anterior o oră, acum a luat 0,2 secunde.

Considerând o optimizare de acest fel în acțiune este nesimțit: când vezi că algoritmul ia decizii, este ca și cum ai fi martor la un matematician genial rezolvând fără probleme probleme cu cele mai clare și mai concise soluții.

Sortare înainte / după linie

Cu celelalte etape de procesare deja finalizate în pre-procesare pe server, datele au fost acum stocate în fișiere binare și trimise la dispozitiv pentru redarea efectivă a hărților la orice nivel de zoom dorit.

5. Rotunjirea cercurilor-arc a liniilor

Cu toate acestea, încă nu am fost destul de terminați. Hărțile de tranzit schematic desenate manual nu arată în realitate cu hărțile prezentate mai sus. Liniile lor sunt frumos rotunjite cu tranziții netede la intersecții. Am dorit ca hărțile noastre să aibă un aspect similar rotunjit.

Când liniile s-au trasat în jurul colțurilor, am dorit ca acestea să rămână perfect paralele, chiar și în cazuri potențial degenerative, ca în Chicago. Acolo, un număr mare de linii se deplasează împreună în jurul curbelor ascuțite, astfel încât desenarea lor în paralel ar putea duce la înghesuirea liniilor pe interiorul cotului.

De obicei, rotunjirea se face folosind curbele bezier, care par a fi ușoare în curbe. Dar, pentru a rămâne fideli aspectului hărților de tranzit schematic, curbele bezier nu erau tocmai corecte. Hărțile de tranzit au linii drepte care se încadrează brusc în segmente de arc circular. Așa că am folosit segmente cu arc pentru rotunjire.

De asemenea, spre deosebire de curbele bezier, orice linie paralelă cu un arc de cerc este în sine un arc de cerc. Atâta timp cât raza este suficient de mare, ni s-a garantat că nu avem cazuri degenerative.

Am venit cu un algoritm personalizat care, având o formă, ar elimina și adăuga puncte pentru a-l rotunji folosind segmente de arc circular. Acesta garantează o rază minimă dată prin simplificarea geometriei după cum este necesar. Raza minimă depinde de lățimea totală a tuturor liniilor paralele.

Forma rezultată este netedă. Este alcătuit în întregime din linii drepte și arc-segmente, ceea ce înseamnă că putem întotdeauna trasa linii în paralel fără niciun fel de artefacte sau cazuri degenerative.

Această abordare ne-a oferit ceva de genul:

Rotunjirea se întâmplă numai de-a lungul segmentelor partajate. Ați putea observa, de asemenea, că am eliminat toate intersecțiile. Abordarea intersecțiilor a fost o problemă majoră, deoarece a trebuit să ne asigurăm că fiecare linie continuă de la un segment la altul și se leagă corect. De asemenea, am folosit algoritmul generator de arc pentru a avea același aspect rotunjit. Iată rezultatul final:

Destul de grozav, nu? Dar în timp ce erau drăguți ... încă păreau ciudat dezbrăcați. Acest lucru se datorează faptului că lipseau opriri.

Așa că am decis să ne oprim din nou asupra versiunii - și să adăugăm un ultim pas.

6. Adăugarea opririlor

Adăugarea de opriri poate părea simplă, dar de fapt necesită o cantitate corectă de procesare pentru a propaga informațiile de oprire prin conducta lungă pe care am creat-o.

De asemenea, am întâlnit multe cazuri în care mai multe opriri din date corespundeau de fapt unei singure stații fizice, așa că a trebuit să le dărâmăm într-o singură oprire.

Iată ce am făcut. Pentru opriri cu mai multe linii, am desenat o bară albă cu un contur negru (spre contrast) pe toate liniile. Pentru opriri pe o singură linie, am desenat un cerc simplu folosind culoarea acelei linii de tranzit. De asemenea, am adăugat o suprapunere albă pentru a reduce contrastul stratului de hartă de mai jos. Acesta este rezultatul final:

Pentru a permite utilizatorilor să activeze și să oprească selectiv liniile din pagina de setări a aplicațiilor noastre, am decis că rotunjirea, precum și unele opriri și procesări ar trebui să fie efectuate pe dispozitiv. Deci, în New York City, puteți dezactiva toate liniile de tranzit bazate în New Jersey (sau toate liniile NYC dacă locuiți în New Jersey). Cu atât de multe linii de tranzit în anumite zone, acest lucru permite utilizatorilor să creeze hărți complet personalizate.

Rețineți cum sunt cele mai recente linii bazate pe ce linie sunt active și cum oprirea își schimbă culoarea.

Concluzie

Așa am procedat așa. Sigur, implementarea hărților de tranzit generate în mod automat a necesitat multă muncă, dar a meritat. Hărțile noastre sunt mult mai puternice decât PDF-urile obișnuite să obțineți de la agenții, nu vă feriți de hârtiile pe care le pliați și le blocați în portofel. Care sunt principalele diferențe?

Hărțile noastre de tranzit sunt scalabile, astfel încât putem adăuga cu ușurință orașe noi în același stil vizual, oriunde în lume ne extindem până la următoarea. Sunt personalizabile, astfel încât utilizatorii pot activa / opri rețele și moduri pentru a crea hărți de tranzit personalizate. Și sunt, de asemenea, contextuale: spre deosebire de un PDF al unei hărți a agenției, hărțile noastre încorporează locația dvs., oferindu-vă un sentiment de locul în care vă aflați în raport cu liniile din apropiere și ajustează aspectul în funcție de nivelul de zoom.

Și în cele din urmă, hărțile noastre de tranzit diagrama oferă mai mult decât doar informațiile de bază despre sistemele de tranzit. Ele sunt emblematice ale orașelor: piese importante de artă funcțională care leagă oamenii de mediile lor. Vrem să contribuim la construirea acestei conexiuni și credem că noile noastre hărți de tranzit fac exact asta.

Suntem încântați să continuăm îmbunătățirea, dar suntem mulțumiți de ceea ce am realizat până acum. Am lansat cu 55 de orașe. Răspunsul la postarea noastră pe blog care a comparat hărțile noastre cu Google și Apple a fost incredibil de pozitiv. Pentru echipa de backend, este minunat să le facă pe oameni să vadă și să aprecieze munca și efortul pe care îl depunem în ceea ce determină experiența aplicației. Ne motivează să continuăm să ne împingem tehnologia în continuare.

Dincolo de asta, mai avem încă multe probleme „grele” de rezolvat. Vom continua să lucrăm sub capotă, nu doar pentru a avea cea mai frumoasă aplicație cu cea mai bună interfață de utilizator, ci cea mai funcțională, puternică și precisă aplicație de tranzit de acolo.

Vrei să te joci cu hărțile noastre?
Puteți obține gratuit Tranzit în App Store și Google Play. Sau aflați mai multe despre companie pe site-ul nostru.

Simțiți că vă confruntați cu provocări ca acesta pentru o viață? Angajam!