Pencarian MapKit: Kiat & Trik


MapKit adalah pustaka perangkat lunak yang memungkinkan Anda untuk menggunakan data peta dan teknologi Yandex dalam aplikasi seluler. Dia memiliki dokumentasi resmi yang sudah berisi deskripsi terperinci tentang metode API, jadi hari ini kita akan membicarakan hal lain.


Dalam posting ini, saya akan memberi tahu pembaca Habr tentang fitur pencarian di MapKit dan berbagi rekomendasi dan trik yang mungkin berguna bagi Anda.


TL; DR Jika Anda tidak ingin membaca seluruh artikel, berikut adalah dua poin paling berguna sebagai kompensasi untuk membaca kata pengantar:


  • Jangan lupa untuk menyimpan sesi, jika tidak pencarian tidak akan berfungsi.
  • Semua informasi yang paling menarik disimpan dalam metadata objek. Jika Anda ingin mengetahui alamat lengkap, jam buka, atau berapa harga cappuccino di kafe tertentu, maka Anda perlu metadata.

Tautan ke dokumentasi dalam teks akan untuk Android, kelas dan metode untuk iOS disebut dengan cara yang sama.


Apa yang bisa dicari


Pertama-tama, mari kita bicara tentang apa yang dapat dilakukan pencarian di MapKit. Pencarian dapat melakukan apa yang Anda harapkan dari aplikasi peta ketika Anda ingin menemukan sesuatu di sana.



Saat Anda mengetik di bilah pencarian "cafe", "Lev Tolstoy street, 16" atau "trem 3", maka pencarian teks berfungsi. Ini adalah jenis pencarian yang paling canggih. Ditimbun dalam arti mendukung set parameter maksimum untuk penyesuaian. Anda dapat mencoba segera mencari di sepanjang rute atau jalan yang Anda minati, mengklarifikasi jumlah hasil yang diinginkan, mengatur posisi pengguna, dan sebagainya. Jika setelah pencarian pertama Anda ingin memindahkan peta atau menerapkan filter pada permintaan ("apotek dengan kumpulan"), ini adalah permintaan ulang .


Pencarian sebaliknya akrab bagi sebagian besar pengguna pada pertanyaan "Apa yang ada di sini?". Ini memungkinkan Anda mengklik peta untuk menentukan jalan atau rumah mana yang "di bawah kursor" atau organisasi mana yang dekat dengan titik ini. Pencarian oleh URI diperlukan ketika Anda ingin menemukan objek tertentu. Ini dapat digunakan, misalnya, untuk membuat bookmark dalam suatu aplikasi. Kami menemukan kedai kopi favorit kami, menandainya dengan tanda bintang - lain kali mungkin untuk menemukan organisasi ini oleh URI, di mana pun jendela peta berada. Baik pencarian balik maupun pencarian URI mendukung permintaan ulang, karena tidak ada yang ditentukan untuk mereka.


Peluang lain yang ada dalam pencarian adalah petunjuk pencarian , yang memungkinkan Anda untuk secara otomatis melengkapi kueri saat Anda mengetiknya. Tapi kami akan menunda cerita rinci tentang mereka untuk lain waktu.


Bagaimana permintaannya diatur


Pencarian, seperti banyak bagian MapKit, berfungsi secara sinkron. Objek utama untuk bekerja dengan sinkronisasi ini adalah sesi pencarian . Mari kita lihat contoh kecil.


Sedikit tentang contoh

Contoh dalam artikel ini ada di Kotlin untuk membuatnya lebih mudah untuk bekerja dengan nilai opsional dan kode boilerplate lebih sedikit. MapKit memiliki aplikasi demo . Ini dapat digunakan untuk menguji contoh, tetapi untuk ini, SearchActivity harus dikonversi dari Jawa ke Kotlin. showMessage , yang dari waktu ke waktu muncul dalam kode, adalah cara mudah bagi Anda untuk menampilkan satu baris teks pada layar atau dalam log.


 // `searchManager`  `searchSession` –  .    //    ,     . searchManager = SearchFactory.getInstance().createSearchManager( SearchManagerType.ONLINE ) val point = Geometry.fromPoint(Point(59.95, 30.32)) searchSession = searchManager!!.submit("", point, SearchOptions(), object: Session.SearchListener { override fun onSearchError(p0: Error) { showMessage("Error") } override fun onSearchResponse(p0: Response) { showMessage("Success") } } ) 

Segera setelah panggilan submit kontrol akan kembali ke kode Anda, dan ketika MapKit menerima respons dari server, SearchListener akan dipanggil.


