.. highlight:: js ***************** Creación de mapas ***************** Bajo este epígrafe se hará una exposición didáctica de cómo aprovechar las ventajas de la librería :file:`leafext.js` para construir mapas. De hecho, el *mapa de adjudicaciones y oferta educativa* es una aplicación particular de su uso basada en tres niveles: - :file:`leafext.js` en un nivel más bajo y absolutamente genérico. - :file:`adjofer/maps/adjofer/map.js` para definir en concreto ila naturaleza de mapa, cuáles son los iconos, cómo se dibujan a partir de los datos y cómo varían en función de las correcciones y filtros que se apliquen sobre tales datos. Su implementación es independiente de cuál sea la herramienta que se use para construir la interfaz visual. - :file:`interfaz/adofer.vuejs/scripts/main.js` que implementa una interfaz atractiva haciendo uso de los dos niveles anteriores. Es el encargado de la barra lateral. Trataremos aquí cómo crear un segundo nivel usando las herramientas que nos brinda el primero, aunque para los ejemplos prácticos tendremos que introducir algún aspecto propio del tercero, ya que el mapa se tendrá que ver de alguna forma. Más adelante, describiremos cómo usar :ref:`el segundo nivel implementado para el mapa de adjudaciones ` para construir una interfaz visual (tercer nivel). Conceptos previos ***************** Partimos de la necesidad de representar sobre un mapa una serie de entidades cada una de las cuales tiene asociado un conjunto de datos. Para ello, podemos usar Leaflet_ y colocar una marca por entidad, pero el uso de esta librería (u otra equivalente) se limitará a permitirnos asignar un icono cuyo aspecto será independiente de los valores concretos de los datos. Nuestra intención, sin embargo, es ligar el aspecto del icono a los datos En principio, distinguiremos estos conceptos. **Datos** Incluyen las coordinadas de la entidad y otros datos característicos de los que se quiere dejar constancia, total o parcialmente, a través del aspecto visual de la marca. **Marca** Es la plasmación de la entidad (y sus datos) sobre el mapa. **Clase** (o **tipo**) de marca. Todas las marcas que representen un mismo tipo de entidad pertenecen a una misma clase de marca. Cada clase tiene, además, asociados un *sistema de correcciones* y un *sistema de filtros*. **Sistema de correcciones** Sistema que permite registrar, aplicar y revertir correcciones aplicables a los datos de la marca que constituyan una serie. Por ejemplo, en un centro educativo, su oferta de enseñanzas es la lista de enseñanzas que se imparten en él. Al usuario pueden interesarle unas enseñanzas, pero no otras, así que su intención puede ser eliminar temporalmente esas enseñanzas que considera irrelevantes a los efectos de su búsqueda. **Sistema de filtros** Sistema que permite fijar criterios para hacer desaparecer (o volver a mostrar) marcas. .. _dev-map-util: .. rubric:: ¿Cuándo puede resultarme útil leafext.js? La librería se torna útil cuando, dado un conjunto de datos a representar sobre un mapa: #. Se quiere que los iconos que representan datos de un mismo tipo de entidad no sean exactamente iguales, sino que partiendo de una plantilla sufran alteraciones en función de loes valores de sus datos. Por ejemplo, que el color dependa de lo grande que sea la magnitud del valor correspondiente. #. Los datos puede sufrir correcciones por la interacción del usuario, lo cual por supuesto podrá tener reflejo en el aspecto del icono, si éste dependía de los datos cuyo valor ha cambiado. #. Parte de los iconos pueden filtrarse y desaparecer, como consecuencia de las decisiones del usuario. Y, por supuesto, cuando deseamos hacer todo esto conjuntamente. ;-) Para que se haga una idea, esto es mapa desarrollado con la librería: .. image:: files/iconos.png Todos los iconos representan centros educativos y todos tienen una plantilla común, pero sus detalles visuales particulares dependen de los datos que cada uno tiene asociados (algunos son bilingües en inglés y muestran una bandera, otros son de compensatoria y muestran un pequeño círculo azul, etc.). Uso básico ********** .. note:: Quizás pueda servirle de ayuda a la lectura, tener a la vista desde el principio :ref:`el ejemplo mínimo de aplicación `. Preliminares ============ .. _dev-map-data: Datos ----- La idea es que disponemos de un conjunto de datos, que describen un conjunto de entidades localizables en un mapa. Por ejemplo, la entidades pueden ser centros educativos, de cada uno de lo cuales se conoce su situación geográfica y una serie de características de interés. Nuestra intención es convertir cada entidad en una marca dentro del mapa. Consideremos que el formato de los datos es GeoJSON_: .. code-block:: json { "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [-5.9526, 37.275475] }, "properties": { "name": "Centro 1", "adj": ["Suprimido", "Concursillo", "Concursillo", "Interino"], "oferta": ["SMR", "DAM", "BACHILLERATO"], "tipo": "normal" } }, { "type": "Feature", "geometry": { "type": "Point", "coordinates": [-4.6389, 37.58434] }, "properties": { "name": "Centro 2", "adj": ["Concursillo", "Expectativa", "Interino"], "oferta": ["SMR", "ASIR"], "tipo": "dificil" } } ] } .. note:: No es requisito que los datos tengan este formato, pero es un estándar y Leaflet_ dispone de `un tipo de capa `_ que es capaz de interpretarlos directamente generando una marca y conectándole los datos a través del atributo ``feature``. En cualquier caso, es posible utilizar un formato cualquiera de datos, si creamos nosotros mismos la marca y le asociamos sus datos a través de un atributo cualquiera. Requerimientos -------------- Como es obvio, el uso de la librería exige la carga previa de Leaflet_: .. code-block:: html A lo que podríamos añadir nuestros *plugins* favoritos de Leaflet_, y la carga de nuestra librería y el *script* donde desarrollaremos la creación del mapa. .. code-block:: html Las pautas para escribir este último *script* (:file:`scripts/demo.js`) (y el propio documento |HTML| claro está) son el propósito de este documento. También, por supuesto, deberíamos incluir en el |HTML| un elemento en el que incrustar el mapa. Típicamente: .. code-block:: html
.. _leafext-carga: Carga básica ============ Para cargar el mapa y los datos podemos distinguir cuatro tareas distintas:: const Icono = crearIcono(); map = L.map("map").setView([37.07, -6.27], 9); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18 }).addTo(map); // Y una capa GeoJSON para crear las marcas y conectarles los datos. const layer = L.geoJSON(null, { pointToLayer: (f, p) => new Centro(p, { icon: new Icono(), title: f.properties.name, }) }).addTo(map); const Centro = L.MutableMarker.extend({ options: {mutable: "feature.properties"} }); layer.addData(datos); #. La creación del icono, que hemos incluido dentro de la función ``crearIcono()``, a lo que dedicaremos el próximo apartado. #. La creación del mapa, que es la habitual con Leaflet_. #. La creación de una capa para el tratamiento de los datos en formato *GeoJSON*. En este caso se ha supuesto que los datos se obtuvieron previamente de algún modo. Obsérvese cómo se usa la clase de marca (``Centro``) e icono (``Icono``). En caso de que el formato de entrada no sea GeoJSON_, podríamos usar simplemente `L.LayerGroup `_ o `L.FeatureGroup `_, aunque tendríamos que ligar manualmente los datos a la marca. .. seealso:: Vea cómo :ref:`tratar datos que no tengan formato GeoJSON `. #. La creación de la marca apropiada que trataremos más adelante. .. _crear-icono: Icono ===== La definición del icono es la parte más engorrosa de toda la programación, en la medida en que al ser un icono cuyo aspecto cambia según los datos particulares asociados a cada marca o según las correcciones que el usuario imponga a estos datos, hay que definir cuáles son las reglas de cambio. En un icono normal, además de propiedades adicionales como el tamaño o el punto de anclaje, la propiedad fundamental es aquella que define cuál es el icono: ``iconUrl`` para iconos que se definen como imágenes, y ``html`` para iconos L.DivIcon_. Para nuestros iconos diversos y mutables, en cambio, hay que definir también cómo los datos se traducen en detalles visuales del icono. Para ilustrar la explicación usemos este sencillo, parecido a un *Chupa Chups*: .. image:: files/chupachups.png El icono tiene dos detalles que depende de los datos asociados: el número que representa el número de adjudicaciones; y el fondo del círculo que es un color que depende del tipo de centro. Definición ---------- Para la definición de iconos, en principio, se ha manipulado la clase L.DivIcon_ para que mediante opciones adicionales acepte una plantilla para generar iconos más que un elemento |HTML| que defina el aspecto invariante del icono. .. js:autoclass:: MutableIcon Las opciones que debemos proporcionar en la creación de un tipo\ [#]_ de icono son las siguientes: ``html`` (o bien, ``url``) Define la plantilla que se usará para crear el icono. Sobre esa plantilla se realizarán variaciones determinadas por los valores concretos de los datos. Si se proporciona ``url`` se entiende que es un fichero donde se ha almacenado la definición. Un típico caso, sería pasar la |URL| a un |SVG|:: const url = "images/centro.svg"; ``html``, en cambio, debe usarse cuado la definición de la plantilla se hace: * A través de una cadena:: const html = '
' * A través de un DocumentFragment_ que sería el objeto que obtendríamos si hubiéramos incluido la definición a través de un `