Cómo intenté arreglar un mapa de búsqueda de controladores

Esta es una historia sobre cómo intenté resolver un problema extraño que me lo impidió. Mirando hacia el futuro, diré que estoy satisfecho con la solución resultante y llevé la aplicación a su final lógico. Sin embargo, para ejecutarlo completamente, necesita más recursos, así que decidí tomar un descanso y preguntarle a la gente si alguien más lo necesitaba. Para este propósito (y también solo para hablar) escribo aquí.

Dos palabras sobre mí: vivo en Dublín, Irlanda, trabajo como programador. No se sienta exactamente en el lugar, por eso en mi tiempo libre en casa vi varios proyectos, principalmente en la mesa. Escribo sobre Habré por primera vez, aunque leí durante muchos años.

El problema


Hace mucho tiempo, cuando tenía que viajar mucho a lugares de trabajo desconocidos, comencé a notar que la búsqueda estándar en cualquier tarjeta no es absolutamente aplicable a los conductores de hoy. Mire: está conduciendo en un área desconocida, y tiene una flecha de gasolina en cero. Tus acciones Si en ese momento no estoy solo en el automóvil, entonces le digo al pasajero: "Bueno, busque una estación de servicio cerca mientras conduzco". Porque si lo hace usted mismo, debe realizar las siguientes acciones:

  1. Para parar
  2. En la aplicación de mapas, ingrese "gasolina" en la búsqueda (o haga clic en uno de los botones rápidos que ahora ofrecen algunas tarjetas)
  3. La aplicación realiza una búsqueda y le muestra un mapa enorme con una docena de estaciones de servicio
  4. Intenta averiguar cuál está más cerca de usted y hace clic en él para construir una ruta

En mi opinión, una pesadilla. En primer lugar, debe detenerse o al menos esperar un semáforo. Porque las tarjetas son complejas y los iconos son pequeños. En segundo lugar, la tarjeta nifig no te dice cuál es la estación de servicio más cercana. En este sentido, Google es lo peor de todo: incluso en los resultados en forma de lista, obstinadamente no empuja hacia arriba el lugar más cercano, y el más calificado / con fotos / pagado / no tengo idea.



Bueno, el tercer factor: nos estamos moviendo. El resultado de que las tarjetas emitidas fue relevante en algún momento, pero ya estamos lejos de eso. ¿Notó que incluso la ruta establecida por Google no se actualiza automáticamente si hemos cambiado? Solo si la navegación ya se ha iniciado, se reconstruirá.

En general, el problema es claro. La lógica que funciona para los peatones y sus necesidades, para los conductores, no se trata de nada. No me importa la clasificación del lugar y qué tipo de cocina allí: necesito, sin distraerme de la carretera, en tiempo real obtener una ruta a la estación de servicio más cercana, carga, estacionamiento, cajero automático, etc.

Idea


Ahora intentemos determinar el escenario de búsqueda ideal. El criterio es el siguiente:

  • la interacción es corta y clara para no distraer al conductor
  • salida transparente basada en la distancia
  • actualización en tiempo real

Lo primero que se pide es reemplazar la búsqueda estándar de una sola vez con un escaneo. Es decir, la cadena de acciones es algo como esto:

  1. Lanzó un escaneo
  2. Montar, mirando resultados actualizados en tiempo real
  3. Cuando me gustó algo, hice clic y allané la ruta

Usted determina los criterios de búsqueda de antemano; de hecho, este es el tipo de lugar y el radio de la exploración. Además, mientras el automóvil se mueve, la aplicación funciona como un radar. Rápidamente se hizo evidente que, en primer lugar, el radio no es redondo, sino en forma de una isolina. En segundo lugar, debe construirse no por distancia, sino por tiempo, porque los minutos son mucho más fáciles de percibir que los kilómetros.

