
En un
artículo anterior, hablé sobre cómo hacer una aplicación móvil con un mapa. En la continuación de la serie "sobre la rodilla", compartiré con ustedes las herramientas para implementar los planos de planta.
La declaración inicial del problema en una forma simplificada: quiero poder visualizar el plano de planta en su aplicación móvil y poder mostrar la ubicación de una organización específica en él. También me gustaría ver la ubicación del usuario, pero aquí el problema está en el plano técnico: necesita un equipo que le permita obtener las coordenadas del dispositivo en interiores. Así que dejamos este aspecto fuera del alcance del artículo y nos enfocamos en la parte del software.
A continuación, le mostraré varias opciones con las que puede implementar los requisitos descritos anteriormente. Todo depende de qué datos tiene y qué debe poder hacer exactamente la aplicación. Y comenzaremos con lo más simple.
La primera opción API lista con datos
La primera opción que consideraremos es el uso de un widget listo para usar de 2GIS. La descripción de la API se puede ver en
api.2gis.ru. Le conviene si el edificio que le interesa ya se presenta en 2GIS, y los pisos ya se han dibujado en el edificio. Es decir, en términos de datos, todo ya se ha hecho. Y lo más importante, está listo para conectarse en línea, ya que esta opción funcionará exclusivamente con Internet.
Para mostrar los pisos en este caso, prácticamente no se requiere nada de usted. Implementación de la ejecución como un widget web, que solo necesita poner en WebView.
<WebView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/am_webview" android:layout_marginTop="160dp" />
Este es el contenedor en el que colocaremos nuestro widget. Lo inicializamos de la siguiente manera:
webView = findViewById(R.id.am_webview) webView.settings.javaScriptEnabled = true webView.settings.useWideViewPort = true webView.settings.loadWithOverviewMode = true
Configuramos el propio webView, asegúrese de habilitar JavaScript, javaScriptEnabled = true, ya que interactuaremos con el widget a través de él. Si es necesario, puede activar las barras de desplazamiento y los botones de zoom (vea el código comentado), pero no funciona muy bien, por lo que no lo recomiendo.
Lo más importante es cargar HTML con nuestro widget
webView.loadUrl ("file: ///android_asset/map_api.html") y suscribirse a eventos, si es necesario. En el ejemplo anterior, después de cargar el mapa, llamamos al método
initMap definido en map_api.html, pasando el identificador del edificio para el que queremos mostrar los pisos.
HTML es un código bastante simple. Se
llama al método
DG.FloorsWidget.init , en el que se pasa el objeto json que contiene los datos para inicializar. Como contenedor, especifique la identificación con la que hemos declarado un div en el marcado HTML anterior. Ajusta el ancho, la altura. Y en
initData pasamos el edificio en la etiqueta
complexId y parámetros de visualización de widgets adicionales, que se pueden encontrar en la documentación de la API. Por cierto, el identificador se puede ver en respuesta a la consulta de búsqueda, que 2GIS envía cuando hace clic en el edificio que le interesa en
2gis.ru. En mi ejemplo, usé el Dubai Mall. Pero nadie se molesta en indicar ningún otro edificio con pisos.
El toque final. Para pasar a una compañía específica, llame al método showFirm, pasando el identificador de la compañía
webView.loadUrl("javascript:showFirm('$firmId')")
Es muy simple Se puede ver un ejemplo de implementación listo para usar en
Github .

Ventajas de la opción considerada:
- datos listos con planos de planta y datos de la empresa;
- implementación ligera ya hecha en webview;
- Búsqueda lista según 2GIS.
Contras:
- Internet requerido;
- se requieren conocimientos básicos de HTML y JavaScript;
- solo datos de 2GIS y solo aquellos edificios que ya tienen pisos.
La segunda opción. Plano de planta como una imagen
Si la opción con datos listos y API no es adecuada para usted, puede usar lo siguiente.
En este caso, necesitará un plano de planta en forma de imagen, por ejemplo, jpeg o png. Si se requiere interactividad del tipo "metido en la imagen - abrió la tarjeta de objeto", entonces también será necesaria la geocodificación, que deberá implementarse de forma independiente. Si ajusta a las coordenadas globales, la imagen debe ajustarse correctamente y una de las esquinas debe desplazarse a las coordenadas reales para que pueda calcular los desplazamientos a partir de ella. No nos detendremos en este tema en detalle, espero que todo esté claro aquí.
Lo más importante es mostrar esta imagen en la aplicación. Y para esto, la biblioteca
TileView es ideal para nosotros. De hecho, esta biblioteca permite, en principio, mostrar mosaicos de mapas. Pero también es adecuado para nosotros, ya que proporciona la capacidad de moverse y hacer zoom, centrarse en una posición específica, mostrar marcadores, transformar las coordenadas y la capacidad de suscribirse a varios eventos.
Para que la biblioteca funcione de la manera más eficiente posible, la imagen original debe prepararse cortándola en mosaicos. Hay una
instrucción bastante simple
para esto . Recomiendo un script al final de la página especificada que creará 4 mosaicos. Agregamos el resultado obtenido a los activos y mostramos nuestra imagen en la actividad con un código simple:
tileView.setSize(floorWidth, floorHeight) tileView.setShouldRenderWhilePanning(true) tileView.addDetailLevel(1f, "tiles/floors1/1000/%d_%d.jpg") tileView.addDetailLevel(0.500f, "tiles/floors1/500/%d_%d.jpg") tileView.addDetailLevel(0.250f, "tiles/floors1/250/%d_%d.jpg") tileView.addDetailLevel(0.125f, "tiles/floors1/125/%d_%d.jpg") tileView.defineBounds(0.0, 0.0, floorWidth.toDouble(), floorHeight.toDouble()) tileView.setScaleLimits(0f, 5f) tileView.setMinimumScaleMode(ZoomPanLayout.MinimumScaleMode.FIT)
Todo, la pantalla está lista. Puede suscribirse a eventos y agregar la lógica necesaria. Por ejemplo, un marcador se puede mostrar así:
tileView.setMarkerAnchorPoints(-0.5f, -0.5f) tileView.addMarker(imageView, x, floorHeight - y, null, null)
Un ejemplo completo está disponible en
Github .

