MapKit-Suche: Tipps und Tricks


MapKit ist eine Softwarebibliothek, mit der Sie Kartendaten und Yandex-Technologien in mobilen Anwendungen verwenden können. Sie hat eine offizielle Dokumentation , die bereits eine detaillierte Beschreibung der API-Methoden enthält, daher werden wir heute über etwas anderes sprechen.


In diesem Beitrag werde ich Habr-Lesern die Funktionen der Suche in MapKit erläutern und Empfehlungen und Tricks mitteilen, die für Sie nützlich sein können.


TL; DR Wenn Sie nicht den gesamten Artikel lesen möchten, sind hier zwei der nützlichsten Punkte als Ausgleich für das Lesen des Vorworts:


  • Vergessen Sie nicht, Sitzungen zu speichern, da sonst die Suche nicht funktioniert.
  • Alle interessantesten Informationen werden in den Metadaten des Objekts gespeichert. Wenn Sie die vollständige Adresse, die Öffnungszeiten oder die Kosten einer Tasse Cappuccino in einem bestimmten Café erfahren möchten, benötigen Sie Metadaten.

Die Links zur Dokumentation im Text gelten für Android, Klassen und Methoden für iOS werden auf die gleiche Weise aufgerufen.


Was kann suchen


Lassen Sie uns zunächst darüber sprechen, was die Suche in MapKit bewirken kann. Die Suche kann das tun, was Sie von einer Kartenanwendung erwarten, wenn Sie dort etwas finden möchten.



Wenn Sie in die Suchleiste "Cafe", "Lev Tolstoy Street, 16" oder "Tram 3" eingeben, funktioniert die Textsuche . Dies ist die ausgefeilteste Art der Suche. In dem Sinne aufgeschichtet, dass es den maximalen Satz von Parametern für die Anpassung unterstützt. Sie können sofort versuchen, entlang einer Route oder Straße zu suchen, an der Sie interessiert sind, die gewünschte Anzahl von Ergebnissen zu klären, die Position des Benutzers festzulegen usw. Wenn Sie nach der ersten Suche die Karte verschieben oder Filter auf die Anfrage anwenden möchten („Apotheken mit Pool“), handelt es sich um erneute Anfragen .


Die umgekehrte Suche ist den meisten Benutzern bei der Frage „Was ist hier?“ Vertraut. Sie können auf die Karte klicken, um festzustellen, welche Straße oder welches Haus sich "unter dem Cursor" befindet oder welche Organisationen sich in der Nähe dieses Punkts befinden. Die Suche nach URI ist erforderlich, wenn Sie ein bestimmtes Objekt suchen möchten. Es kann beispielsweise verwendet werden, um Lesezeichen in einer Anwendung zu erstellen. Wir haben unser Lieblingscafé gefunden und es mit einem Sternchen markiert. Das nächste Mal ist es möglich, genau diese Organisation nach URI zu finden, wo immer sich das Kartenfenster befindet. Weder die umgekehrte Suche noch die URI-Suche unterstützen erneute Anforderungen, da für sie nichts angegeben werden muss.


Eine weitere Möglichkeit, die in der Suche enthalten ist, sind Suchhinweise , mit denen Sie die Abfrage automatisch ergänzen können, während Sie sie eingeben. Aber wir werden eine detaillierte Geschichte über sie um ein anderes Mal verschieben.


Wie die Anfrage angeordnet ist


Die Suche funktioniert wie viele Teile von MapKit asynchron. Das Hauptobjekt für die Arbeit mit dieser Asynchronität ist die Suchsitzung . Schauen wir uns ein kleines Beispiel an.


Ein bisschen über Beispiele

Die Beispiele in diesem Artikel beziehen sich auf Kotlin, um die Arbeit mit optionalen Werten und weniger Boilerplate-Code zu vereinfachen. MapKit hat eine Demo-Anwendung . Es kann zum Testen von Beispielen verwendet werden, aber dafür sollte SearchActivity von Java nach Kotlin konvertiert werden. showMessage , die von Zeit zu Zeit im Code angezeigt wird, können Sie bequem eine Textzeile auf dem Bildschirm oder im Protokoll anzeigen.


 // `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") } } ) 