Sesi pencarian memungkinkan Anda untuk:


  • Batalkan permintaan . Misalnya, jika pengguna menutup layar pencarian.
  • Ulangi permintaan jika terjadi kesalahan. Misalnya, jika ada masalah dengan jaringan.
  • Lanjutkan interaksi dengan pencarian setelah respons diterima. Permintaan ulang dilakukan melalui sesi.

Sesi setelah pembatalan secara otomatis dibatalkan. Ini berarti bahwa jika tidak disimpan di sisi kode klien, pencarian tidak akan berfungsi.
Jangan lupa untuk menyimpan sesi, sesi adalah teman Anda!


Opsi Pencarian


Cara umum untuk mengonfigurasi kueri penelusuran adalah melalui kelas SearchOptions , yang memungkinkan Anda mengubah parameter kueri.


  • Parameter utama dari ini adalah SearchType . Ini memungkinkan Anda untuk menentukan apakah Anda ingin melihat toponim, organisasi, atau transportasi dalam jawabannya (kemungkinan besar Anda tidak akan memerlukan jenis lain).
  • Parameter kueri penting lainnya adalah cuplikan . Kami akan membicarakannya secara lebih rinci di bagian perangkat tanggapan.
  • Jika Anda ingin mendapatkan geometri toponim (misalnya, jalan atau area), maka Anda perlu memesannya melalui setGeometry(true) . Perlu diingat bahwa geometri cukup "berat" dalam hal data yang dikirim.
  • Secara default, pencarian tidak mengembalikan organisasi yang ditutup (sementara atau permanen), tetapi jika Anda membutuhkannya, Anda perlu mengatur setSearchClosed(true) .

Selain parameter yang tercantum, ada beberapa lagi yang mungkin berguna bagi Anda, mereka dapat ditemukan dalam dokumentasi untuk kelas. Perhatikan bahwa tidak semua kueri mendukung semua kombinasi parameter. Dokumentasi untuk setiap metode SearchManager atau Session menunjukkan parameter mana dari SearchOptions yang dimengerti.


Bagaimana jawabannya diatur