Entonces pensé en una forma de generar los resultados. Más precisamente, ¿necesito una tarjeta? El conductor mira la aplicación con un ojo e interactúa con un dedo; no necesita un mapa, sino un texto grande y botones grandes. Por lo tanto, decidí inmediatamente que la pantalla principal sería una lista, y podría agregar un mapa al principio y luego ver si debía dejarlo.

Plan


Conociendo mi propia peculiaridad de los proyectos de estiramiento, decidí darme 2 meses para todo, al final me encontré en 3. En principio, la aplicación es bastante simple:

  1. Cliente desde un par de pantallas (búsqueda, lista y mapa)
  2. Envía periódicamente al servidor sus coordenadas, radio de búsqueda y tipo de lugares.
  3. El servidor construye una isolínea a tiempo (por cierto, tiene su nombre en inglés - isochrone), realiza una búsqueda en lugares y devuelve una lista

Suena como un pulmón más ligero. Ya tenía experiencia previa en cartografía (hace un par de años hice un portal de bienes raíces donde la búsqueda estaba en el mapa), por lo que la pila en el backend quedó clara de inmediato:

  • importar datos de OpenStreetMaps a Elasticsearch
  • OpenTripPlanner para construir contornos

En el cliente, pensé, decidí usar el nuevo marco de Google: Flutter. Es multiplataforma, bastante flexible y le permite crear aplicaciones completas con un mínimo de código. Por supuesto, es crudo y no está claro qué está en producción, pero se ve perfecto para la creación de prototipos. Debe aclararse que en este punto tenía experiencia en desarrollo nativo para Android (era un líder de equipo) y decidí, por así decirlo, enfrentar al enemigo. El enemigo no daba tanto miedo.

Implementación


La primera aplicación prototipo estuvo lista muy rápidamente: Flutter tiene un umbral de entrada bajo y una filosofía comprensible de tipo redux. Por extraño que parezca, la descripción declarativa de las interfaces también fue agradable, así como el reinicio en caliente (React Native, su mapa de bits). En general, la impresión era que Google representaba la mayoría de las enfermedades congénitas de intentos anteriores. Sin embargo, entiendo a las personas que pueden no querer entrar en esto: a alguien no le gusta el dardo, un número limitado de widgets y la "depuración visual" que se ofrece aquí es algo muy crudo.

En el backend, hice lo siguiente:

  1. Entregó Nominatim, cargó el extracto de datos de OpenStreetMaps (tomado aquí ) en su base de datos, utilizando su utilidad estándar osm2pgsql. ¿Por qué recurrí al pequeño pero muy agradable geocodificador agrio abierto Photon ? Anteriormente, ya lo usé en un par de proyectos: genera un índice Elasticsearch, importa datos de la base de datos de Nominatim allí y busca este índice. Me gusta con velocidad y mapeo puro (por ejemplo, probé Pelias y me gustó menos). Su principal problema es la versión anterior del elástico, pero en mi caso no necesitaba la funcionalidad del geocodificador, solo los datos, así que después de importar, transfirí el índice a la instalación del elástico de la última versión con alma pura. Por cierto, ¿por qué elegí Elasticsearch? Es muy rápido y tiene la función de encontrar coordenadas por polígono.
  2. El vertedero, también conocido como isocrona, inicialmente generó OpenTripPlanner para mí. Este es un buen planificador de rutas de código abierto. Funciona de la siguiente manera: toma el mismo extracto de OpenStreetMaps y lo compila en un gran gráfico de carreteras, que, como un objeto separado, se guarda en el disco. Cuando se inicia el servidor, este gráfico se carga en la RAM y se buscan en todas las rutas. Pros: recuperación rápida, funcionalidad rica (por ejemplo, genera contornos desde la caja) y buena velocidad. Contras: esta velocidad depende de la cantidad de RAM, y la documentación es extremadamente desagradable. Solo documentación monstruosa. Recuerdos vietnamitas.
  3. Lancé una pequeña API en la pitón, que toma el tipo de lugar y el radio de búsqueda en segundos, solicita un polígono a OpenTripPlanner, luego lo busca en Elasticsearch. Solicita una ruta a cada ubicación encontrada (nuevamente desde OpenTripPlanner), toma su tiempo y duración. Después de eso, todos los datos recopilados están bellamente empaquetados y devueltos.

