Ini adalah kisah tentang bagaimana saya mencoba memecahkan satu masalah aneh yang menghalangi saya sendiri. Ke depan, saya akan mengatakan bahwa saya puas dengan solusi yang dihasilkan dan membawa aplikasi ke akhir yang logis. Namun, untuk menjalankannya sepenuhnya, Anda membutuhkan lebih banyak sumber daya, jadi saya memutuskan untuk beristirahat dan bertanya kepada orang-orang apakah ada orang lain yang membutuhkannya. Untuk tujuan ini (dan juga untuk berbicara) saya menulis di sini.
Dua kata tentang diri saya: Saya tinggal di Dublin, Irlandia, saya bekerja sebagai programmer. Itu tidak tepat di tempat, itu sebabnya di waktu luang saya di rumah saya melihat berbagai proyek, terutama di atas meja. Saya menulis di Habré untuk pertama kalinya, meskipun saya membaca selama bertahun-tahun.
Masalah
Sudah lama sekali, ketika saya harus sering bepergian ke tempat-tempat asing untuk bekerja, saya mulai memperhatikan bahwa pencarian standar pada kartu apa pun sama sekali tidak berlaku untuk pengemudi saat ini. Lihat: Anda mengemudi di daerah yang tidak dikenal, dan Anda memiliki panah bensin di nol. Tindakan Anda? Jika pada saat itu saya tidak sendirian di dalam mobil, maka saya memberi tahu penumpang: "Baiklah, cari pompa bensin terdekat saat saya mengemudi." Karena jika Anda melakukannya sendiri, Anda perlu melakukan tindakan berikut:
- Untuk berhenti
- Dalam aplikasi peta, masukkan "bensin" dalam pencarian (atau klik salah satu tombol cepat yang sekarang menawarkan beberapa kartu)
- Aplikasi melakukan pencarian dan menampilkan peta besar dengan selusin pompa bensin
- Anda mencoba mencari tahu mana yang paling dekat dengan Anda dan mengkliknya untuk membangun rute
Menurut saya, mimpi buruk. Pertama, Anda harus berhenti atau setidaknya menunggu lampu lalu lintas. Karena kartunya kompleks dan ikonnya kecil. Kedua, kartu nifig tidak memberi tahu Anda apa pompa bensin terdekat. Dalam hal ini, Google adalah yang terburuk: bahkan dalam hasil dalam bentuk daftar, Google terus-menerus mendorong bukan tempat terdekat, dan yang paling dinilai / dengan foto / berbayar / saya tidak tahu.

