En este post te mostraré un ejemplo de proyecto con OpenLayers en donde se muestran:
- Introducción a OpenLayers
- Descargar OpenLayers
- Creación de mapa
- Centrado de mapa en determinada ubicación
- Marcadores personalizados con imagen propia
- Listener de eventos como click o zoom cambiado
- Consumo de API PHP para obtener los marcadores
Voy a desglosar cada cosa y al final dejaré el código del proyecto completo.
Introducción a OpenLayers
La librería se define a sí misma (traducida literalmente) como:
Una biblioteca llena de funciones y de alto rendimiento para todas sus necesidades de mapeo
En pocas palabras es una librería para manejar mapas, así como Google maps, pero esta es una alternativa gratuita y open source muy parecida a Google maps.
El punto que más destaca es que es gratis.
Puedes visitar el repositorio en https://github.com/openlayers/openlayers y ver su página web en https://openlayers.org/
Descargar OpenLayers
Si usas npm puedes instalar OpenLayers con:
npm install ol
En caso de que quieras usarlo sin NPM es totalmente posible. Aquí dejo los enlaces de la última versión que también se pueden obtener en unpkg.com
Css: https://unpkg.com/openlayers@4.6.5/dist/ol.css
JavaScript: https://unpkg.com/openlayers@4.6.5/dist/ol.js
Incluye ambos en tu HTML y estamos listos.
Creación y centrado de mapa
Vamos a ver cómo crear, en primer lugar, un mapa, y mostrarlo en un contenedor. En el HTML creamos un div con el id mapa y le damos el estilo necesario.
Después, con JavaScript, creamos el mapa:
const LATITUD_CENTRO = 19.413793,
LONGITUD_CENTRO = -99.128145,
ZOOM = 15;
const mapa = new ol.Map({
target: 'mapa', // el id del elemento en donde se monta
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([LONGITUD_CENTRO, LATITUD_CENTRO]),
zoom: ZOOM,
})
});
Para crear un mapa creamos una nueva instancia de ol.Map
, se va a montar en el elemento con el id mapa
en la línea 6.
Después, por defecto, tiene una capa y una vista. En la vista (línea 12) utilizamos las coordenadas para centrarlo en determinada ubicación y le ponemos un zoom de 15.
Marcadores
Podemos crear un marcador, para ello debemos crear una capa. Después, para agregarle el icono se indica el estilo. El marcador va dentro de una capa y finalmente la capa se agrega al mapa con el método addLayer
.
El código queda así:
let marcador = new ol.Feature({
geometry: new ol.geom.Point(
ol.proj.fromLonLat([-99.12105, 19.419617])// En dónde se va a ubicar
),
});
// Agregamos icono
marcador.setStyle(new ol.style.Style({
image: new ol.style.Icon({
src: "pizza.png",
})
}));
// marcadores debe ser un arreglo
const marcadores = []; // Arreglo para que se puedan agregar otros más tarde
marcadores.push(marcador);// Agregamos el marcador al arreglo
let capa = new ol.layer.Vector({
source: new ol.source.Vector({
features: marcadores, // A la capa le ponemos los marcadores
}),
});
// Y agregamos la capa al mapa
mapa.addLayer(capa);
Lo resaltable es que se indican las coordenadas del mapa en la línea 3 y el icono del marcador en la línea 10.
Los marcadores son un arreglo, podemos invocar a push
cuantas veces sea necesario para agregar un elemento al arreglo.
La capa se define en la línea 19 con los marcadores en features
y finalmente se añade al mapa en la línea 25. Ahora tenemos un bonito marcador de una pizzería:
Por cierto, la imagen fue tomada de Pixabay.
Eventos
El mapa que acabamos de crear tiene varios eventos. Vamos a ver dos de ellos.
Click
El click es cuando se hace click en un elemento del mapa o en el mapa; con esto podemos detectar si alguien hace click en un marcador.
mapa.on('singleclick', function(evt) {
var feature = mapa.forEachFeatureAtPixel(evt.pixel, function(feature, layer) {
// Aquí se puede filtrar la feature
return feature;
});
if (feature) {
console.log("Click en: ", feature);
}
});
Cambio de zoom
Del mismo modo podemos escuchar cuando el usuario aleja o acerca el mapa, es decir, cambia el zoom:
let zoomActual = mapa.getView().getZoom();
mapa.on('moveend', function(e) {
let nuevoZoom = mapa.getView().getZoom();
if (zoomActual != nuevoZoom) {
console.log('Nuevo zoom: ' + nuevoZoom);
zoomActual = nuevoZoom;
}
});
Consumo de API con PHP para marcadores dinámicos
Es momento de mostrar el pequeño proyecto o demostración. He colocado un select
de categorías que sirve para ver categorías en el mapa. En el change
del select
hago una petición http con fetch a mi servidor con PHP y obtengo las coordenadas de los marcadores para dibujarlos.
Vamos parte por parte. Primero veamos el código HTML:
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1,
shrink-to-fit=no">
<meta name="author" content="Parzibyte">
<title>Demostración OpenLayers</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/ol.css">
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<main role="main" class="container-fluid">
<div class="row">
<div class="col-12">
<h1>Demostración mapas con OpenLayers</h1>
</div>
<div class="col-12 mb-2">
<div class="form-group">
<label for="selectCategorias">Categoría:</label>
<select class="form-control" id="selectCategorias">
<option value="veterinarias">Veterinarias</option>
<option value="pizzerias">Pizzerías</option>
</select>
</div>
</div>
<div class="col-12">
<div id="mapa"></div>
</div>
</div>
</main>
<script type="text/javascript" src="js/ol.js"></script>
<script type="text/javascript" src="js/script.js"></script>
</body>
</html>
Existe la lista desplegable y el div con el id mapa
. Estoy usando Bootstrap 4 para maquetar. Ahora veamos el script:
let ultimaCapa; // Para removerla cada vez que se selecciona otra categoría
const LATITUD_CENTRO = 19.413793,
LONGITUD_CENTRO = -99.128145,
ZOOM = 15;
const mapa = new ol.Map({
target: 'mapa', // el id del elemento en donde se monta
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([LONGITUD_CENTRO, LATITUD_CENTRO]),
zoom: ZOOM,
})
});
const refrescarMapaConCategoria = categoria => {
fetch(`coordenadas.php?categoria=${categoria}`)
.then(datos => datos.json())
.then(coordenadasConIcono => {
dibujarMarcadoresEnMapa(coordenadasConIcono);
});
};
const dibujarMarcadoresEnMapa = coordenadasConIcono => {
if (ultimaCapa) {
mapa.removeLayer(ultimaCapa);
}
const { icono, coordenadas } = coordenadasConIcono;
const marcadores = [];
coordenadas.forEach(coordenada => {
let marcador = new ol.Feature({
geometry: new ol.geom.Point(
ol.proj.fromLonLat([coordenada.longitud, coordenada.latitud])
),
});
marcador.setStyle(new ol.style.Style({
image: new ol.style.Icon(({
src: icono,
}))
}));
marcadores.push(marcador);
});
ultimaCapa = new ol.layer.Vector({
source: new ol.source.Vector({
features: marcadores,
}),
});
mapa.addLayer(ultimaCapa);
};
const $select = document.querySelector("#selectCategorias");
const obtenerCategoriaSeleccionada = () => $select.options[$select.selectedIndex].value;
const refrescarMapaConCategoriaSeleccionada = () => refrescarMapaConCategoria(obtenerCategoriaSeleccionada())
// Cuando seleccionen otra opción, refrescar el mapa
$select.addEventListener("change", refrescarMapaConCategoriaSeleccionada);
// Algunos eventos que podrían ser de utilidad
mapa.on('singleclick', function(evt) {
var feature = mapa.forEachFeatureAtPixel(evt.pixel, function(feature, layer) {
// Aquí se puede filtrar la feature
return feature;
});
if (feature) {
console.log("Click en: ", feature);
}
});
let zoomActual = mapa.getView().getZoom();
mapa.on('moveend', function(e) {
let nuevoZoom = mapa.getView().getZoom();
if (zoomActual != nuevoZoom) {
console.log('Nuevo zoom: ' + nuevoZoom);
zoomActual = nuevoZoom;
}
});
// Al inicio de todo, obtener con la primer categoría
refrescarMapaConCategoriaSeleccionada();
Lo único que se hace es escuchar el cambio de selección del elemento del select
; cuando eso sucede se hace una petición y se obtienen los marcadores.
Como se está refrescando el mapa en cada cambio, debo obtener una referencia a la capa de los marcadores para removerla en cada actualización.
Finalmente veamos el servidor de PHP que por el momento muestra información a partir de un arreglo, pero podría fácilmente conectarse a una base de datos.
<?php
if (empty($_GET["categoria"])) {
exit("No hay categoría");
}
$categoria = $_GET["categoria"];
#TODO: hacerlo en una BD
$veterinarias = [
[
"latitud" => 19.419617,
"longitud" => -99.121051,
],
[
"latitud" => 19.418919,
"longitud" => -99.134218,
],
[
"latitud" => 19.411699,
"longitud" => -99.127711,
],
];
$pizzerias = [
[
"latitud" => 19.413955,
"longitud" => -99.115889,
],
[
"latitud" => 19.413858,
"longitud" => -99.138855,
],
[
"latitud" => 19.421102,
"longitud" => -99.126836,
],
];
if ($categoria === "veterinarias") {
echo json_encode([
"icono" => "veterinaria.png",
"coordenadas" => $veterinarias,
]);
} else {
echo json_encode([
"icono" => "pizza.png",
"coordenadas" => $pizzerias,
]);
}
Es un simple if
, que podría remplazarse por una consulta usando where. Por favor observa que estoy devolviendo el icono y las coordenadas, así fácilmente se puede configurar el diseño de cada marcador.
Aquí el resultado final:
Conclusión
Espero que todo haya quedado claro, fue un proyecto simple pero en donde aprendí mucho. Creo que con esto es suficiente para crear una aplicación web simple que utilice mapas y no requiera el poder de Google Maps.
Dejo el enlace al repositorio de GitHub para que puedas explorarlo.
bro quiero contactarte y hablar
Hola. Con gusto, envíeme mensaje en https://parzibyte.me/#contacto
como seria la parte de coordenadas.php alimentada desde una base de datos en mysql que tenga una tabla mapa y que esta solo se guarde la LAT y LONG de cada marcador ??
Hola. Solo debe traer los datos de la tabla y exponerlos en la API para consumirlos del lado del cliente
Saludos 🙂