Unmittelbar nach dem Aufruf von submit kehrt die Steuerung zu Ihrem Code zurück. Wenn MapKit eine Antwort vom Server erhält, wird SearchListener aufgerufen.


Mit der Suchsitzung können Sie:


  • Anfrage abbrechen . Zum Beispiel, wenn der Benutzer den Suchbildschirm schließt.
  • Anfrage im Fehlerfall wiederholen . Zum Beispiel, wenn es Probleme mit dem Netzwerk gab.
  • Setzen Sie die Interaktion mit der Suche fort, nachdem die Antwort empfangen wurde. Anfragen werden über die Sitzung erneut gestellt.

Eine Sitzung bei Stornierung wird automatisch abgebrochen. Dies bedeutet, dass die Suche nicht funktioniert, wenn sie nicht auf der Client-Codeseite gespeichert wird.
Vergessen Sie nicht, die Sitzung zu speichern, die Sitzung ist Ihr Freund!


Suchoptionen


Eine allgemeine Methode zum Konfigurieren von Suchabfragen ist die SearchOptions Klasse, mit der Sie Abfrageparameter ändern können.


  • Der Hauptparameter ist SearchType . Hier können Sie angeben, ob Toponyme, Organisationen oder Transportmittel in der Antwort angezeigt werden sollen (wahrscheinlich benötigen Sie keine anderen Typen).
  • Ein weiterer wichtiger Abfrageparameter sind Snippets . Wir werden im Abschnitt über das Antwortgerät ausführlicher darauf eingehen.
  • Wenn Sie die Geometrie des Toponyms (z. B. Straßen oder Gebiete) setGeometry(true) , müssen Sie es über setGeometry(true) . Beachten Sie, dass die Geometrie in Bezug auf die übertragenen Daten ziemlich "schwer" ist.
  • Standardmäßig gibt die Suche keine geschlossenen (vorübergehend oder dauerhaft) Organisationen zurück. Wenn Sie sie jedoch benötigen, müssen Sie setSearchClosed(true) .

Zusätzlich zu den aufgelisteten Parametern gibt es einige weitere, die für Sie nützlich sein können. Sie finden sie in der Dokumentation für die Klasse. Beachten Sie, dass nicht alle Abfragen alle Parameterkombinationen unterstützen. Die Dokumentation für jede SearchManager oder Session Methode gibt an, welche Parameter von SearchOptions verstanden werden.


Wie ist die Antwort angeordnet?


Nach den unterstützenden Fragen zu urteilen, sind die meisten Benutzer durch das Format der Suchantwort verwirrt. Wenn Sie sich die Antwortklasse ansehen, sieht sie recht einfach aus (zumindest der interessante Teil für uns):


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

Hier gibt getCollection() die Objekte in der Antwort zurück, und getMetadata() enthält einige zusätzliche Daten , die beispielsweise Informationen zum Antwortfenster , zur Art der Rangfolge und zur Anzahl der gefundenen Ergebnisse enthalten . Wenn Sie in die GeoObjectCollection Sie sehen, dass sie ein GeoObjectCollection enthält, das entweder eine andere GeoObjectCollection oder GeoObject .


Es gibt keine Sammlungen in Sammlungen in der Suche (zumindest noch nicht). Schauen GeoObject uns also GeoObject . Innerhalb des Objekts gibt es einen Namen ( getName() ), eine Beschreibung ( getDescriptionText() ), einen Rahmen ( getBoundingBox() ), eine Reihe von Geometrien ( getGeometry() ) und einige andere nicht sehr klare Methoden. Wo sind die Telefonnummern der Organisation? Wie kann man verstehen, auf welche Stadt sich das Toponym bezieht?


Nach den Methoden des Objekts ist dies nicht sehr klar.


Geoobjekt


Es ist Zeit, mehr über GeoObject zu sprechen.


GeoObject ist solch ein grundlegendes "Karten" -Objekt. Darin kann sich ein Straßenereignis, ein vom Suchergebnis getrenntes Objekt, ein Manöver auf der Route oder ein Objekt auf der Karte (POI) befinden, z. B. ein Denkmal oder eine bemerkenswerte Organisation.