Nah, faktor ketiga: kita bergerak. Hasil kartu yang dikeluarkan relevan untuk beberapa hal, tetapi kami sudah jauh dari itu. Apakah Anda memperhatikan bahwa bahkan rute yang diletakkan oleh Google tidak diperbarui secara otomatis jika kami telah bergeser? Hanya jika navigasi telah diluncurkan, maka akan dibangun kembali.
Secara umum, masalahnya jelas. Logika yang berfungsi untuk pejalan kaki dan kebutuhan mereka, bagi pengemudi tidak ada artinya. Saya tidak peduli dengan peringkat tempat dan jenis dapur di sana - saya perlu, tanpa terganggu dari jalan, secara real time mendapatkan rute ke pompa bensin terdekat, biaya, parkir, ATM, dll.
Ide
Sekarang mari kita coba menentukan skenario pencarian yang ideal. Kriteria tersebut adalah sebagai berikut:
- interaksinya singkat dan jelas agar tidak mengganggu pengemudi
- output berbasis jarak transparan
- pembaruan waktu nyata
Hal pertama yang memintanya adalah mengganti pencarian standar satu kali dengan pemindaian. Artinya, rantai tindakannya kira-kira seperti ini:
- Meluncurkan pemindaian
- Berkuda, melihat hasil terkini secara real-time
- Ketika saya menyukai sesuatu, saya mengklik dan membuka rute
Anda menentukan kriteria pencarian sebelumnya - pada kenyataannya, ini adalah jenis tempat dan jari-jari pemindaian. Lebih lanjut, saat mobil bergerak, aplikasi bekerja seperti radar. Cukup cepat menjadi jelas bahwa, pertama, jari-jari tidak bulat, tetapi dalam bentuk isoline. Kedua, itu harus dibangun bukan oleh jarak, tetapi oleh waktu, karena menit lebih mudah dilihat daripada kilometer.
Kemudian saya memikirkan cara untuk menampilkan hasilnya. Lebih tepatnya, apakah saya memerlukan kartu sama sekali? Pengemudi melihat aplikasi dengan satu mata dan berinteraksi dengan satu jari - dia tidak membutuhkan peta, tetapi teks besar dan tombol besar. Karena itu, saya segera memutuskan bahwa layar utama akan menjadi daftar, dan saya mungkin menambahkan peta pada awalnya dan kemudian melihat apakah saya harus meninggalkannya.
Rencanakan
Mengetahui keanehan saya sendiri dari proyek peregangan, saya memutuskan untuk memberikan diri saya 2 bulan untuk semuanya - pada akhirnya saya bertemu di 3. Pada prinsipnya, aplikasi ini cukup sederhana:
- Klien dari sepasang layar (pencarian, daftar, dan peta)
- Secara berkala mengirim ke server koordinat, radius pencarian, dan jenis tempat
- Server membangun isoline dalam waktu (omong-omong, namanya dalam bahasa Inggris - isochrone), melakukan pencarian di tempat dan mengembalikan daftar
Kedengarannya seperti paru-paru yang lebih ringan. Saya sudah memiliki pengalaman sebelumnya dalam kartografi (beberapa tahun yang lalu saya membuat portal real estat di mana pencarian ada di peta), jadi tumpukan di backend langsung jelas:
- impor data dari OpenStreetMaps ke Elasticsearch
- OpenTripPlanner untuk membangun kontur
Pada klien, saya pikir, memutuskan untuk menggunakan kerangka kerja baru dari Google - Flutter. Ini adalah lintas platform, cukup fleksibel, dan memungkinkan Anda untuk membuat aplikasi lengkap dengan kode minimal. Tentu saja, itu mentah dan tidak jelas apa yang ada di produksi, tetapi terlihat sempurna untuk pembuatan prototipe. Harus diklarifikasi bahwa pada titik ini saya memiliki pengalaman dalam pengembangan asli untuk android (saya adalah seorang pemimpin tim) dan memutuskan, untuk berbicara, menghadapi musuh. Musuh tidak begitu menakutkan.
Implementasi
Aplikasi prototipe pertama siap sangat cepat - Flutter memiliki ambang masuk yang rendah dan filosofi seperti redux yang dapat dimengerti. Anehnya, deskripsi deklaratif antarmuka juga menyenangkan, dan juga hot reboot (Bereaksi Asli, bitmap Anda). Secara umum, kesan adalah bahwa Google menyumbang sebagian besar penyakit bawaan dari upaya sebelumnya. Namun, saya memahami orang-orang yang mungkin tidak ingin membahasnya - seseorang tidak suka panah, sejumlah widget, dan "debug visual" yang ditawarkan di sini adalah sesuatu yang sangat mentah.
Di backend, saya melakukan hal berikut:
- Memberikan Nominatim, mengunggah ekstrak data OpenStreetMaps (dibawa ke sini ) ke dalam basis datanya menggunakan utilitas osm2pgsql aslinya. Mengapa saya beralih ke Photon asam geokoder terbuka kecil tapi sangat menyenangkan. Sebelumnya, saya sudah menggunakannya dalam beberapa proyek - ini menghasilkan indeks Elasticsearch, mengimpor data dari database Nominatim di sana, dan mencari indeks ini. Saya menyukainya dengan kecepatan dan pemetaan murni (misalnya, saya mencoba Pelias dan saya kurang menyukainya). Masalah utamanya adalah versi lama dari elastis, tetapi dalam kasus saya, saya tidak memerlukan fungsi geocoder itu sendiri, hanya data, jadi setelah mengimpor, saya mentransfer indeks ke pemasangan elastis versi terbaru dengan jiwa murni. Ngomong-ngomong, mengapa saya memilih Elasticsearch? Ini sangat cepat, dan memiliki fungsi menemukan koordinat dengan poligon.
- Landfill - alias isochrone - awalnya menghasilkan OpenTripPlanner untuk saya. Ini adalah perencana rute open source yang cukup bagus. Ia bekerja sebagai berikut: ia mengambil ekstrak OpenStreetMaps yang sama dan mengkompilasinya ke dalam grafik jalan besar, yang, sebagai objek terpisah, disimpan ke disk. Ketika server mulai, grafik ini dimuat ke dalam RAM dan semua rute dicari melalui itu. Pro: mengambil cepat, fungsionalitas yang kaya (misalnya, menghasilkan kontur dari kotak) dan kecepatan yang baik. Cons: kecepatan ini tergantung pada jumlah RAM, dan dokumentasinya sangat menjijikkan. Dokumentasi hanya mengerikan. Kilas balik Vietnam.
- Saya melemparkan api kecil ke python, yang mengambil jenis tempat dan radius pencarian dalam hitungan detik, meminta poligon dari OpenTripPlanner, lalu mencarinya di Elasticsearch. Ia meminta rute ke setiap lokasi yang ditemukan (lagi dari OpenTripPlanner), membutuhkan waktu dan waktu yang lama. Setelah itu, semua data yang dikumpulkan dikemas dan dikembalikan dengan indah.
Saya melakukan pembaruan hasil dengan menggeser koordinat perangkat sebesar 5 meter. Peta itu statis - Saya hanya menggunakan api dari peta Google statis (seperti yang Anda lihat, ini adalah satu-satunya tempat di mana korporasi merangkak ke dunia terbuka kita yang nyaman). Implementasi pertama terlihat seperti ini:




Setelah bermain dengan aplikasi tersebut, saya memutuskan untuk menyembunyikan peta. Dia melakukan pekerjaan yang baik untuk memahami apa poligon pencarian dibuat, dan dia tampak lucu - itu menarik untuk melihat bagaimana gurita ini berubah bentuk secara real time. Namun, hiburan ini tidak membantu aplikasi untuk memenuhi fungsinya dan menempati sepertiga layar.
Terpikir oleh saya untuk menambahkan panah, yang menunjukkan arah untuk setiap hasil. Ini berfungsi seperti ini:
- Ingat koordinat Anda sebelumnya
- Saat bergeser, kami meletakkan rute dari posisi sebelumnya ke saat ini
- Kami mengambil segmen terakhir dari rute kami dan membandingkannya dengan segmen pertama dari rute setiap hasil. Karena mereka diletakkan di sepanjang grid jalan yang sama, dengan probabilitas 99% sudut di antara mereka dekat dengan 0 atau 180.
Trik yang sangat sederhana ini sangat memudahkan pemahaman apakah kita sudah menuju tempat itu atau perlu membalikkan keadaan.


Pada titik ini, saya cukup senang dengan aplikasi yang dihasilkan dan memutuskan untuk mencoba menyebarkannya ke beberapa negara. Meski demikian, Irlandia adalah negara bagian yang sangat kecil, dan indeks elastis dan grafik jalannya kecil. Untuk pengujian, saya memutuskan untuk menghubungkan tetangga Inggris. Ini sekitar 4 kali lebih besar dan memiliki jaringan jalan yang jauh lebih padat (terutama ibu kota dan kota-kota besar). Dan kemudian muncul masalah.
Elasticsearch diharapkan mencerna kenaikan indeks dengan cukup baik, tetapi dengan OpenTripPlanner ada kegagalan total. Itu ditulis dalam Java dan, seperti yang saya katakan di atas, menghasilkan grafik jalan, sehingga setelah memuatnya ke dalam RAM. Grafik untuk Irlandia adalah 1 gigabyte, untuk Inggris sudah 5. Itu mungkin, tentu saja, untuk membaginya menjadi negara, wilayah dan bahkan wilayah, dan kemudian mengarahkan ke grafik yang diinginkan tergantung pada koordinat pengguna. Namun, ini membuat tidak mungkin untuk meletakkan rute antar wilayah, dan yang paling penting, itu tidak menyelesaikan kebutuhan untuk menyimpan semua grafik ini dalam memori. Akhirnya, hanya dengan mengkompilasi setiap objek tersebut SANGAT banyak sumber daya dan bertahan selamanya. Untuk bersenang-senang, saya meluncurkan pada mesin saya (bingkai 16 GB) perakitan Count of France, menunggu sehari dan dibatalkan.
Jelas, teknologi yang telah membuktikan dirinya dengan baik dalam tugas-tugas kecil tidak dirancang untuk penskalaan sama sekali (setidaknya tidak dengan sumber daya saya). Jadi harus mengakui kekalahan, atau merangkak ke teknologi lain. Saya beristirahat selama beberapa hari dan mulai mempelajari apa solusi open source lain yang ada di dunia. Ternyata pada dasarnya ada dua di antaranya:
Jika yang pertama ditulis dalam Java dan memuat grafik jalan ke dalam RAM, maka OSRM - Open Source Routing Machine - sudah ditulis dalam plus dan menyimpan file perantara (tidak kurang mengerikan) pada disk. Dengan demikian, kebutuhan untuk memiliki sejumlah besar RAM digantikan oleh persyaratan disk yang besar dan cepat. Ini lebih nyata.
Garis finish
Setelah beberapa malam memilih dalam dokumentasi, semua kode server dipindahkan ke solusi baru. Itu benar-benar bekerja, dan itu bekerja dengan cukup baik. Itu mungkin untuk menghubungkan beberapa negara, dan bahkan kecepatan pencarian meningkat. Prinsip-prinsip umumnya sama: dari file perantara ekstrak OpenStreetMaps dikompilasi untuk profil "mesin" (profil adalah seperangkat bobot dan instruksi untuk tepi grafik - ada profil "berjalan kaki", "sepeda", dll.). Kemudian file-file ini dimasukkan ke dalam direktori, dan OSRM api sudah membacanya dari disk. Omong-omong, Api ternyata agak besar - kontur dan perencanaan rute dengan berbagai nuansa didukung, bahkan ada generasi ubin untuk peta. Saya memutuskan untuk memikirkan yang terakhir lebih terinci.
Kembali ke aplikasi dan terus mengujinya, saya menyadari beberapa hal lagi:
- menu di atas tidak bagus, jangkauan jauh
- peta umum jelas tidak diperlukan, itu hanya mengikat saya ke google
- kartu hasil membosankan dan monoton
Dia dengan senang hati mengeluarkan peta Google (hore, sekarang 100% open source dan datanya), menyederhanakan menu, pindah ke bawah. Mulai memikirkan apa yang harus dilakukan dengan kartu. Dan kemudian ubin api muncul dengan sangat tepat, yang saya sebutkan di atas. Ini memungkinkan Anda untuk menghasilkan ubin vektor untuk koordinat yang diberikan dan tingkat zoom. Hasilnya dikeluarkan dalam bentuk gumpalan biner dari tipe aplikasi / x-protobuf - tipe data yang agak tidak nyaman untuk manipulasi. Saya tidak akan memerinci (saya harus sedikit berkeringat), tetapi singkatnya tindakan saya terlihat seperti ini:
- Ambil garis rute yang dibangun ke titik dalam bentuk polyline
- Polyline -> GeoJSON
- Dapatkan kotak pembatas bentuk ini
- Minta semua ubin yang ditangkap oleh kotak pembatas ini
- Konversi data ubin dari format biner ke GeoJSON
- Ubin lem, rapikan dengan kotak pembatas, kombinasikan dengan garis rute, warnai
- GeoJSON yang dihasilkan dikonversi menjadi bitmap
Selama aksi, ada nuansa yang berbeda, misalnya, indentasi kotak pembatas atau tanda titik dengan cincin berwarna (dan jadikan jari-jarinya konstan untuk semua level zoom). Gambar yang dihasilkan tampak seperti ini:


Sentuhan akhir
Ketika saya melampirkan rute visual ke setiap hasil, daftar mulai berkilau dengan warna baru. Selain itu, menyadari bahwa setiap gambar, secara default, naik mengangkang ke utara, saya membuatnya berputar sehubungan dengan kompas. Jadi, selain efek visual, chip ini juga menjadi fungsional - menggantikan panah arah. Sekarang setelah Anda mengemudi, Anda dapat melihat dengan pasti sisi mana dari Anda ini atau hasil itu.
Bulan ketiga pembangunan telah kedaluwarsa, dan itu sudah perlu dibulatkan. Semakin banyak yang Anda tambahkan, semakin banyak yang Anda inginkan, jadi pada titik tertentu Anda hanya perlu menyatukan diri dan melepaskan proyek. Saya mengubah dan melukis antarmuka sedikit lagi, dan untuk penyelesaian, saya membuat sketsa logo aplikasi:


dan halaman intro:


Dan akhirnya, versi terakhir dari aplikasi:





Ringkasan
Terima kasih sudah menonton. Saya harap aliran kesadaran ini akan menarik bagi seseorang, dan mungkin bahkan bermanfaat. Pada tahap ini, saya pikir aplikasi sudah siap: cepat, tanpa bug khusus dan dapat bekerja di negara mana pun di dunia. Ngomong-ngomong, Anda mungkin telah memperhatikan bahwa tangkapan layar berasal dari iPhone dan Android, karena berkat Flutter, aplikasi ini bekerja persis sama di kedua platform.
Namun demikian, sejauh ini saya telah memutuskan untuk membekukan segalanya - mengubah pekerjaan saya, kekhawatiran baru telah muncul. Setelah beberapa bulan, saya membersihkan debu dan memutuskan untuk menulis retrospektif. Ulasan Anda menarik: apakah Anda menyukainya, menggunakannya, apa yang bisa diubah.
PS Tentu saja, tentang kesiapan aplikasi adalah omong kosong. Ini siap sebagai prototipe - jika Anda mendekati produksi serius, Anda perlu membuat skrip untuk menyinkronkan data dengan OpenStreetMaps, memeriksa operasi di kebun binatang perangkat, melokalisasi antarmuka, dll. Backend yang sama pada node dan python akan jatuh di bawah beban yang serius.