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 BeispieleDie 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.
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 .
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) {
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.
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( "wi_fi", "", false, BusinessFilter.Values.fromBooleans( listOf(BusinessFilter.BooleanValue(true, true)) ) )
Nun der Enum-Filter:
val enumFilter = BusinessFilter( "type_cuisine", "", false, BusinessFilter.Values.fromEnums( listOf(BusinessFilter.EnumValue( Feature.FeatureEnumValue( "italian_cuisine", "", "" ), 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)
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.