Das Interessanteste an dem Objekt wird in Metadaten gespeichert. Auf sie kann mit der Methode getMetadataContainer() zugegriffen werden. Der Schlüssel in diesem Container ist der Metadatentyp. Wenn Sie in der Dokumentation etwas sehen, das mit dem Wort Metadata endet, sind Sie höchstwahrscheinlich hier. Bei der Suche nach verschiedenen "Metadaten" 15.



Metadaten können in verschiedene Typen unterteilt werden. Der erste Typ sind Metadaten, die bestimmen, zu welchem ​​Typ das Objekt gehört: Toponyme ( ToponymObjectMetadata ), Organisationen ( BusinessObjectMetadata ) oder Transport ( TransitObjectMetadata ). In den Metadaten für das Toponym finden Sie eine strukturierte Adresse und eine detaillierte Geometrie. Die Metadaten für die Organisation sind die Betriebsstunden oder der Standort des Unternehmens. Diese Metadaten werden durch die Art der Suche in der Anforderung bestimmt. Wenn Sie nur nach Toponymen gesucht haben, sollte jedes Objekt in der Antwort über entsprechende Metadaten verfügen. Wenn Sie nach Ortsnamen oder Organisationen gesucht haben, verfügt jedes Objekt über mindestens eine von zwei "Metadaten".


So finden Sie Firmen-Telefonnummern:


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

Und so finden Sie die Stadt in einer strukturierten Adresse:


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

Der zweite Typ sind Metadaten, die mit dem Objekt geliefert werden, obwohl Sie nicht danach gefragt haben. Der Haupttyp, den Sie kennen müssen, ist URIObjectMetadata . In den URIObjectMetadata die eindeutige Kennung des Objekts gespeichert, die bei der Suche nach URI übergeben werden muss .


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

Und der dritte Typ sind Metadaten, die nur dann in die Antwort aufgenommen werden, wenn Sie ausdrücklich nach einer Suche danach fragen. Auf andere Weise werden diese Metadaten als Snippets bezeichnet . Snippets sind kleine zusätzliche Daten, die sich entweder häufiger ändern als die grundlegenden "Referenz" -Daten oder die nicht jeder benötigt. Dies kann eine Bewertung, ein Link zu Fotos oder Panoramen, der Wechselkurs oder der Kraftstoffpreis an einer Tankstelle sein. Die Liste der Snippets muss mithilfe der Suchoptionen festgelegt werden. Wenn der Server über ein geordnetes Snippet verfügt, wird es dem entsprechenden Objekt hinzugefügt.


 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" ) } 

Alle oben aufgeführten Metadaten werden einzelnen Objekten in der Antwort hinzugefügt. Es gibt auch Metadaten, die der gesamten Antwort hinzugefügt werden. Sie werden jedoch in SearchMetadata Methoden herausgebracht und müssen nicht aus einer speziellen Sammlung extrahiert werden.


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

Anwendungsbeispiele


Lassen Sie uns nun die wichtigsten Methoden von Suchklassen durchgehen, Anwendungsbeispiele und einige damit verbundene nicht offensichtliche Punkte betrachten.


Textsuche


Die Hauptmethode für die Textsuche (und wahrscheinlich für die gesamte Suche) ist submit :


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

  • Es wird erwartet, dass der Textparameter den Text enthält, den Sie suchen möchten.
  • Der geometry ist etwas kniffliger. Je nachdem, welche Art von Geometrie übertragen wird, verhält sich die Suche unterschiedlich:
    • Wenn Sie einen Punkt übergeben, wird die Suche in einem kleinen Fenster neben diesem Punkt durchgeführt.
    • Wenn Sie ein rechteckiges Fenster ( BoundingBox ) oder ein Polygon mit vier Punkten übergeben, wird es als Suchfeld verwendet. Ein einfaches Beispiel für ein solches Fenster ist der sichtbare Bereich der Karte.
    • Wenn Sie die Polylinie übergeben , wird das sie beschreibende Fenster als Suchfenster verwendet und das Ranking unter Berücksichtigung dieser Polylinie durchgeführt.
  • Wir haben bereits oben über SearchOptions und SearchListener .

