Hal pertama yang ingin saya katakan adalah sulit. Jauh lebih sulit daripada yang saya kira. Saya memiliki pengalaman yang sangat sulit dalam membawa produk untuk dirilis di tempat kerja, tetapi saya tidak pernah meraih proyek pribadi. Mereka semua berakhir dengan prototipe dengan tingkat jijik yang berbeda-beda, tetapi yang ini tampaknya bertahan. Saat ini, telah diluncurkan untuk 80+ negara (seluruh Eropa, Asia dan Amerika Utara), pada kedua platform seluler, dan pada akhir artikel akan ada tautan unduhan - oleh karena itu, saya mengundang semua orang yang tertarik untuk mencoba, merusak, dan memarahi.

Inilah pemikiran singkat yang mengawali semuanya:
Menurut saya, pencarian pada peta seluler yang ada dilakukan untuk pejalan kaki dan sama sekali tidak bekerja untuk pengemudi. Anda harus berhenti, menyelidiki peta yang dipenuhi dengan informasi dan iklan berlebih, melihat ikon kecil. Ini tidak nyaman, itu tidak akan membantu Anda keluar di tempat yang tidak dikenal, pada akhirnya itu hanya berbahaya. Diperlukan solusi yang intuitif dan bersih yang tidak mengganggu dan tidak membuat Anda melambat.
Pada bagian
pertama, saya menggambarkan jalan saya dari pemikiran sederhana ini ke solusi yang berfungsi, dan kemudian saya akan menjelaskan bagaimana saya menyeret solusi ini ke rilis.
Untuk menghemat waktu Anda, saya akan mulai dengan menceritakan kembali bagian sebelumnya secara singkat: di sana saya menulis bahwa alih-alih mencari, saya memutuskan untuk menggunakan pemindaian dalam gerakan, dan antarmuka aplikasi disederhanakan sebanyak mungkin. Alih-alih jalur masuk yang tidak masuk akal untuk pengemudi, ia menambahkan beberapa tombol besar untuk hal-hal yang dapat berguna di jalan: pompa bensin, pengisian daya, ATM, parkir, farmasi. Alih-alih peta, saya membuat daftar, dan ketika saya memilih hasilnya, navigasi melalui Apple / Google Maps terbuka. Untuk aplikasi ini, saya memutuskan untuk menggunakan Flutter (pada saat yang sama saya jadi tahu hewan apa itu), saya mengambil data dari OpenStreetMap. Saya menyelesaikan cerita saya dengan fakta bahwa prototipe yang lebih atau kurang waras sudah siap.
Kemudian semuanya memakan waktu sekitar 4-5 bulan, kemudian perubahan dalam kehidupan dimulai dan proyek berjalan di pinggir jalan - dan saya mulai bosan. Sebulan kemudian, saya membersihkannya, menyegarkannya di kepala saya dengan menulis artikel di hub dan memutuskan: ayo selesaikan. Siapa pun yang tahu perbedaan antara prototipe dan produk akan tersenyum sedih di tempat ini.
Apa yang terjadi selanjutnya memakan waktu empat bulan lagi. Saya mendapatkan pelacak tugas, secara umum membentuk daftar masalah, mengumpulkan perangkat untuk pengujian. Di malam hari dan di akhir pekan, saya duduk di tempat kerja dan memajukan proyek. Pada saat-saat apa tampaknya daftar itu hanya bertambah, dan saya tenggelam dalam nuansa tak berujung dan sentuhan akhir. Dia mengambil dirinya sendiri, melemparkan sesuatu, di suatu tempat sebaliknya menuju perfeksionisme. Selanjutnya, saya akan mencoba untuk berbicara tentang poin paling menarik dalam pengembangan proyek yang tampaknya sederhana ini.
Teknologi
Arsitektur umum
Di suatu tempat di tengah-tengah pekerjaan, perasaan mulai muncul bahwa arsitektur menyebar di luar kendali. Terlalu banyak komponen dan koneksi telah terputus, beberapa kemacetan telah diraba. Untungnya, proyeknya kecil, dan saya merasakannya tepat waktu, jadi menertibkannya tidak sulit. Pada tingkat komponen individu, ini bermuara pada refactoring dan membuang perpustakaan yang tidak perlu, di tingkat global, saya menyebarkan fungsionalitas ke 3 server kecil yang saya mulai di
DigitalOcean .
- Server API (Python) - peletakan-server utama, kita beralih ke sana. Tidak banyak logika, terutama pembentukan hasil untuk keluaran. Paling ekonomis dari segi sumber daya.
- Server elastis (Jawa) - Elasticsearch dan Photon (open source geocoder) berputar di atasnya. Mereka menggunakan indeks yang sama dengan mana seluruh planet diimpor dari OpenStreetMap. Fungsi server: mencari tempat dengan landfill dan geocoder. Secara alami, elastis sangat cepat dan ringan, sehingga server juga tidak terlalu berminyak.
- Server geo (Node) adalah yang paling sulit. Berdasarkan Open Source Routing Machine, saya menulis api kecil, dan tugasnya mencakup semua perhitungan geografis: meletakkan rute, menghitung isochrones, dan menghasilkan ubin. Setiap operasi individu tidak terlalu banyak akal, tetapi lusinan dari mereka diperlukan untuk pencarian apa pun, dan ini menjadi hambatan. Saat ini, pada server ini 16 GB RAM, dan secara umum, semuanya berfungsi dalam sepersekian detik - kecuali untuk menghasilkan ubin. Ketika ada banyak dari mereka dalam antrian, Anda dapat menunggu gambar dengan kartu dalam beberapa detik. Untungnya, mereka muncul pada klien secara tidak sinkron, dan ini tidak terlalu merusak keseluruhan gambar (saya harap).
Selain itu, saya memutuskan untuk memberi makan ekstrak dari OpenStreetMap untuk perhitungan geografis secara terpisah oleh negara. Kerjanya seperti ini: kami membuat permintaan pertama dengan koordinat ke geocoder kami, itu menentukan negara, dan kemudian kami mengambil file yang diunduh hanya dari negara ini untuk manipulasi yang kami butuhkan. Ini perlu, karena bahkan server saya yang agak kuat tidak dapat mengubah ekstrak lebih besar dari dua gigabytes - proses cepat memakan semua memori dan tersedak. Untungnya, hampir semua negara memenuhi batas ini, kecuali Amerika Serikat: monster ini harus dibagi menjadi beberapa negara bagian. Akhirnya, untuk mempertahankan satu setengah ratus ekstrak, saya memutuskan untuk menulis banyak naskah yang memeriksa kesehatan, perbaikan, dan pembaruan mereka.