Realicé la actualización de los resultados cambiando las coordenadas del dispositivo en 5 metros. El mapa es estático: acabo de usar una API de mapas estáticos de Google (como puede ver, este es el único lugar donde la corporación, sin embargo, se metió en nuestro acogedor mundo abierto). La primera implementación se veía así:







Después de jugar con la aplicación, decidí ocultar el mapa. Ella hizo un buen trabajo al comprender para qué polígono se hizo la búsqueda, y se veía divertida: fue interesante ver cómo este pulpo cambia de forma en tiempo real. Sin embargo, este entretenimiento no ayudó a la aplicación a cumplir sus funciones y ocupó un tercio de la pantalla.

También se me ocurrió agregar una flecha, que indicaba la dirección de cada resultado. Funcionó así:

  1. Recuerda tus coordenadas anteriores
  2. Al cambiar, establecemos la ruta desde la posición anterior a la actual
  3. Tomamos el último segmento de nuestra ruta y comparamos con el primer segmento de la ruta cada resultado. Dado que se colocan a lo largo de la misma cuadrícula de carreteras, con un 99% de probabilidad de que el ángulo entre ellos sea cercano a 0 o 180.

Este truco muy simple hace que sea muy fácil entender si ya nos dirigimos hacia un lugar o si tendremos que dar la vuelta.



En este punto, estaba bastante satisfecho con la aplicación resultante y decidí intentar implementarla en varios países. Aún así, Irlanda es un estado muy pequeño, respectivamente, y el índice elástico y el gráfico de carreteras fueron pequeños. Para las pruebas, decidí conectar el vecino Reino Unido. Es aproximadamente 4 veces más grande y tiene una red de carreteras mucho más densa (especialmente la capital y las grandes ciudades). Y entonces surgió un problema.

Se espera que Elasticsearch haya digerido bastante bien el aumento del índice, pero con OpenTripPlanner hubo una falla completa. Está escrito en Java y, como dije anteriormente, genera un gráfico de carreteras, de modo que después de cargarlo en la RAM. El gráfico para Irlanda era de 1 gigabyte, para el Reino Unido ya era de 5. Era posible, por supuesto, dividirlo en países, regiones e incluso regiones, y luego redirigirlo al gráfico deseado según las coordenadas del usuario. Sin embargo, esto hizo imposible establecer rutas entre regiones y, lo más importante, no resolvió la necesidad de mantener todos estos gráficos en la memoria. Finalmente, simplemente compilar cada uno de esos objetos tomó MUCHOS recursos y duró para siempre. Para divertirme, lancé en mi máquina (marcos de 16 GB) el ensamblaje del Conde de Francia, esperé un día y lo cancelé.

Obviamente, una tecnología que ha demostrado su eficacia en tareas pequeñas no está diseñada para escalar en absoluto (al menos no con mis recursos). Por lo tanto, debe admitir la derrota o arrastrarse a otra tecnología. Me tomé un descanso por un par de días y comencé a estudiar qué otras soluciones de código abierto existen en el mundo. Resultó que básicamente hay dos de ellos:


Si el primero está escrito en Java y carga el gráfico de ruta en la RAM, entonces OSRM (Open Source Routing Machine) ya está escrito en más y mantiene sus archivos intermedios (no menos monstruosos) en el disco. Por lo tanto, la necesidad de tener una gran cantidad de RAM fue reemplazada por el requisito de un disco grande y rápido. Esto es mas real.

Línea de meta


