Comment j'ai essayé de corriger une recherche de carte pour les pilotes

C'est une histoire sur la façon dont j'ai essayé de résoudre un problème étrange qui m'a empêché moi-même. À plus long terme, je dirai que je suis satisfait de la solution qui en résulte et que l’application a atteint sa fin logique. Cependant, pour l'exécuter pleinement, vous avez besoin de plus de ressources, j'ai donc décidé de faire une pause et de demander aux gens si quelqu'un d'autre en avait besoin. A cet effet (et aussi juste pour parler) j'écris ici.

Deux mots sur moi: je vis à Dublin, en Irlande, je travaille comme programmeur. Il ne se trouve pas exactement sur place, c'est pourquoi pendant mon temps libre à la maison, j'ai vu divers projets, principalement sur la table. J'écris sur Habré pour la première fois, même si je lis depuis de nombreuses années.

Le problème


Il y a très longtemps, alors que je devais beaucoup voyager dans des endroits inconnus pour le travail, j'ai commencé à remarquer que la recherche standard sur toutes les cartes n'est absolument pas applicable aux conducteurs aujourd'hui. Regardez: vous conduisez dans une zone inconnue et vous avez une flèche d'essence à zéro. Vos actions? Si à ce moment je ne suis pas seul dans la voiture, alors je dis au passager: "Eh bien, cherchez une station-service à proximité pendant que je conduis." Parce que si vous le faites vous-même, vous devez effectuer les actions suivantes:

  1. Arrêter
  2. Dans l'application cartographique, entrez «essence» dans la recherche (ou cliquez sur l'un des boutons rapides que certaines cartes proposent maintenant)
  3. L'application effectue une recherche et vous montre une immense carte avec une douzaine de stations-service
  4. Vous essayez de déterminer lequel est le plus proche de vous et cliquez dessus pour construire un itinéraire

À mon avis, un cauchemar. Tout d'abord, vous devez arrêter ou au moins attendre un feu de circulation. Parce que les cartes sont complexes et les icônes sont petites. Deuxièmement, la carte nifig ne vous dit pas quelle est la station-service la plus proche. À cet égard, Google est le pire de tous: même dans les résultats sous forme de liste, il pousse constamment vers le haut, pas l'endroit le plus proche, et le plus noté / avec photos / payé / je n'en ai aucune idée.



Eh bien, le troisième facteur: nous bougeons. Le résultat que les cartes délivrées étaient pertinentes à un moment donné, mais nous en sommes déjà loin. Avez-vous remarqué que même l'itinéraire tracé par Google n'est pas mis à jour automatiquement si nous avons changé? Ce n'est que si la navigation a déjà été lancée qu'elle sera reconstruite.

En général, le problème est clair. Une logique qui fonctionne pour les piétons et leurs besoins, pour les conducteurs, ce n'est rien. Je ne me soucie pas de la cote de l'endroit et du type de cuisine - j'ai besoin, sans être distrait de la route, d'obtenir en temps réel un itinéraire vers la station-service la plus proche, la recharge, le parking, le guichet automatique, etc.

Idée


Essayons maintenant de déterminer le scénario de recherche idéal. Le critère est le suivant:

  • l'interaction est courte et claire pour ne pas distraire le conducteur
  • sortie transparente basée sur la distance
  • mise à jour en temps réel

La première chose qui le prie est de remplacer la recherche unique standard par une analyse. Autrement dit, la chaîne d'actions est quelque chose comme ceci:

  1. Lancement d'une analyse
  2. Rouler, regarder les résultats mis à jour en temps réel
  3. Quand j'ai aimé quelque chose, j'ai cliqué et tracé la route

Vous déterminez les critères de recherche à l'avance - en fait, c'est le type de lieu et le rayon du scan. De plus, pendant que la voiture se déplace, l'application fonctionne comme un radar. Assez rapidement, il est devenu clair que, premièrement, le rayon n'est pas rond, mais sous la forme d'une isoline. Deuxièmement, elle doit être construite non pas par la distance, mais par le temps, car les minutes sont beaucoup plus faciles à percevoir que les kilomètres.