Secara umum, semua ini terdengar lebih rumit daripada bekerja dalam praktiknya. Pada prinsipnya, saya puas dengan solusi yang dihasilkan - ini memiliki cadangan yang baik untuk penskalaan dengan jumlah wilayah yang didukung dan tingkat beban.
Isochron dinamis
Untuk waktu yang lama, salah satu masalah utama bagi saya adalah kepadatan hasil yang heterogen. Alasannya cukup dimengerti - ini adalah kepadatan heterogen dari jaringan jalan itu sendiri dan bangunan di atasnya. Di kota berukuran sedang dalam radius 5 menit akan ada 2-3 ATM, dan sekarang kita akan pindah ke pusat kota metropolis - dan untuk 5 menit yang sama akan ada 20 atau 30 hasil. Akhirnya, kami melompat ke pedesaan dan mengamati hasil 0 yang hampir dijamin sampai kami semakin dekat ke kota dan radius pencarian menangkap sesuatu.
Masalah ini memberikan beban non-linear dan tidak dapat diprediksi pada server, dan yang paling penting - pengalaman yang agak buruk bagi pengguna. Menambahkan filter ke opsi (5 menit, 10 menit, 30 menit) pada dasarnya tidak menyelesaikan apa pun. Di desa, bahkan radius 30 menit pun tidak dapat mengembalikan apa pun, tetapi dalam megalopolis, bahkan 5 menit akan membanjiri Anda dengan hasil. Selain itu, kami menambahkan fungsionalitas ekstra, ke dalam tombol-tombol yang harus digunakan pengemudi. Secara umum, omong kosong, Anda memerlukan solusi yang berbeda secara fundamental.
Ketika solusi ditemukan, ternyata sangat sederhana. Alih-alih bergerak dari arah yang berlawanan dan mendorong pengguna untuk memilih radius pencarian, kita dapat membuat radius ini otomatis. Logikanya sebenarnya elementer:
- Anda menetapkan batas pada hasil - misalnya, setidaknya 1 dan tidak lebih dari 20 - dan mulai dengan 10 menit
- Lakukan pencarian berdasarkan tempat. Sejauh ini, kami tidak perlu mendapatkan arahan kepada mereka, jadi kami hanya perlu menghitung isochrone dan menyaring dengan poligon di elastis - kedua operasi sangat murah
- Jika jumlah hasil merayap keluar satu arah dari batas (dalam kasus kami 0 atau 20+), bagi atau gandakan waktunya dengan 2 dan lakukan pencarian lagi. Jika sudah termasuk dalam batas, maka kita sudah membangun rute, urutkan berdasarkan waktu, dll.
Sebenarnya, ini sedikit lebih rumit dan ada beberapa nuansa, misalnya, kotak kota yang sangat padat, ketika kita telah mengurangi waktu seminimal mungkin, dan masih terlalu banyak hasil. Di sini sudah perlu untuk menyortir, dan karena itu untuk meletakkan rute, yang agak mahal. Namun, ini adalah kasus yang ekstrem dan tidak terlalu mencolok.
Pada kenyataannya, seseorang tidak mungkin untuk menggulir daftar di bawah 5-6 posisi, sehingga dalam 95% skenario isochron dinamis menyelesaikan masalah. Kami menghapus hambatan - jumlah hasil yang tidak dapat diprediksi - dan menjadikan beban pada server geografis untuk semua permintaan hampir datar. Memeriksa ini sangat mudah:
Cara lama: ambil radius 10 menit dan 30 hasil
Hasil: 1 permintaan isochron + 30 permintaan untuk rute = 31
Cara baru: periksa, 30 hasil banyak, bagi radius menjadi setengah, sekarang kami mendapatkan 10 hasil
Hasil: 2 permintaan untuk isochrone + 10 permintaan untuk rute = 12
Logika peta baru
Pada bagian terakhir, saya menggambarkan mekanisme untuk menghasilkan kartu dengan rute yang telah ditentukan. Kemudian ternyata menjadi sangat rumit dan mahal dalam hal komputasi, tetapi saya sangat menyukainya sehingga saya memutuskan untuk meninggalkannya. Pada saat yang sama, saya mengerti bahwa dalam bentuk mereka saat ini mereka memiliki sedikit manfaat praktis - tidak jelas dari mereka ke mana Anda akan pergi, dan mereka semua berbalik ke utara. Itu perlu untuk disempurnakan.
Hal pertama yang saya putuskan untuk lakukan adalah menyebarkan peta secara real time menggunakan kompas. Dalam bergetar, ini dijelaskan oleh logika mikroskopis dan bekerja sangat cepat, namun, dengan 10+ hasil yang terus berputar, kinerja mulai mengering. Selain itu, tampak sangat memuakkan: pada kenyataannya, gambar statis berputar, dan ini lebih membingungkan selama perjalanan daripada yang entah bagaimana membantu.
Gagasan berikutnya adalah untuk menunjukkan pada peta arah pergerakan panah. Itu sangat sederhana - saya sudah memiliki vektor dihitung, dan semua yang harus saya lakukan adalah menghasilkan bentuk geometris panah. Pada saat yang sama, dalam posisi statis, kartu terus menunjukkan posisi pengemudi dengan spidol bulat. Ada satu peringatan - perlu menormalkan ukuran penanda dan panah untuk tingkat zoom yang berbeda. Ini tampaknya menjadi tugas yang sederhana, tetapi saya terjebak di sana untuk waktu yang lama. Masalahnya adalah ini: Saya membuat semua simbol pada peta dalam meter, dan mengambil sebagai dasar fraksi dari ketinggian seluruh peta dalam meter. Ternyata selama pembuatan peta - menentukan kotak pembatas kotak, menempelkan dan memotong ubin, dll. - kesalahan terakumulasi, dan kesalahan kecil ini pada akhirnya menyebabkan ukuran penanda yang sangat berbeda secara visual. Terutama neraka adalah situasi dengan kartu skala kecil. Saya tidak akan masuk ke rincian solusi, namun, karena kesalahan ini, logika pembuatan kartu harus sepenuhnya digambar ulang.
Turf banyak membantu dengan ini - seperangkat alat yang hebat untuk memanipulasi geodata.
Dengan kartu panah sudah lebih berguna, tetapi masih ada sesuatu yang hilang. Setelah pengujian langsung, menjadi jelas bahwa semua kartu diputar ke utara. Dalam statika, ini tidak mencolok, tetapi langsung menjadi jelas ketika Anda berada di belakang kemudi. Pengemudi secara tidak sadar mengharapkan panah untuk selalu mengarah ke atas saat mengemudi. Setelah menemukan ini, saya kembali duduk untuk bekerja. Sekali lagi ini adalah salah satu tugas yang tampaknya sangat sederhana, tetapi Anda akan menghabiskan beberapa hari setelahnya. Tampaknya - menghitung azimuth, dan putar GeoJSON terakhir sebelum rasterisasi. Tapi ada satu nuansa lagi - GeoJSON akhir ini dihasilkan oleh kotak pembatas langsung, dan, diputar dan dipangkas di atasnya, ia mendeteksi tempat-tempat kosong.

