MapKit est une bibliothèque de logiciels qui vous permet d'utiliser les données cartographiques et les technologies Yandex dans des applications mobiles. Elle a une documentation officielle qui contient déjà une description détaillée des méthodes de l'API, donc aujourd'hui nous allons parler d'autre chose.
Dans cet article, je parlerai aux lecteurs Habr des caractéristiques de la recherche dans MapKit et partagerai des recommandations et des astuces qui pourraient vous être utiles.
TL; DR Si vous ne souhaitez pas lire l'intégralité de l'article, voici deux des points les plus utiles pour compenser la lecture de l'avant-propos:
- N'oubliez pas de sauvegarder les sessions, sinon la recherche ne fonctionnera pas.
- Toutes les informations les plus intéressantes sont stockées dans les métadonnées de l'objet. Si vous souhaitez connaître l'adresse complète, les heures d'ouverture ou le prix d'une tasse de cappuccino dans un café particulier, vous avez besoin de métadonnées.
Les liens vers la documentation dans le texte seront pour Android, les classes et méthodes pour iOS sont appelées de la même manière.
Que peut rechercher
Tout d'abord, parlons de ce que la recherche dans MapKit peut faire. La recherche peut faire ce que vous attendez d'une application cartographique lorsque vous souhaitez y trouver quelque chose.
Lorsque vous tapez dans la barre de recherche «café», «rue Lev Tolstoï, 16» ou «tram 3», la recherche de texte fonctionne. Il s'agit du type de recherche le plus sophistiqué. Entassé en ce sens qu'il prend en charge l'ensemble maximal de paramètres pour la personnalisation. Vous pouvez essayer immédiatement de rechercher le long d'un itinéraire ou d'une rue qui vous intéresse, clarifier le nombre de résultats souhaité, définir la position de l'utilisateur, etc. Si après la première recherche vous souhaitez déplacer la carte ou appliquer des filtres à la demande («pharmacies avec piscine»), il s'agit de nouvelles demandes .
La recherche inversée est familière à la plupart des utilisateurs sur la question «Qu'est-ce qui est ici?». Il vous permet de cliquer sur la carte pour déterminer quelle rue ou maison est "sous le curseur" ou quelles organisations sont proches de ce point. La recherche par URI est nécessaire lorsque vous souhaitez trouver un objet spécifique. Il peut être utilisé, par exemple, pour créer des signets dans une application. Nous avons trouvé notre café préféré, marqué d'un astérisque - la prochaine fois, il sera possible de trouver exactement cette organisation par URI, où que se trouve la fenêtre de la carte. Ni la recherche inversée ni les recherches d'URI ne prennent en charge les demandes supplémentaires, car il n'y a rien à spécifier pour elles.
Une autre possibilité qui réside dans la recherche est les indices de recherche , qui vous permettent de compléter automatiquement la requête pendant que vous la tapez. Mais nous reporterons une histoire détaillée à leur sujet pour une autre fois.
Organisation de la demande
La recherche, comme de nombreuses parties de MapKit, fonctionne de manière asynchrone. L'objet principal pour travailler avec cette asynchronie est la session de recherche . Regardons un petit exemple.
Un peu d'exemplesLes exemples de l'article seront sur Kotlin pour faciliter le travail avec des valeurs optionnelles et moins de code passe-partout. MapKit a une application de démonstration . Il peut être utilisé pour tester des exemples, mais pour cela, SearchActivity
doit être converti de Java en Kotlin. showMessage
, qui apparaît de temps en temps dans le code, est un moyen pratique pour vous d'afficher une ligne de texte à l'écran ou dans le journal.
Immédiatement après avoir appelé submit
contrôle reviendra à votre code et lorsque MapKit recevra une réponse du serveur, SearchListener sera appelé.
La session de recherche vous permet de:
- Annuler la demande . Par exemple, si l'utilisateur ferme l'écran de recherche.
- Répétez la demande en cas d'erreur. Par exemple, en cas de problème avec le réseau.
- Poursuivez l'interaction avec la recherche une fois la réponse reçue. Les nouvelles demandes sont faites pendant la session.
Une session lors de l'annulation est automatiquement annulée. Cela signifie que s'il n'est pas enregistré du côté du code client, la recherche ne fonctionnera pas.
N'oubliez pas de sauvegarder la session, la session est votre amie!
Options de recherche
Une manière générale de configurer les requêtes de recherche consiste à SearchOptions
classe SearchOptions
, qui vous permet de modifier les paramètres de requête.
- Le principal de ces paramètres est
SearchType
. Il vous permet de spécifier si vous voulez voir des toponymes, des organisations ou des transports dans la réponse (vous n'aurez probablement pas besoin d'autres types). - Un autre paramètre de requête important est l' extrait de code . Nous en parlerons plus en détail dans la section sur le dispositif de réponse.
- Si vous souhaitez obtenir la géométrie du toponyme (par exemple, les rues ou les zones), vous devez le commander via
setGeometry(true)
. Gardez à l'esprit que la géométrie est assez "lourde" en termes de données transmises. - Par défaut, la recherche ne renvoie pas les organisations fermées (temporairement ou définitivement), mais si vous en avez besoin, vous devez définir
setSearchClosed(true)
.
En plus des paramètres répertoriés, il y en a d'autres qui peuvent vous être utiles, ils peuvent être trouvés dans la documentation de la classe. Notez que toutes les requêtes ne prennent pas en charge toutes les combinaisons de paramètres. La documentation de chaque méthode SearchOptions
ou Session
indique les paramètres de SearchOptions
qu'il comprend.
Comment est arrangée la réponse
À en juger par les questions à l'appui, la plupart des utilisateurs sont confus par le format de la réponse de recherche. Si vous regardez la classe de réponse, elle semble assez simple (au moins, la partie intéressante pour nous):
public class Response { public synchronized SearchMetadata getMetadata(); public synchronized GeoObjectCollection getCollection();
Ici, getCollection()
renvoie les objets dans la réponse, et getMetadata()
est quelques données supplémentaires qui contiennent, par exemple, des informations sur la fenêtre de réponse , le type de classement et le nombre de résultats trouvés . Si vous regardez à l'intérieur de GeoObjectCollection
vous pouvez voir qu'il contient des Item
qui peuvent être d'autres GeoObjectCollection
ou GeoObject
.
Il n'y a pas de collections à l'intérieur des collections dans la recherche (du moins pas encore), alors jetons un coup d'œil à GeoObject
. À l'intérieur de l'objet, il y a un nom ( getName()
), une description ( getDescriptionText()
), un cadre ( getBoundingBox()
), un ensemble de géométries ( getGeometry()
) et quelques autres méthodes pas très claires. Où sont les numéros de téléphone de l'organisation? Comment comprendre à quelle ville le toponyme fait référence?
Selon les méthodes de l'objet, ce n'est pas très clair.
Geoobject
Il est temps de parler davantage de GeoObject
.
GeoObject
est un objet "carte" de base. À l'intérieur, il peut y avoir un événement routier, un objet distinct du résultat de la recherche, une manœuvre sur l'itinéraire ou un objet sur la carte (POI), tel qu'un monument ou une organisation notable.
Tout ce qui est le plus intéressant sur l'objet est stocké dans des métadonnées. Ils sont accessibles à l'aide de la méthode getMetadataContainer()
. La clé de ce conteneur est le type de métadonnées. Si vous voyez quelque chose dans la documentation qui se termine par le mot Metadata
, vous êtes probablement ici. A la recherche de différents morceaux de "métadonnées" 15.

Les métadonnées peuvent être divisées en plusieurs types. Le premier type est des métadonnées qui déterminent le type auquel appartient l'objet: toponymes ( ToponymObjectMetadata
), organisations ( BusinessObjectMetadata
) ou transport ( TransitObjectMetadata
). Dans les métadonnées du toponyme, vous pouvez trouver une adresse structurée et une géométrie détaillée. Les métadonnées de l'organisation sont les heures d'ouverture ou le site de l'entreprise. Ces métadonnées sont déterminées par le type de recherche dans la demande - si vous avez recherché uniquement des toponymes, chaque objet de la réponse doit avoir des métadonnées correspondantes. Si vous cherchiez des noms de lieux ou des organisations, alors chaque objet aura au moins une des deux "métadonnées".
Voici comment trouver les numéros de téléphone de l'entreprise:
val phones = response.collection.children.firstOrNull()?.obj ?.metadataContainer ?.getItem(BusinessObjectMetadata::class.java) ?.phones
Et voici comment trouver la ville dans une adresse structurée:
val city = response.collection.children.firstOrNull()?.obj ?.metadataContainer ?.getItem(ToponymObjectMetadata::class.java) ?.address ?.components ?.firstOrNull { it.kinds.contains(Address.Component.Kind.LOCALITY) } ?.name
Le deuxième type est celui des métadonnées fournies avec l'objet, bien que vous n'en ayez pas posé la question. Le type principal que vous devez connaître est URIObjectMetadata
. À l'intérieur de URIObjectMetadata
l'identifiant unique de l'objet est stocké, qui doit être transmis dans la recherche par URI .
Et le troisième type est les métadonnées, qui n'entreront dans la réponse que si vous demandez spécifiquement une recherche à ce sujet. D'une manière différente, ces métadonnées sont appelées extraits . Les extraits de code sont de petits éléments de données supplémentaires qui changent plus souvent que les données de référence de base, ou dont tout le monde n'a pas besoin. Cela peut être une note, un lien vers des photos ou des panoramas, le taux de change ou le prix du carburant dans une station-service. La liste des extraits doit être définie à l'aide des options de recherche. Si le serveur a un extrait commandé, il l'ajoutera à l'objet correspondant.
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) {
Toutes les métadonnées répertoriées ci-dessus sont ajoutées aux objets individuels dans la réponse. Il existe également des métadonnées qui sont ajoutées à l'ensemble de la réponse. Mais ils sont mis en évidence dans les méthodes SearchMetadata
et ils n'ont pas besoin d'être extraits d'une collection spéciale.
Exemples d'utilisation
Passons maintenant en revue les principales méthodes des classes de recherche, examinons des exemples d'utilisation et certains points non évidents qui leur sont associés.
Recherche de texte
La méthode principale pour la recherche de texte (et pour la recherche entière, probablement) est de submit
:
Session submit( String text, Geometry geometry, SearchOptions searchOptions, SearchListener searchListener );
- Le paramètre
text
devrait contenir le texte que vous souhaitez rechercher. - Le paramètre de
geometry
est un peu plus délicat. Selon le type de géométrie transféré, la recherche se comportera différemment:
- Si vous passez un point, la recherche sera effectuée dans une petite fenêtre à côté de ce point.
- Si vous passez une fenêtre rectangulaire (
BoundingBox
) ou un polygone de quatre points, il sera utilisé comme champ de recherche. Un exemple simple d'une telle fenêtre est la zone visible de la carte. - Enfin, si vous passez la polyligne , alors la fenêtre la décrivant sera utilisée comme fenêtre de recherche, et le classement sera effectué en tenant compte de cette polyligne.
- Nous avons déjà parlé de
SearchOptions
et SearchListener
ci-dessus.
Le serveur peut considérer que la bonne réponse ne se trouve pas dans la fenêtre dans laquelle la recherche initiale a été effectuée («café à Vladivostok» lorsque la fenêtre de recherche est à Moscou). Dans ce cas, vous devrez prendre la fenêtre de réponse et y déplacer la carte pour que les résultats soient visibles à l'écran (les nouvelles demandes ne se permettent pas de le faire et ne demandent pas de déplacer la carte).
La méthode d' submit
a un jumelage d'envoi:
Session submit( String text, Polyline polyline, Geometry geometry, SearchOptions searchOptions, SearchListener searchListener );
avec un paramètre supplémentaire. Ce paramètre peut être utilisé pour transférer une grande polyligne (par exemple, un itinéraire vers une autre ville) et une petite fenêtre de recherche. Ensuite, la recherche elle-même supprimera la partie nécessaire de la polyligne transférée et l'utilisera uniquement pour la requête.
Re-demandes
Contrairement aux autres types de demandes, les nouvelles demandes sont effectuées à l'aide d'une session de recherche, qui renvoie le même submit
et son frère jumeau. Certaines des méthodes de session sont simples et directes:
- vous pouvez modifier la fenêtre de demande (lorsque l'utilisateur, par exemple, a déplacé la carte)
- les options de recherche peuvent être mises à jour (par exemple, pour mettre à jour la position de l'utilisateur)
- Vous pouvez changer le type de classement - par distance ou par note .
Pour effectuer une recherche affinée, vous devez utiliser la méthode resubmit
. Il accepte le même SearchListener
qu'une recherche régulière. Avant de l'appeler, vous pouvez modifier plusieurs paramètres de session. Par exemple, modifiez simultanément le type de classement et appliquez des filtres.
Filtres
Puisque nous parlons de filtres. Les filtres sont lorsque le Wi-Fi et la cuisine italienne. Ils ont probablement la syntaxe la plus déroutante de toutes les interfaces de recherche dans MapKit. Cela est dû au fait que les mêmes structures de données sont utilisées pour obtenir des filtres à partir de la réponse de recherche et pour spécifier des filtres dans la nouvelle demande.
Les filtres sont de deux types. Les filtres booléens supposent seulement deux valeurs mutuellement exclusives - oui ou non. Cela peut être la présence d'une connexion Wi-Fi dans un café, des toilettes dans une station-service ou un parking à proximité de l'organisation. Les filtres d'énumération supposent de nombreuses valeurs qui peuvent être demandées ensemble. C'est, par exemple, le type de cuisine pour un café ou les types de carburant dans une station-service.
Voyons d'abord comment obtenir les filtres disponibles pour le redémarrage en cours:
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)}" } }
Dans la ligne résultante, pour les filtres booléens, seul l'identifiant sera affiché, et pour les filtres enum, l'identifiant du filtre lui-même et les identifiants des valeurs disponibles. Maintenant, armés de la connaissance des identifiants disponibles, nous chercherons les cafés de la cuisine italienne qui ont le Wi-Fi. Ajoutez d'abord un filtre booléen:
val boolFilter = BusinessFilter( "wi_fi", "", false, BusinessFilter.Values.fromBooleans( listOf(BusinessFilter.BooleanValue(true, true)) ) )
Maintenant, le filtre enum:
val enumFilter = BusinessFilter( "type_cuisine", "", false, BusinessFilter.Values.fromEnums( listOf(BusinessFilter.EnumValue( Feature.FeatureEnumValue( "italian_cuisine", "", "" ), true, true )) ) )
Enfin, vous pouvez ajouter des filtres à la session et appeler resubmit()
:
searchSession!!.setFilters(listOf(boolFilter, enumFilter)) searchSession!!.resubmit(this)
Veuillez noter que vous ne pouvez pas définir de filtres pour la première requête. Vous devez d'abord obtenir une réponse de recherche qui répertorie les filtres disponibles. Et seulement alors pour former un redémarrage.
Résultats supplémentaires
Une autre session vous permet de vérifier s'il existe des résultats de recherche supplémentaires pour votre demande. Et, si c'est le cas, prenez-les. Par exemple, lorsque vous recherchez un café dans votre ville, il est fort probable que tous ne tiennent pas sur une seule page de la réponse de recherche. fetchNextPage
hasNextPage
et fetchNextPage
nécessaires pour afficher les pages suivantes de la liste. Ici, vous devez savoir que, tout d'abord, l'appel à fetchNextPage
une exception si la méthode hasNextPage
renvoie false
. Et deuxièmement, l'utilisation de ces méthodes implique que les paramètres restants ne changent pas. Autrement dit, la session est utilisée soit pour affiner la demande ( resubmit()
), soit pour récupérer les pages suivantes ( fetchNextPage()
). La combinaison de ces modes n'est pas nécessaire.
Recherche inversée
La recherche inversée pour plus de commodité est également appelée submit
:
Session submit( Point point, Integer zoom, SearchOptions searchOptions, SearchListener searchListener )
Il diffère des autres types de requêtes en ce qu'il ne nécessite qu'un seul type de recherche pour entrer. Vous passez soit le type GEO
et recherchez des noms de lieux, soit le type BIZ
et recherchez des organisations. Il n'y en a pas de troisième.
Lorsque vous effectuez une recherche avec un type GEO
, certains points doivent être clarifiés. Veuillez noter que la réponse contiendra plusieurs objets dans la hiérarchie (c'est-à-dire qu'elle comprendra une maison, une rue, une ville, etc.). Dans les cas simples, vous pouvez prendre uniquement le premier objet. Dans les plus complexes, recherchez dans la hiérarchie souhaitée.
Le niveau de zoom est nécessaire pour produire des résultats adéquats en fonction de ce que l'utilisateur voit sur la carte. Imaginez un utilisateur regardant une carte à l'échelle nationale. Ensuite, il sera étrange pour lui de cliquer sur une rue ou une maison séparée si l'utilisateur a accidentellement réussi à y pénétrer. Assez de villes. C'est à cela que sert le paramètre de zoom
.
val point = Point(55.734, 37.588)
Recherche par URI
Tout est assez clair ici - nous prenons l'URI des URIObjectMetadata
, souvenez-vous-en, après un certain temps nous URIObjectMetadata
dans la recherche et par cet URI nous obtenons exactement l'objet dont nous nous sommes souvenus.
searchSession = searchManager!!.resolveURI(uri, SearchOptions(), this)
D'une certaine façon, même ennuyeux.
Couche de recherche et avenir brillant
À côté de SearchManager
il existe toujours une chose appelée couche de recherche . La couche a été conçue pour combiner la recherche avec la carte. Il sait lui-même comment y ajouter des résultats, déplacer la carte pour que ces résultats s'affichent et faire des requêtes lorsque l'utilisateur déplace la carte. À bien des égards, il est similaire au SearchManager
et à la Session
combinés, mais le travail intégré avec la carte ajoute de nouvelles fonctionnalités. Et en parler dépasse le cadre de cet article. Au moment de la sortie de MapKit 3.1, nous avions déjà parcouru la couche de recherche dans les applications réelles, vous pouvez donc essayer de l'utiliser dans votre maison. Cela facilitera peut-être votre recherche.
Conclusion
J'espère qu'après avoir lu l'article, vous comprendrez comment travailler avec la recherche dans MapKit en force. Certes, il y avait encore des moments subtils et non triviaux (par exemple, nous n'avons presque pas parlé des conseils et de la couche de recherche). Quelque chose peut être trouvé dans la documentation, quelque chose à clarifier dans les projets sur GitHub ou demandez notre support.
Essayez MapKit, utilisez la recherche et accédez à Maps pour les rendre encore meilleurs!
PS Et venez également nous rendre visite le 29 novembre pour écouter comment le backend de routage automobile est organisé . Qui, soit dit en passant, peut également être utilisé dans MapKit , mais c'est une histoire complètement différente.