Ensuite, j'ai pensé à un moyen de produire les résultats. Plus précisément, ai-je besoin d'une carte? Le conducteur regarde l'application d'un œil et interagit avec un doigt - il n'a pas besoin d'une carte, mais d'un gros texte et de gros boutons. Par conséquent, j'ai immédiatement décidé que l'écran principal serait une liste, et je pourrais d'abord ajouter une carte, puis voir si je devais la quitter.

Plan


Connaissant ma propre particularité des projets d'étirement, j'ai décidé de me donner 2 mois pour tout - au final je me suis rencontré en 3. En principe, l'application est assez simple:

  1. Client à partir d'une paire d'écrans (recherche, liste et carte)
  2. Envoie périodiquement au serveur ses coordonnées, son rayon de recherche et le type de lieux
  3. Le serveur construit une isoligne dans le temps (au fait, il a son nom en anglais - isochrone), fait une recherche par endroits et retourne une liste

Cela ressemble à un poumon plus léger. J'avais déjà une certaine expérience en cartographie (il y a quelques années, j'ai fait un portail immobilier où la recherche était sur la carte), donc la pile sur le backend était immédiatement claire:

  • importer des données d'OpenStreetMaps dans Elasticsearch
  • OpenTripPlanner pour créer des contours

Sur le client, j'ai pensé, décidé d'utiliser le nouveau framework de Google - Flutter. Il est multiplateforme, assez flexible et vous permet de créer des applications à part entière avec un minimum de code. Bien sûr, c'est brut et ce n'est pas clair ce qui est en production, mais ça a l'air parfait pour le prototypage. Il convient de préciser qu'à ce stade, j'avais une expérience dans le développement natif pour Android (j'étais chef d'équipe) et j'ai décidé, pour ainsi dire, d'affronter l'ennemi. L'ennemi n'était pas si effrayant.

Implémentation


Le premier prototype d'application a été prêt très rapidement - Flutter a un seuil d'entrée bas et une philosophie de type redux compréhensible. Curieusement, la description déclarative des interfaces était également agréable, ainsi que le redémarrage à chaud (React Native, votre bitmap). En général, l'impression était que Google était responsable de la plupart des maladies congénitales des tentatives précédentes. Cependant, je comprends les gens qui peuvent ne pas vouloir s'y lancer - quelqu'un n'aime pas les fléchettes, un nombre limité de widgets et le «débogage visuel» proposé ici est quelque chose de très brut.

Sur le backend, j'ai fait ce qui suit:

  1. Livré Nominatim, téléchargé l'extrait de données OpenStreetMaps (pris ici ) dans sa base de données en utilisant son utilitaire natif osm2pgsql. Pourquoi me suis-je tourné vers le petit mais très agréable géocodeur aigre ouvert Photon . Plus tôt, je l'ai déjà utilisé dans quelques projets - il génère un index Elasticsearch, y importe des données de la base de données Nominatim et recherche cet index. Je l'aime avec la vitesse et la cartographie pure (par exemple, j'ai essayé Pelias et je l'aimais moins). Son principal problème est l'ancienne version de l'élastique, mais dans mon cas, je n'avais pas besoin de la fonctionnalité du géocodeur lui-même, seulement des données, donc après l'importation, j'ai transféré l'index à l'installation de l'élastique de la dernière version avec une âme pure. Au fait, pourquoi ai-je choisi Elasticsearch? Il est très rapide et a pour fonction de trouver des coordonnées par polygone.
  2. La décharge - alias isochrone - a initialement généré OpenTripPlanner pour moi. Il s'agit d'un très bon planificateur d'itinéraire open source. Il fonctionne comme suit: il prend le même extrait OpenStreetMaps et le compile dans un grand graphique de route, qui, en tant qu'objet séparé, est enregistré sur le disque. Lorsque le serveur démarre, ce graphique est chargé dans la RAM et toutes les routes y sont recherchées. Avantages: prise en main rapide, fonctionnalités riches (par exemple, génère des contours à partir de la boîte) et bonne vitesse. Inconvénients: cette vitesse dépend de la quantité de RAM et la documentation est extrêmement dégoûtante. Documentation juste monstrueuse. Flashbacks vietnamiens.
  3. J'ai jeté une petite API sur le python, qui prend le type de lieu et le rayon de recherche en quelques secondes, demande un polygone à OpenTripPlanner, puis le recherche dans Elasticsearch. Il demande un itinéraire vers chaque emplacement trouvé (à nouveau depuis OpenTripPlanner), prend sa longueur et son temps. Après cela, toutes les données collectées sont magnifiquement emballées et retournées.