Después de un par de noches de recoger la documentación, todo el código del servidor se transfirió a una nueva solución. Realmente funcionó, y funcionó bastante bien. Fue posible conectar varios países, e incluso la velocidad de búsqueda aumentó. Los principios generales fueron los mismos: a partir del extracto de OpenStreetMaps, se compilaron archivos intermedios para el perfil de "máquina" (un perfil es un conjunto de pesos e instrucciones para los bordes de un gráfico; hay perfiles "a pie", "bicicleta", etc.). Luego, estos archivos se colocaron en un directorio, y la API OSRM ya los leyó del disco. Api, por cierto, resultó ser bastante grande: se admitieron los contornos y la planificación de rutas con varios matices, incluso hubo generación de mosaicos para el mapa. Decidí detenerme en esto último con más detalle.

Volviendo a la aplicación y continuando probándola, me di cuenta de un par de cosas más:

  • el menú de arriba no es bueno, llega lejos
  • el mapa general definitivamente no es necesario, solo me vincula a google
  • las tarjetas de resultados son aburridas y monótonas

Felizmente tiró un mapa de Google (hurra, ahora 100% de código abierto y sus datos), simplificó el menú, bajó. Comenzó a pensar qué hacer con las tarjetas. Y luego las fichas API aparecieron muy oportunamente, lo que mencioné anteriormente. Le permite generar un mosaico de vectores para las coordenadas y el nivel de zoom dados. El resultado se emite en forma de un blob binario de tipo application / x-protobuf, un tipo de datos que es bastante inconveniente para la manipulación. No entraré en detalles (tuve que sudar un poco), pero en pocas palabras mis acciones se veían así:

  1. Tome la línea de la ruta construida hasta el punto en forma de polilínea
  2. Polilínea -> GeoJSON
  3. Obtén el cuadro delimitador de esta forma
  4. Solicite todas las fichas capturadas por este cuadro delimitador
  5. Convierta datos de mosaico de formato binario a GeoJSON
  6. Pegue las baldosas, recorte por el cuadro delimitador, combine con la línea de ruta, coloree
  7. El GeoJSON resultante se convierte en un mapa de bits

En el curso de la acción, hubo diferentes matices, por ejemplo, sangrar el cuadro delimitador o marcar puntos con anillos de colores (y hacer que su radio sea constante para todos los niveles de zoom). La imagen resultante se veía así:



Toques finales


Cuando adjunté una ruta visual a cada resultado, la lista comenzó a brillar con nuevos colores. Además, al darme cuenta de que cada imagen, por defecto, está montando a horcajadas en el norte, las hice girar en relación con la brújula. Por lo tanto, además del efecto visual, este chip también se ha vuelto funcional, reemplazando la flecha de dirección. Ahora que está conduciendo, puede ver con seguridad de qué lado de usted está este o aquel resultado.

El tercer mes de desarrollo expiró, y ya era necesario completarlo. Cuanto más agregue, más querrá, por lo que en algún momento solo tendrá que recuperarse y abandonar el proyecto. Ajusté y pinté la interfaz un poco más, y para una sensación de finalización dibujé el logotipo de la aplicación:



y página de introducción:



Y finalmente, la versión final de la aplicación:









Resumen


Bueno, gracias por mirar. Espero que esta corriente de conciencia sea interesante para alguien, y tal vez incluso útil. En esta etapa, creo que la aplicación está lista: es rápida, sin errores especiales y puede funcionar en cualquier país del mundo. Por cierto, es posible que hayas notado que las capturas de pantalla eran tanto de iPhone como de Android, porque gracias a Flutter, la aplicación funciona exactamente igual en ambas plataformas.

Sin embargo, hasta ahora he decidido congelar todo: cambié mi trabajo, aparecieron nuevas preocupaciones. Después de un par de meses, desempolvé el polvo y decidí escribir una retrospectiva. Su opinión es interesante: ¿le gustaría, úsela, qué se puede cambiar?

PD Por supuesto, la disponibilidad de la aplicación no tiene sentido. Está listo como prototipo: si te acercas a una producción seria, debes crear scripts para sincronizar datos con OpenStreetMaps, verificar el funcionamiento en el zoológico de dispositivos, localizar interfaces, etc. El mismo backend en el nodo y python caerá bajo cualquier carga seria.

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


All Articles