Der Server kann davon ausgehen, dass sich die richtige Antwort nicht in dem Fenster befindet, in dem die erste Suche durchgeführt wurde („Café in Wladiwostok“, wenn sich das Suchfenster in Moskau befindet). In diesem Fall müssen Sie das Antwortfenster öffnen und die Karte dorthin verschieben, damit die Ergebnisse auf dem Bildschirm angezeigt werden (erneute Anforderungen erlauben sich dies nicht und sie bitten nicht, die Karte zu verschieben).


Die submit Methode hat einen submit Zwilling:


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

mit einem zusätzlichen Parameter. Mit diesem Parameter können Sie eine große Polylinie (z. B. eine Route in eine andere Stadt) und ein kleines Suchfenster übertragen. Die Suche selbst schneidet dann den erforderlichen Teil der übertragenen Polylinie aus und verwendet ihn nur für die Abfrage.


Anfragen


Erneute Anfragen werden im Gegensatz zu anderen Arten von Anfragen mit Hilfe einer Suchsitzung durchgeführt, die dieselbe submit und ihren Zwillingsbruder zurückgibt. Ein Teil der Methoden der Sitzung ist einfach und unkompliziert:


  • Sie können das Anforderungsfenster ändern (wenn der Benutzer beispielsweise die Karte verschoben hat)
  • Suchoptionen können aktualisiert werden (z. B. um die Position des Benutzers zu aktualisieren)
  • Sie können die Art der Rangfolge ändern - nach Entfernung oder Bewertung .

Um eine verfeinerte Suche durchzuführen, müssen Sie die Methode zum resubmit verwenden. Es akzeptiert denselben SearchListener wie eine reguläre Suche. Vor dem Aufruf können Sie mehrere Sitzungsparameter ändern. Ändern Sie beispielsweise gleichzeitig die Art der Rangfolge und wenden Sie Filter an.


Filter


Da sprechen wir über Filter. Filter sind bei Wi-Fi und italienischer Küche. Sie haben wahrscheinlich die verwirrendste Syntax aller Suchoberflächen in MapKit. Dies liegt an der Tatsache, dass dieselben Datenstrukturen verwendet werden, um Filter aus der Suchantwort abzurufen und Filter in der erneuten Anforderung anzugeben.



Es gibt zwei Arten von Filtern. Boolesche Filter nehmen nur zwei sich gegenseitig ausschließende Werte an - Ja oder Nein. Dies kann das Vorhandensein von WLAN in einem Café, einer Toilette an einer Tankstelle oder einem Parkplatz in der Nähe der Organisation sein. Aufzählungsfilter nehmen viele Werte an, die zusammen angefordert werden können. Dies ist beispielsweise die Art der Küche für ein Café oder die Art des Kraftstoffs an einer Tankstelle.


Lassen Sie uns zunächst sehen, wie Sie die Filter für den aktuellen Neustart verfügbar machen:


 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)}" } } 

In der resultierenden Zeile wird für Boolesche Filter nur der Bezeichner angezeigt, und für Enum-Filter der Bezeichner des Filters selbst und die Bezeichner der verfügbaren Werte. Mit der Kenntnis der verfügbaren Kennungen werden wir nun nach den Cafés der italienischen Küche suchen, die über WLAN verfügen. Fügen Sie zuerst einen Booleschen Filter hinzu:


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

Nun der 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 )) ) ) 

Schließlich können Sie der Sitzung Filter hinzufügen und resubmit() aufrufen:


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

Bitte beachten Sie, dass Sie keine Filter für die erste Abfrage festlegen können. Zuerst müssen Sie eine Suchantwort erhalten, in der die verfügbaren Filter aufgelistet sind. Und erst dann einen Neustart bilden.


Zusätzliche Ergebnisse