J'ai fait la mise à jour des résultats en décalant les coordonnées de l'appareil de 5 mètres. La carte est statique - je viens d'utiliser une api de cartes Google statiques (comme vous pouvez le voir, c'est le seul endroit où la société a quand même rampé dans notre monde confortable et ouvert). La première implémentation ressemblait à ceci:







Après avoir joué avec l'application, j'ai décidé de masquer la carte. Elle a bien compris pour quel polygone la recherche avait été effectuée et elle avait l'air amusante - c'était intéressant de voir comment cette pieuvre change de forme en temps réel. Cependant, ce divertissement n'a pas aidé l'application à remplir ses fonctions et occupait un tiers de l'écran.

Il m'est également venu à l'esprit d'ajouter une flèche, qui indiquait la direction de chaque résultat. Cela a fonctionné comme ceci:

  1. Rappelez-vous vos coordonnées précédentes
  2. Lors du déplacement, nous établissons l'itinéraire de la position précédente à la position actuelle
  3. Nous prenons le dernier segment de notre itinéraire et comparons avec le premier segment de l'itinéraire chaque résultat. Puisqu'ils sont posés le long du même réseau routier, avec une probabilité de 99%, l'angle entre eux est proche de 0 ou 180.

Cette astuce très simple facilite grandement la compréhension de savoir si nous nous dirigeons déjà vers l'endroit ou s'il faudra faire demi-tour.



À ce stade, j'étais très satisfait de l'application résultante et j'ai décidé d'essayer de la déployer dans plusieurs pays. Pourtant, l'Irlande est un très petit État, respectivement, et l'indice élastique et le graphique routier étaient petits. Pour les tests, j'ai décidé de connecter le Royaume-Uni voisin. Il est environ 4 fois plus grand et possède un réseau routier beaucoup plus dense (en particulier la capitale et les grandes villes). Et puis un problème est survenu.

Elasticsearch aurait très bien digéré l'augmentation de l'indice, mais avec OpenTripPlanner, il y a eu un échec complet. Il est écrit en Java et, comme je l'ai dit ci-dessus, génère un graphique des routes, de sorte qu'après le chargement dans la RAM. Le graphique pour l'Irlande était de 1 gigaoctet, pour le Royaume-Uni, il en était déjà 5. Il était bien sûr possible de le diviser en pays, régions et même régions, puis de rediriger vers le graphique souhaité en fonction des coordonnées de l'utilisateur. Cependant, cela n'a pas permis de tracer des routes entre les régions et, surtout, cela n'a pas résolu le besoin de conserver tous ces graphiques en mémoire. Enfin, la simple compilation de chacun de ces objets a pris TRÈS de ressources et a duré une éternité. Pour le plaisir, j'ai lancé sur ma machine (cadres 16 Go) l'assemblage du Comte de France, attendu une journée et annulé.

Évidemment, une technologie qui a fait ses preuves dans les petites tâches n'est pas du tout conçue pour évoluer (du moins pas avec mes ressources). Il doit donc soit admettre la défaite, soit ramper vers une autre technologie. J'ai pris une pause de quelques jours et j'ai commencé à étudier quelles autres solutions open source existent dans le monde. Il s'est avéré qu'il y en a essentiellement deux:


Si le premier est écrit en Java et charge le graphe routier dans la RAM, alors OSRM - Open Source Routing Machine - est déjà écrit en plus et conserve ses fichiers intermédiaires (non moins monstrueux) sur le disque. Ainsi, la nécessité d'avoir une énorme quantité de RAM a été remplacée par l'exigence d'un disque grand et rapide. C'est plus réel.

Ligne d'arrivée