Pada diagram di atas, saya kira-kira memberikan solusi. Akibatnya, ternyata tidak terlalu mahal dalam hal sumber daya dan mencakup 99% dari skenario (saya pikir bug akan naik di suatu tempat dekat kutub). Secara umum, server geo-komputasi masih merupakan bagian paling intensif dari proyek, tetapi sekarang kartu rutenya, selain estetika, juga sangat praktis. Saya bahkan mencoba untuk sampai ke tempat menggunakan kartu-kartu ini secara eksklusif, tidak termasuk navigasi. Dan bahkan tiba.

Kualitas data
Saya mengambil semua data saya dengan cara yang berbeda dari OpenStreetMap. Seperti yang Anda ketahui, sumber daya ini 100% nirlaba dan didukung oleh pikiran kolektif. Ini adalah plus (gratis dan dengan struktur yang jelas), ini juga minus - datanya sangat heterogen.
Pada tingkat tinggi, ini berarti cakupan bumi tidak merata secara keseluruhan: di negara-negara dan kota-kota dengan banyak penggemar, setiap halaman dan jalur dijelaskan, dan di tempat-tempat lain hampir ada zona kosong dengan objek-objek sederhana yang samar. Data diperbarui dengan ketidakmerataan yang persis sama. Saat menguji aplikasi saya, saya menemukan beberapa kali pompa bensin baru, kafe, dan kadang-kadang seluruh jalan yang belum saya letakkan di peta. Mengeluh tentang hal ini bodoh: Google yang sama menghabiskan anggaran astronomi dan berisi seluruh staf mobil yang bertanggung jawab atas relevansi datanya. Jadi di sini yang terbaik yang bisa kita lakukan adalah menyinkronkan lebih sering dengan ekstrak OpenStreetMap. Semoga sukses untuk komunitas mereka.
Tetapi pada tingkat yang lebih rendah, karena pengeditan peta yang kacau, ada sejumlah masalah lain yang dapat diselesaikan sepenuhnya. Ini terutama menyangkut data sampah dan duplikat. Keragaman kekacauan ini sangat mencolok: tempat yang sama dapat digambarkan 3 kali dengan cara yang berbeda, institusi tidak memiliki nama, tipe dan tag yang diletakkan secara salah, dan seterusnya. Semua ini tidak memiliki solusi terpadu, melainkan diperlukan tindakan kompleks untuk mensistematisasikan konten. Sebagai contoh, saya memiliki kondisi berikut:
Ada beberapa sinonim dan variasi tag yang sama -> kami uraikan kamus alias (mis. Parking, parking_space, parking_entrance, dll.).
Ada beberapa tempat dengan tipe dan koordinat yang sama:
- jika setiap orang tidak memiliki nama -> jenis tempat menjadi namanya
- hanya satu yang memiliki nama -> ambil
- setiap orang memiliki nama dan mereka berbeda -> mengambil nama belakang secara kronologis
Ada beberapa tempat dengan tipe yang sama dan koordinat yang hampir sama:
- jika setiap orang tidak memiliki nama -> kemungkinan besar duplikat, kami tidak akan mempersulit. Gabungkan menjadi satu titik dengan koordinat rata-rata, di mana jenis tempat menjadi namanya. Seorang pria akan datang dan mengerti
- hanya satu yang memiliki nama -> hal yang sama, hanya sekarang kita sudah memiliki nama
- setiap orang memiliki nama dan mereka berbeda -> tetapi ini adalah sebuah cluster
Cluster dalam kasus kami adalah kartu di header tempat beberapa tempat dijelaskan. Paling sering ini adalah kelompok toko atau pompa bensin di dekatnya. Atau misalnya, satu ATM terletak di dalam gedung bank, dan yang lainnya di luar. Mereka tidak mewakili kesulitan bagi kami: kami menghitung rata-rata koordinat dan merencanakan rute ke mereka. Di antarmuka, kami menunjukkan ini dengan bersih dan sederhana:

Antarmuka dan Desain
Jadi kebetulan pada saya bahwa sebelum dimulainya pengembangan, saya biasanya sudah membayangkan gambaran akhir. Pada saat yang sama, saya tidak suka menggambar diagram dan konsep, lebih memilih untuk membentuk desain yang paralel dengan fungsi (jika ini adalah proyek saya, tentu saja). Pendekatan berulang ini sangat keren di satu sisi, karena memungkinkan Anda untuk beralih antara visual dan kode, di sisi lain - kadang-kadang Anda harus mengulang semuanya terlalu sering. Hal yang sama terjadi di sini: Saya sepertinya telah menyekop antarmuka paling sederhana seratus kali. Mulai dari hal sepele seperti ikon dan indentasi, diakhiri dengan komposisi kartu, menu, dll. Saya tidak akan menjelaskan semuanya, saya akan membahas masalah-masalah utama dengan cepat. Jika Anda tidak tertarik dengan desain, silakan lewati saja.
Palet warna
Untuk waktu yang lama saya tidak bisa mengerti apa yang harus dilakukan dengan palet. Saya benar-benar ingin menetapkan kategori tempat dengan warna berbeda, kecuali hijau - saya memutuskan untuk menyimpannya sebagai aksen. Saya memilih warna yang mudah dibedakan dan kaya, semuanya tampak baik-baik saja. Setelah beberapa waktu, saya menemukan bahwa biru untuk pompa bensin menggemakan biru, yang pada peta menunjukkan posisi pengemudi. Dia tidak melakukan apa-apa dengan ini, membiarkannya apa adanya - tetapi perfeksionis internal tidak puas.