Pros:
- la capacidad de hacer completamente fuera de línea;
- preparación de datos relativamente simple, plano de planta en forma de imagen.
Contras:
La tercera opción. Datos vectoriales
Esta opción es la más avanzada y la más difícil. Se supone que ha preparado datos vectoriales, es decir, pisos completamente dibujados en el vector. Necesitarás varios tipos de objetos. Áreas de organizaciones, estacionamientos, patios de comidas, escenarios, pistas de patinaje, etc. Objetos lineales: principalmente paredes, direcciones de flujo. Objetos puntuales: entradas / salidas, ascensores, cajeros automáticos y similares.
Así es como se ve un plano de planta en Fiji, sistema interno 2GIS:

Bueno, para su visualización, el motor de vectores, del que hablé en el
artículo anterior , mapsforge-vtm es adecuado para nosotros.
Para demostrar el enfoque, preparé datos de prueba: un conjunto de cuadrados y líneas para varios pisos usando el ejemplo de un edificio en la soleada isla de Chipre. Para la preparación, tomé la geometría original del edificio y la corté en piezas correspondientes a los componentes de la geometría, solo por simplicidad. Como saben, la parte más difícil es la preparación de datos de calidad. El resto es cuestión de tecnología. Necesitará botones para cambiar pisos, estilos preparados para representar diferentes cuadrados y líneas, y una superposición para representarlos.
Vea el código completo
aquí .
La clase
FloorData contiene el código de prueba de geodatos para nuestros pisos, y la clase
FloorsManager está diseñada para representarlos.
En el constructor, definimos estilos para cuadrados y muros:
styles.put(ObjectType.Floor, org.oscim.layers.vector.geometries.Style.builder() .fillColor(Color.GRAY) .build());
Y en el método
drawFloor , determinamos la lógica de agregar objetos a capas en el mapa:
public void drawFloor(int floorId) { hideFloors(); indoorLayer = new CustomVectorLayer(this.map); List<GeoData> geoObjects = this.floorData.getFloorData(floorId); for (GeoData geo: geoObjects) { indoorLayer.add(geo.getGeometry(), styles.get(geo.getObjectType())); } this.map.layers().add(indoorLayer); indoorLayer.update(); }
Todo es elemental aquí. Cree una nueva capa
indoorLayer , agregue datos de piso previamente preparados con los estilos necesarios y agregue la capa al mapa
this.map.layers (). Add (indoorLayer) .
Queda por agregar botones para cambiar de piso. Para hacer esto, hay un
FloorPickerControl basado en
RecyclerView , que hace justo lo que necesita. No perdamos el tiempo, vea la fuente.
Y aquí está el Dubai Mall en nuestra aplicación. También implementó la edición de geo objetos.

Pros:
- de nuevo, la capacidad de hacer completamente fuera de línea;
- imagen de alta calidad;
- la posibilidad de una variedad de estilización dinámica;
- La capacidad de implementar un editor de datos.
Contras:
- preparación de datos complejos
Al final del artículo, quiero decir que la tarea de mostrar planos en la aplicación no es tan terrible como podría parecer a primera vista. Tiene varias opciones con sus ventajas y desventajas, entre las cuales puede elegir la más adecuada para resolver su problema.
Todas las referencias en un solo lugar:
Artículo sobre el mapa en el móvilAPI 2GISEjemplo de API 2GISBiblioteca TileViewEjemplo de TileViewUn ejemplo en mapsforge-vtm