Après quelques nuits de sélection dans la documentation, tout le code du serveur a été transféré vers une nouvelle solution. Cela a vraiment fonctionné et a plutôt bien fonctionné. Il a été possible de connecter plusieurs pays, et même la vitesse de recherche a augmenté. Les principes généraux étaient les mêmes: à partir de l'extrait OpenStreetMaps, des fichiers intermédiaires ont été compilés pour le profil «machine» (un profil est un ensemble de poids et d'instructions pour les bords d'un graphique - il y a des profils «à pied», «vélo», etc.). Ensuite, ces fichiers ont été placés dans un répertoire, et l'API OSRM les a déjà lus à partir du disque. Api, soit dit en passant, s'est avéré être assez grand - les contours et la planification de l'itinéraire avec diverses nuances étaient pris en charge, il y avait même une génération de tuiles pour la carte. J'ai décidé de m'attarder sur ce dernier plus en détail.

En revenant à l'application et en continuant à la tester, j'ai réalisé quelques autres choses:

  • le menu en haut n'est pas bon, va bien loin
  • la carte générale n'est certainement pas nécessaire, elle ne me lie qu'à google
  • les cartes de résultat sont ennuyeuses et monotones

Il a heureusement jeté une carte Google (hourra, désormais 100% open source et ses données), simplifié le menu, baissé. J'ai commencé à penser quoi faire avec les cartes. Et puis les tuiles api sont apparues très opportunément, ce que j'ai mentionné ci-dessus. Il vous permet de générer une vignette vectorielle pour les coordonnées et le niveau de zoom donnés. Le résultat est émis sous la forme d'un blob binaire de type application / x-protobuf - un type de données assez peu pratique pour la manipulation. Je n'entrerai pas dans les détails (j'ai dû transpirer un peu), mais en un mot mes actions ressemblaient à ceci:

  1. Prendre la ligne de l'itinéraire construit jusqu'au point sous forme de polyligne
  2. Polyligne -> GeoJSON
  3. Obtenez la boîte englobante de cette forme
  4. Demander toutes les tuiles capturées par cette boîte englobante
  5. Convertir les données de tuiles du format binaire en GeoJSON
  6. Coller les carreaux, couper par la boîte englobante, combiner avec la ligne de route, coloriser
  7. Le GeoJSON résultant est converti en bitmap

Au cours de l'action, il y avait différentes nuances, par exemple, indenter le cadre de sélection ou marquer des points avec des anneaux colorés (et rendre leur rayon constant pour tous les niveaux de zoom). L'image résultante ressemblait à ceci:



Touche finale


Lorsque j'ai attaché un itinéraire visuel à chaque résultat, la liste a commencé à briller de nouvelles couleurs. De plus, réalisant que chaque image, par défaut, chevauche le nord, je les ai fait tourner par rapport à la boussole. Ainsi, en plus de l'effet visuel, cette puce est également devenue fonctionnelle - remplaçant la flèche de direction. Maintenant que vous conduisez, vous pouvez voir avec certitude de quel côté de vous tel ou tel résultat est.

Le troisième mois de développement a expiré et il fallait déjà terminer. Plus vous en ajoutez, plus vous en voulez, donc à un moment donné, il vous suffit de vous ressaisir et de lâcher le projet. J'ai peaufiné et peint un peu plus l'interface, et pour un sentiment d'achèvement, j'ai esquissé le logo de l'application:



et page d'introduction:



Et enfin, la version finale de l'application:









Résumé


Eh bien, merci d'avoir regardé. J'espère que ce courant de conscience sera intéressant pour quelqu'un, et peut-être même utile. A ce stade, je pense que l'application est prête: elle est rapide, sans bugs particuliers et peut fonctionner dans n'importe quel pays du monde. À propos, vous avez peut-être remarqué que les captures d'écran provenaient à la fois de l'iPhone et d'Android, car grâce à Flutter, l'application fonctionne exactement de la même manière sur les deux plates-formes.

Néanmoins, jusqu'à présent, j'ai décidé de tout geler - j'ai changé d'emploi, de nouvelles inquiétudes sont apparues. Après quelques mois, j'ai dépoussiéré la poussière et décidé d'écrire une rétrospective. Votre avis est intéressant: l'aimeriez-vous, l'utiliser, ce qui peut être changé.

PS Bien sûr, la préparation de l'application est un non-sens. Il est prêt en tant que prototype - si vous approchez d'une production sérieuse, vous devez créer des scripts pour synchroniser les données avec OpenStreetMaps, vérifier le fonctionnement au zoo des appareils, localiser les interfaces, etc. Le même backend sur le nœud et python tombera sous n'importe quelle charge sérieuse.

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


All Articles