Dilihat oleh pertanyaan-pertanyaan yang mendukung, sebagian besar semua pengguna bingung dengan format respons pencarian. Jika Anda melihat kelas respons, tampilannya cukup sederhana (setidaknya, bagian yang menarik bagi kami):


 public class Response { public synchronized SearchMetadata getMetadata(); public synchronized GeoObjectCollection getCollection(); // ... } 

Di sini getCollection() mengembalikan objek dalam respons, dan getMetadata() adalah beberapa data tambahan yang berisi, misalnya, informasi tentang jendela respons , jenis peringkat dan jumlah hasil yang ditemukan . Jika Anda melihat ke dalam GeoObjectCollection Anda dapat melihat bahwa itu berisi beberapa Item yang bisa berupa GeoObjectCollection atau GeoObject .


Tidak ada koleksi di dalam koleksi dalam pencarian (setidaknya belum), jadi mari kita lihat GeoObject . Di dalam objek ada nama ( getName() ), deskripsi ( getDescriptionText() ), bingkai ( getBoundingBox() ), satu set geometri ( getGeometry() ), dan beberapa metode lain yang tidak terlalu jelas. Di mana nomor telepon organisasi? Bagaimana memahami kota mana yang dimaksud dengan toponiminya?


Menurut metode objek, ini tidak begitu jelas.


Geoobject


Sudah waktunya untuk berbicara lebih banyak tentang GeoObject .


GeoObject adalah objek "kartu" dasar. Di dalamnya dapat berupa acara jalan, objek yang terpisah dari hasil pencarian, manuver di rute atau objek di peta (POI), seperti monumen atau organisasi terkenal.



Semua yang paling menarik tentang objek disimpan dalam metadata. Mereka dapat diakses menggunakan metode getMetadataContainer() . Kunci dalam wadah ini adalah tipe metadata. Jika Anda melihat sesuatu dalam dokumentasi yang diakhiri dengan kata Metadata , maka kemungkinan besar Anda ada di sini. Dalam mencari berbagai potongan "metadata" 15.



Metadata dapat dibagi menjadi beberapa jenis. Tipe pertama adalah metadata yang menentukan jenis objek milik: toponim ( ToponymObjectMetadata ), organisasi ( BusinessObjectMetadata ) atau transportasi ( TransitObjectMetadata ). Dalam metadata untuk toponim, Anda dapat menemukan alamat terstruktur dan geometri terperinci. Metadata untuk organisasi adalah jam operasi atau situs perusahaan. Metadata ini ditentukan oleh jenis pencarian dalam permintaan - jika Anda hanya mencari toponim, maka setiap objek dalam respons harus memiliki metadata yang sesuai. Jika Anda mencari nama tempat atau organisasi, maka setiap objek akan memiliki setidaknya satu dari dua "metadata".


Berikut cara menemukan nomor telepon perusahaan:


 val phones = response.collection.children.firstOrNull()?.obj ?.metadataContainer ?.getItem(BusinessObjectMetadata::class.java) ?.phones 

Dan inilah cara menemukan kota di alamat terstruktur:


 val city = response.collection.children.firstOrNull()?.obj ?.metadataContainer ?.getItem(ToponymObjectMetadata::class.java) ?.address ?.components ?.firstOrNull { it.kinds.contains(Address.Component.Kind.LOCALITY) } ?.name 

Tipe kedua adalah metadata yang datang dengan objek, meskipun Anda tidak bertanya tentangnya. Jenis utama yang perlu Anda ketahui adalah URIObjectMetadata . Di dalam URIObjectMetadata pengidentifikasi unik objek disimpan, yang harus dilewatkan dalam pencarian oleh URI .


 //       «»,     //     val uri = response.collection.children.firstOrNull()?.obj ?.metadataContainer ?.getItem(UriObjectMetadata::class.java) ?.uris ?.firstOrNull() ?.value 

Dan tipe ketiga adalah metadata, yang akan muncul dalam jawaban hanya jika Anda secara khusus meminta pencarian tentang itu. Dengan cara yang berbeda, metadata ini disebut snippet . Cuplikan adalah potongan kecil data tambahan yang berubah lebih sering daripada data "referensi" dasar, atau yang tidak dibutuhkan semua orang. Ini bisa berupa peringkat, tautan ke foto atau panorama, nilai tukar atau harga bahan bakar di pompa bensin. Daftar cuplikan harus ditetapkan menggunakan opsi pencarian. Jika server memiliki potongan yang dipesan, maka itu akan menambahkannya ke objek yang sesuai.


 val point = Geometry.fromPoint(Point(59.95, 30.32)) val options = SearchOptions() options.snippets = Snippet.FUEL.value searchSession = searchManager!!.submit("", point, options, this) ... override fun onSearchResponse(response: Response) { //         showMessage(response.collection.children.firstOrNull()?.obj ?.metadataContainer ?.getItem(FuelMetadata::class.java) ?.fuels ?.joinToString("\n") { "Fuel(name=${it.name}, price=${it.price?.text})" } ?: "No fuel" ) } 

Semua metadata yang tercantum di atas ditambahkan ke objek individual dalam respons. Ada juga metadata yang ditambahkan ke seluruh respons. Tetapi mereka dibawa dalam metode SearchMetadata dan mereka tidak perlu diekstraksi dari koleksi khusus apa pun.


 //            response.metadata.businessResultMetadata?.categories //     (  )     response.metadata.toponymResultMetadata?.responseInfo?.mode 

Contoh Penggunaan


Sekarang mari kita melihat metode utama dari kelas pencarian, lihat contoh penggunaan dan pada beberapa poin yang tidak jelas terkait dengan mereka.


Pencarian Teks


Metode utama untuk pencarian teks (dan untuk seluruh pencarian, mungkin) adalah submit :


 Session submit( String text, Geometry geometry, SearchOptions searchOptions, SearchListener searchListener ); 

  • Parameter text diharapkan berisi teks yang ingin Anda cari.
  • Parameter geometry agak sulit. Bergantung pada jenis geometri yang ditransfer, pencarian akan berperilaku berbeda:
    • Jika Anda melewati suatu titik, maka pencarian akan dilakukan di jendela kecil di sebelah titik ini.
    • Jika Anda melewati jendela persegi panjang ( BoundingBox ) atau poligon empat titik, maka itu akan digunakan sebagai kotak pencarian. Contoh sederhana dari jendela semacam itu adalah area peta yang terlihat.
    • Akhirnya, jika Anda melewati polyline , maka jendela yang menggambarkannya akan digunakan sebagai jendela pencarian, dan peringkat akan dilakukan dengan mempertimbangkan polyline ini.
  • Kami sudah berbicara tentang SearchOptions dan SearchOptions atas.

Server dapat mempertimbangkan bahwa jawaban yang benar tidak ada di jendela di mana pencarian awal dilakukan (“warnet di Vladivostok” ketika jendela pencarian berada di Moskow). Dalam hal ini, Anda perlu mengambil jendela respons dan memindahkan kartu ke sana sehingga hasilnya terlihat di layar (permintaan ulang tidak mengizinkannya dan mereka tidak meminta untuk memindahkan kartu).


Metode submit memiliki submit kembar:


 Session submit( String text, Polyline polyline, Geometry geometry, SearchOptions searchOptions, SearchListener searchListener ); 

dengan satu parameter tambahan. Parameter ini dapat digunakan untuk mentransfer polyline besar (misalnya, rute ke kota lain) dan jendela pencarian kecil. Kemudian pencarian itu sendiri akan memotong bagian yang diperlukan dari polyline yang ditransfer dan hanya akan menggunakannya untuk permintaan.


Permintaan ulang


Permintaan ulang, tidak seperti jenis permintaan lainnya, dilakukan dengan bantuan sesi pencarian, yang mengembalikan kiriman yang sama dan saudara kembarnya. Bagian dari metode sesi ini sederhana dan mudah:


  • Anda dapat mengubah jendela permintaan (ketika pengguna, misalnya, memindahkan kartu)
  • opsi pencarian dapat diperbarui (misalnya, untuk memperbarui posisi pengguna)
  • Anda dapat mengubah jenis peringkat - berdasarkan jarak atau peringkat .

Untuk melakukan pencarian yang disempurnakan, Anda perlu menggunakan metode resubmit . Ia menerima SearchListener sama dengan pencarian biasa. Sebelum memanggilnya, Anda dapat mengubah beberapa parameter sesi. Misalnya, secara bersamaan ubah jenis peringkat dan terapkan filter.


Filter


Karena kita berbicara tentang filter. Filter adalah saat Wi-Fi dan masakan Italia. Mereka mungkin memiliki sintaks yang paling membingungkan dari semua antarmuka pencarian di MapKit. Ini disebabkan oleh fakta bahwa struktur data yang sama digunakan untuk memperoleh filter dari respons pencarian dan untuk menentukan filter dalam permintaan ulang.



Filter datang dalam dua jenis. Filter Boolean hanya mengasumsikan dua nilai yang saling eksklusif - ya atau tidak. Ini mungkin kehadiran Wi-Fi di kafe, toilet di pompa bensin atau parkir di dekat organisasi. Filter Enum mengasumsikan banyak nilai yang dapat diminta bersama. Ini, misalnya, adalah jenis dapur untuk kafe atau jenis bahan bakar di pompa bensin.


Pertama, mari kita lihat cara mendapatkan filter yang tersedia untuk reboot saat ini:


 private fun filters(response: Response): String? { fun enumValues(filter: BusinessFilter) = filter .values .enums ?.joinToString(prefix = " -> ") { e -> e.value.id } ?: "" return response .metadata .businessResultMetadata ?.businessFilters ?.joinToString(separator = "\n") { f -> "${f.id}${enumValues(f)}" } } 

Pada baris yang dihasilkan, untuk filter boolean, hanya pengidentifikasi yang akan ditampilkan, dan untuk filter enum, pengidentifikasi filter itu sendiri dan pengidentifikasi nilai yang tersedia. Sekarang, dengan berbekal pengetahuan tentang pengidentifikasi yang tersedia, kami akan mencari kafe-kafe masakan Italia yang memiliki Wi-Fi. Pertama-tama tambahkan filter boolean:


 val boolFilter = BusinessFilter( /* id= */ "wi_fi", /* name= */ "", /* disabled= */ false, /* values= */ BusinessFilter.Values.fromBooleans( listOf(BusinessFilter.BooleanValue(true, true)) ) ) 

Sekarang enum filter:


 val enumFilter = BusinessFilter( /* id= */ "type_cuisine", /* name= */ "", /* disabled= */ false, /* values= */ BusinessFilter.Values.fromEnums( listOf(BusinessFilter.EnumValue( Feature.FeatureEnumValue( /* id= */ "italian_cuisine", /* name= */ "", /* imageUrlTemplate= */ "" ), true, true )) ) ) 

Terakhir, Anda dapat menambahkan filter ke sesi dan resubmit() panggilan resubmit() :


 searchSession!!.setFilters(listOf(boolFilter, enumFilter)) searchSession!!.resubmit(this) 

Harap perhatikan bahwa Anda tidak dapat menetapkan filter untuk kueri pertama. Pertama, Anda perlu mendapatkan respons pencarian yang mencantumkan filter yang tersedia. Dan hanya kemudian membentuk reboot.


Hasil tambahan

Sesi lain memungkinkan Anda memeriksa apakah ada hasil pencarian tambahan untuk permintaan Anda. Dan, jika benar, dapatkan mereka. Misalnya, ketika Anda mencari kafe di kota Anda, kemungkinan besar semuanya tidak akan muat di satu halaman jawaban pencarian. Beberapa fetchNextPage dan fetchNextPage diperlukan untuk melihat halaman-halaman berikut dalam daftar. Di sini Anda perlu tahu bahwa pertama-tama, memanggil fetchNextPage akan mengeluarkan pengecualian jika metode hasNextPage mengembalikan false . Dan kedua, penggunaan metode ini menyiratkan bahwa parameter yang tersisa tidak berubah. Yaitu, sesi digunakan baik untuk memperbaiki permintaan ( resubmit() ), atau untuk mengambil halaman berikut ( fetchNextPage() ). Menggabungkan mode-mode ini tidak perlu.


Pencarian terbalik


Pencarian sebaliknya untuk kenyamanan juga disebut submit :


 Session submit( Point point, Integer zoom, SearchOptions searchOptions, SearchListener searchListener ) 

Ini berbeda dari jenis kueri lain karena hanya membutuhkan satu jenis pencarian untuk masuk. Anda melewati tipe GEO dan mencari nama tempat, atau tipe BIZ dan mencari organisasi. Tidak ada yang ketiga.


Saat mencari kembali dengan tipe GEO , ada poin yang perlu diklarifikasi. Harap dicatat bahwa jawabannya akan berisi beberapa objek dalam hierarki (yaitu, jawabannya akan mencakup rumah, jalan, kota, dan sebagainya). Dalam kasus sederhana, Anda dapat mengambil objek pertama saja. Di yang lebih kompleks, cari melalui hierarki yang Anda inginkan.


Level zoom diperlukan untuk menghasilkan hasil yang memadai tergantung pada apa yang dilihat pengguna di peta. Bayangkan pengguna melihat peta secara nasional. Maka akan aneh baginya untuk mengklik pada jalan atau rumah yang terpisah jika pengguna tidak sengaja berhasil masuk ke mereka. Cukup banyak kota. Inilah yang menjadi parameter zoom .


 val point = Point(55.734, 37.588) //         «  , 16» searchSession = searchManager!!.submit(point, 16, SearchOptions(), this) //    –  " " searchSession = searchManager!!.submit(point, 14, SearchOptions(), this) 

Cari oleh URI


Semuanya cukup jelas di sini - kita mengambil URI dari URIObjectMetadata , ingatlah, setelah beberapa saat kita mencari dan dengan URI ini kita mendapatkan objek yang kita ingat dengan tepat.


 searchSession = searchManager!!.resolveURI(uri, SearchOptions(), this) 

Entah bagaimana, bahkan membosankan.


Lapisan Pencarian dan Masa Depan yang Cerah


Di sebelah SearchManager masih ada sesuatu yang disebut lapisan pencarian . Lapisan itu disusun untuk menggabungkan pencarian dengan peta. Dia sendiri tahu cara menambahkan hasil ke dalamnya, memindahkan peta sehingga hasil ini ditampilkan dan melakukan kueri ulang saat pengguna memindahkan peta. Dalam banyak hal, ini mirip dengan SearchManager dan Session gabungan, tetapi pekerjaan yang terintegrasi dengan peta menambahkan fitur-fitur baru. Dan berbicara tentang mereka berada di luar cakupan artikel ini. Pada saat rilis MapKit 3.1, kami telah menjalankan lapisan pencarian di aplikasi nyata, sehingga Anda dapat mencoba menggunakannya di rumah Anda. Mungkin itu akan membuat pekerjaan pencarian Anda lebih mudah.



Kesimpulan


Saya harap setelah membaca artikel Anda akan memiliki pemahaman tentang bagaimana bekerja dengan pencarian di MapKit dengan kekuatan penuh. Tentunya masih ada beberapa momen halus dan non-sepele (misalnya, kami hampir tidak berbicara tentang tips dan lapisan pencarian). Sesuatu dapat ditemukan dalam dokumentasi, sesuatu untuk diklarifikasi dalam proyek-proyek tentang GitHub atau meminta dukungan kami.


Coba MapKit, gunakan pencarian di dalamnya dan datang ke Maps untuk membuatnya lebih baik!


PS Dan juga datang mengunjungi kami pada tanggal 29 November untuk mendengarkan tentang bagaimana pengaturan rute otomotif diatur . Ngomong -ngomong, bisa juga digunakan di MapKit , tapi ini adalah cerita yang sama sekali berbeda.

Source: https://habr.com/ru/post/id428564/


All Articles