"Di jalan" dan "Anda sudah dekat"
Setelah logika muncul yang menentukan arah pergerakan pengemudi, menjadi mungkin untuk membagi rute menjadi "sepanjang jalan" dan sisanya. Seperti yang sudah saya katakan, ini ditentukan oleh segmen pertama dari rute yang diletakkan ke tempat itu: apakah itu bertepatan dengan segmen terakhir dari rute pengemudi. Jika demikian, maka kita sudah pergi ke tempat ini. Kemudian muncul pertanyaan tentang bagaimana menampilkan ini di antarmuka. Selain perubahan pada peta yang saya jelaskan di atas, ide muncul dari lempeng "Di jalan" (atau "Menuju" dalam bahasa Inggris - sepertinya artinya sama). Saya menggunakan kembali dadu yang sama untuk skenario lain: ketika jarak ke tempat yang ditemukan kurang dari 25 meter. Maka tidak masuk akal untuk mendapatkan arahan, saya menyembunyikan peta dan menulis bahwa Anda sudah dekat ("Anda dekat" / "Lihat sekeliling").

Kartu komunitas
Pada awal pengembangan untuk debug saya menggunakan peta statis dari Google untuk melihat isochron dan hasilnya. Lalu dia berlari-lari bersamanya untuk waktu yang lama, tidak tahu di mana harus menempel: tampaknya peta itu hal yang menarik, tetapi tampaknya itu tidak boleh mengambil tempat. Selain itu, bahkan tidak ingin bergantung pada Google dalam hal sepele seperti itu. Jadi pada akhirnya, saya menghapus peta itu, tetapi setelah beberapa waktu saya mulai menghasilkan kartu rute dan menyadari bahwa saya secara teknologi telah "tumbuh" ke peta besar sendiri. Ternyata tidak begitu sulit untuk melakukan ini, meskipun sejauh ini peta umum tetap menjadi bagian paling intensif dari seluruh proyek. Dan agar tidak memakan ruang di antarmuka, saya meletakkan kartu pada halaman terpisah (mereka akan menariknya lebih jarang).

Lokalisasi
Untuk output normal dalam produksi, pelokalan diperlukan. Itu selalu di satu sisi pekerjaan yang sangat langsung dan sederhana, di sisi lain - ketika Anda mulai melakukannya, kerumunan kecoak merangkak keluar dari mana-mana. Dalam kasus saya, konten utama dari OSM sudah dilokalkan, sehingga hanya tipe tempat dan elemen antarmuka yang tersisa. Dengan pengecualian beberapa colokan (untuk waktu yang lama saya tidak bisa merumuskan die "Sepanjang jalan") semuanya mudah. Perlu dicatat bahwa nama tempat dapat menempati 2 dan 3 baris, dan mungkin tidak
masuk ke layar dengan lebar kecil - karena itu widget
auto_size_text membantu di sini, saya merekomendasikannya dalam batas yang wajar.

Tapi di sisi teknis, itu tidak begitu mulus. β
Intl_translation , β¦ . , . , (!) - , β¦ , , .
, , β - . , , .
, , . , -, β . , . , . .
- support androidx: - , , . , β . : . , , .
, , :


. - β . , β2:
Android Auto Apple CarPlay. , .
, .