In einer anderen Sitzung können Sie überprüfen, ob zusätzliche Suchergebnisse für Ihre Anfrage vorhanden sind. Und wenn ja, holen Sie sie sich. Wenn Sie beispielsweise nach einem Café in Ihrer Stadt suchen, passen höchstwahrscheinlich nicht alle auf eine Seite der Suchantwort. Zum Anzeigen der folgenden Seiten in der Liste fetchNextPage hasNextPage und fetchNextPage erforderlich. Hier müssen Sie wissen, dass der Aufruf von fetchNextPage eine Ausnahme hasNextPage , wenn die hasNextPage Methode false zurückgibt. Und zweitens impliziert die Verwendung dieser Methoden, dass sich die verbleibenden Parameter nicht ändern. Das heißt, die Sitzung wird entweder zum Verfeinern der Anforderung ( resubmit() ) oder zum Abrufen der folgenden Seiten ( fetchNextPage() ) verwendet. Das Kombinieren dieser Modi ist nicht erforderlich.


Suche umkehren


Die umgekehrte Suche nach Bequemlichkeit wird auch als submit :


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

Es unterscheidet sich von anderen Arten von Abfragen darin, dass nur eine Art von Suche erforderlich ist . Sie übergeben entweder den GEO Typ und suchen nach Ortsnamen oder den BIZ Typ und suchen nach Organisationen. Es gibt kein drittes.


Bei der Suche mit einem GEO Typ müssen einige Punkte geklärt werden. Bitte beachten Sie, dass die Antwort mehrere Objekte in der Hierarchie enthält (dh die Antwort enthält ein Haus, eine Straße, eine Stadt usw.). In einfachen Fällen können Sie nur das erste Objekt nehmen. Durchsuchen Sie bei komplexeren die gewünschte Hierarchie.


Die Zoomstufe wird benötigt, um je nach dem, was der Benutzer auf der Karte sieht, angemessene Ergebnisse zu erzielen. Stellen Sie sich einen Benutzer vor, der sich bundesweit eine Karte ansieht. Dann ist es für ihn seltsam, auf eine separate Straße oder ein separates Haus zu klicken, wenn der Benutzer versehentlich in diese hineingekommen ist. Ziemlich genug Städte. Dafür dient der zoom Parameter.


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

Suche nach URI


Hier ist alles ganz klar - wir nehmen den URI aus den URIObjectMetadata , erinnern uns daran, nach einer Weile URIObjectMetadata wir in die Suche und durch diesen URI erhalten wir genau das Objekt, an das wir uns erinnern.


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

Irgendwie sogar langweilig.


Suchebene und glänzende Zukunft


Neben SearchManager gibt es noch eine Suchschicht . Die Ebene wurde konzipiert, um die Suche mit der Karte zu kombinieren. Er selbst weiß, wie man Ergebnisse hinzufügt, die Karte so verschiebt, dass diese Ergebnisse angezeigt werden, und erneut Abfragen durchführt, wenn der Benutzer die Karte verschiebt. In vielerlei Hinsicht ähnelt es dem kombinierten SearchManager und der kombinierten Session , aber die integrierte Arbeit mit der Karte fügt neue Funktionen hinzu. Und darüber zu sprechen, würde den Rahmen dieses Artikels sprengen. Zum Zeitpunkt der Veröffentlichung von MapKit 3.1 hatten wir die Suchebene bereits in realen Anwendungen durchlaufen, sodass Sie versuchen können, sie zu Hause zu verwenden. Vielleicht erleichtert es Ihnen die Suche.



Fazit


Ich hoffe, dass Sie nach dem Lesen des Artikels ein Verständnis dafür haben, wie Sie mit der Suche in MapKit in vollem Umfang arbeiten können. Sicherlich gab es noch einige subtile und nicht triviale Momente (zum Beispiel haben wir fast nicht über die Tipps und die Suchebene gesprochen). In der Dokumentation finden Sie etwas, das Sie in Projekten auf GitHub klären können, oder fragen Sie unseren Support.


Probieren Sie MapKit aus, verwenden Sie die darin enthaltene Suche und kommen Sie zu Maps , um sie noch besser zu machen!


PS Und besuchen Sie uns am 29. November, um zu erfahren, wie das Automotive Routing Backend aufgebaut ist . Was übrigens auch in MapKit verwendet werden kann , aber das ist eine ganz andere Geschichte.

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


All Articles