
En un artículo anterior, hablé sobre cómo hacer rápidamente un marcador web. Pero, ¿qué sucede si establece una tarea más ambiciosa: ensamblar su propia aplicación con una tarjeta, sin anuncios y con blackjack? ¿Y si en solo un par de días?
¡Hagámoslo! Pido gato.
Primero, descubramos qué tenemos que hacer. En la salida, queremos obtener una aplicación con datos de referencia y un mapa. Y para trabajar sin conexión. Como desarrollador, me interesa principalmente solo el mapa, porque ya sabemos cómo mostrar datos de referencia. Y fuera de línea es una limitación bastante fuerte en este caso, porque no hay muchas bibliotecas buenas con soporte fuera de línea. Por lo tanto, en el artículo nos concentraremos en el mapa, pero hablemos de la guía de pasada.
Elige un motor de mapas
Lo primero que debe hacer es obtener los datos para la aplicación. Existen muchas fuentes en el mercado, gratuitas y no muy numerosas. Para empezar, OpenStreetMap es bastante adecuado para nosotros como fuente abierta de datos de mapas. Allí puede tomar una cierta cantidad de PDI para nuestro directorio.
El siguiente paso es elegir un motor de tarjeta. En Internet, hay bastantes de ellos, incluso menos gratuitos, y con el soporte fuera de línea generalmente hay solo unos pocos. Sugiero usar una opción genial: mapsforge / vtm . ¡Este es un motor vectorial OpenGL, muy rápido, compatible con fuera de línea, Android, iOS, varias fuentes de datos, estilos personalizados, superposiciones, marcadores, modelos 3D e incluso 3D de objetos! Muy, muy guay.
El repositorio tiene muchos ejemplos de inicio rápido, hay mapas listos para usar, hay un complemento que le permite ensamblar su propio mapa a partir de datos en formato OSM. Entonces, ¡comencemos!
MapView mapView = findViewById(R.id.map_view); this.map = mapView.map(); File baseMapFile = getMapFile("cyprus.map"); MapFileTileSource tileSource = new MapFileTileSource(); tileSource.setMapFile(baseMapFile.getAbsolutePath()); VectorTileLayer layer = this.map.setBaseMap(tileSource); MapInfo info = tileSource.getMapInfo(); if (info != null) { MapPosition pos = new MapPosition(); pos.setByBoundingBox(info.boundingBox, Tile.SIZE * 4, Tile.SIZE * 4); this.map.setMapPosition(pos); } this.map.setTheme(VtmThemes.DEFAULT); this.map.layers().add(new BuildingLayer(this.map, layer)); this.map.layers().add(new LabelLayer(this.map, layer));
Cree una fuente de datos MapFileTileSource, especifique la ubicación del archivo de mapa. Además, nos posicionamos en el centro del cuadro delimitador que nos interesa, para no estar en algún lugar fuera de la ubicación seleccionada cuando se inicia la aplicación. Instala el tema predeterminado. Agregue una capa de casas y una capa de firmas. Eso es todo Lanzamos - milagros!
Parece más rápido y más fácil y no podría ser.
Hacemos geocodificación
El siguiente paso importante es implementar la geocodificación. La tarjeta en sí ya es buena, pero se necesita interactividad. Queremos aprovechar el mapa y ver información sobre el objeto que golpeamos. Y hay alguna dificultad. En general, no existe una geocodificación completa en nuestra biblioteca. Este es quizás su mayor inconveniente. Si no se inventa nada, podemos aprovechar la funcionalidad existente.
Resultó relativamente detallado. Debe encontrar un mosaico, obtener formas (en la terminología de OSM, este es un objeto lineal) y puede extraer algunos atributos de ellos. Además de las formas, es posible obtener un PDI, pero eso es todo. Tendrá que terminar el resto de la lógica usted mismo: elija el "correcto" de todo el conjunto de objetos que golpeó el clic, filtre por nivel de zoom. Y una cosa más. De hecho, perdemos información sobre la geometría original y obtenemos en respuesta a una búsqueda solo un conjunto de líneas. Si también desea hacer un editor geográfico, obviamente esto no será suficiente.
Pero para demostrar el enfoque, todo nos conviene.
Geocodificación avanzada
En términos generales, hay una opción más avanzada. Para esto necesitamos nuestra propia base. En particular, puede usar SQLite. Es cierto que SQLite estándar no será suficiente para nosotros, y tendremos que construir el nuestro conectando el complemento RTree para la búsqueda geográfica. Cómo hacer esto, ya lo dije en el artículo , la sección "Haz una buena búsqueda".
En este caso, tenemos un control total sobre los datos, podemos guardar todo lo que se requiere y en el formato correcto. También podemos ajustar la búsqueda de texto completo y buscar nuestros objetos geográficos y empresas por nombre, dirección y otros atributos.
La direccion es:
- Hacemos tablas:
- Llenamos todo con datos.
- Cuando toca el mapa, obtenemos GeoPoint y ejecutamos la solicitud:
SELECT id FROM geo_index WHERE minX>=-81.08 AND maxX<=-80.58 AND minY>=35.00 AND maxY<=35.44
- Último paso: filtrar y seleccionar el objeto apropiado.
Una de las opciones de implementación se puede ver en el repositorio .
Como resultado, ya sabemos cómo mostrar el mapa y manejar los clics. No esta mal.
Agrega pequeñas cosas importantes.
Agreguemos un par de características importantes.
Comencemos con la ubicación actual. En mapsforge / vtm solo hay un especial para esto. Ubicación Capa de capa. El uso es extremadamente simple.
LocationLayer locationLayer = new LocationLayer(this.map); locationLayer.setEnabled(true);
Solo hay un inconveniente: es la ondulación constante del "punto azul" en el borde de la pantalla cuando la ubicación actual está fuera del mapa. Lo más probable es que, en el proceso de uso, rara vez se encuentre en una situación así, pero esto provoca una representación constante, por lo tanto, carga un poco el procesador. Para deshacerse de esto es un poco más difícil, debe ingresar al sombreador y solucionarlo. Pero esto ya es para los perfeccionistas. Cómo hacerlo, puedes ver aquí .
Entonces, la posición es. Es hora de agregar el botón de navegación a la posición actual, como en todas las aplicaciones de mapeo que se respetan.
View vLocation = findViewById(R.id.am_location); vLocation.setOnClickListener(v -> this.map.animator().animateTo(initialGeoPoint));
También necesitamos los botones de zoom.
View vZoomIn = findViewById(R.id.am_zoom_in); vZoomIn.setOnClickListener(v -> this.map.animator().animateZoom(500, 2, 0, 0)); View vZoomOut = findViewById(R.id.am_zoom_out); vZoomOut.setOnClickListener(v -> this.map.animator().animateZoom(500, 0.5, 0, 0));
Y la guinda del pastel es una brújula.
View vCompass = findViewById(R.id.am_compass); vCompass.setVisibility(View.GONE); vCompass.setOnClickListener(v -> { MapPosition mapPosition = this.map.getMapPosition(); mapPosition.setBearing(0); this.map.animator().animateTo(500, mapPosition); vCompass.animate().setListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { vCompass.setVisibility(View.GONE); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }).setDuration(500).rotation(0).start(); }); this.map.events.bind((e, mapPosition) -> { if (e == Map.ROTATE_EVENT) { vCompass.setRotation(mapPosition.getBearing()); vCompass.setVisibility(View.VISIBLE); } });
Capturando el mundo
Amigos, estamos en la línea de meta. Queda por agregar el toque final. Estamos planeando capturar el mundo, lo que significa que necesitamos incluirlo de alguna manera en nuestra aplicación.
Y las cosas son tales que con nuestro motor es mucho más fácil de lo que parece.
Necesitamos modificar ligeramente el método de carga del mapa agregándole un MultyMapTileSource. Esto es esencialmente un contenedor para cualquier otra fuente de mosaico, lo que le permite mostrar todo lo que se le agrega en el mapa a la vez. Solo una característica asesina. Como resultado, nos queda por preparar un mapa del mundo con detalles mínimos, agregarlo primero a nuestro contenedor y dibujar el resto en la parte superior. ¡Además, podemos agregar inmediatamente todas las tarjetas que tenemos en el catálogo con mapas de la aplicación! Preciosa, simplemente preciosa. Y no olvides que está fuera de línea :)
Quizás estamos listos para el lanzamiento. Recopilamos la compilación, la ponemos en el mercado y obtenemos las estrellas merecidas :)
Un par de cucharas de alquitrán en un enorme barril de miel.
El motor de código abierto se está desarrollando activamente, pero su equipo, francamente, es bastante modesto. En general, esta es una persona bajo el nombre devemux86 . Y un par de chicos más de vez en cuando.
A veces hay artefactos en el renderizado, algunos parpadeos y contracciones. Pero nunca me he encontrado con ningún problema crítico y más accidentes, que no pueden dejar de alegrarme.
Hay un matiz más que puede no ser agradable. Este es un dibujo de filetes y círculos. Un ejemplo de cómo se ve esto en la captura de pantalla:
Si en la geometría inicial hay muchos puntos (el redondeo es suave), entonces en el mapa se puede ver un círculo bastante "angular" con muchas pequeñas protuberancias y concavidades. Obviamente, esto se hace por el rendimiento y el tamaño del archivo de mapa, pero no se ve muy bien.
Quizás esto es todo lo que hay en contra por hoy. Tú decides si puedes vivir con ellos o no. Mientras tanto, hemos estado usando esta biblioteca durante más de 1,5 años, el vuelo es excelente, al menos en Android.
Resumen
En este artículo, he demostrado que incluso un problema tan poco trivial puede resolverse con relativa rapidez. Tienes un esqueleto listo para usar, con el que puedes crear un prototipo de cualquier proyecto que implique el uso de un mapa sin conexión en un tiempo mínimo.
Si hay interés, en el próximo artículo mostraré cómo hacer pisos a la 2GIS. Y esto es en realidad mucho más simple de lo que parece :)