Tecnologías libres geoespaciales

Propósito

Desarrollar una aplicacón cliente-servidor SIG compleja mediante tecnologías SIG libres.

Índice

  1. Datos

  2. SIG

  3. Servidor Web

  4. Cliente Web

  5. Comunicación C/S

  6. Estilo

  7. Más datos

  8. Overpass API

  9. Geocoding

  10. Ruteador

Materiales

Carpeta de Materiales

Datos

Fuentes de Datos

Información Geográfica Voluntaria

Datos Abiertos

  • Empresas
  • Administraciones públicas
  • Grupos de investigación

OpenStreetMap

Proyecto colaborativo donde la comunidad crea y actualiza el mapa libre del mundo.

Formato

  • Nodes
  • Ways
  • Relations
  • Tags

Descargar datos

SIG

PostgreSQL

Sistema de Bases de Datos relacional de código abierto.

Utiliza el lenguaje declarativo SQL.

SELECT [columnas] FROM [bases de datos] WHERE [condición]

PostGIS

Módulo que añade soporte de objetos geográficos.

Añade tipos y funciones para convertir la base de datos en espacial.

PgAdmin

Herramienta para administrar bases de datos PostgreSQL.

Conectando desde pgAdmin

Con pgAdmin, conectarse a PostgreSQL

usuario : eghost | contraseña : eghost

Preparando la BD

Descripción PgAdmin Consola
Conectar desde consola
postgres : postgres.
(pág. ant.) su postgres
SI fuese necesario crear otro usuario llamado user2 con clave user2. psql -c "CREATE USER user2 WITH PASSWORD 'user2';"
Crear una base de datos
llamada app para el usuario user.
psql -c "CREATE DATABASE app OWNER user"
Crear extensión postgis en la base de datos app. psql -d app -c "CREATE EXTENSION postgis;"

osm2pgsql

Conversor para pasar datos OSM a formato PostGIS. Aquí se traduce de Nodes, Ways y Relations a geometrías tipo Point, LineString, Polygon, etc.

consola osm2pgsql -c -s -d app -U eghost -W -H localhost -P 5432 euskadi.osm.pbf-c : Crear los datos (si ya existe algo, borrar todo lo existente).
-s : Modo slim, almacena datos temporales en la BD reduciendo el consumo de RAM.
-d : Nombre de la BD.
-U : Nombre del usuario con el que se insertarán los datos.
-W : Solicitar contraseña del usuario.
-H : Dirección donde está PostgreSQL.
-P : Puerto al que está conectado PostgreSQL.
--cache-strategy sparse : Estrategia de cache para la inserción.
--cache X : Tamaño de la cache.

Al rededor de 5 min.

qGIS

Visualizar datos geográficos, reproyectar archivos, crear consultas, etc.


Complementos

OpenLayers Plugin

SRID / Proyección

3857 / 900913 4326 / LatLon
Esfera Elipsoide
Valores métricos (-20037508.34 a 20037508.34) Grados (-180 a 180 y -90 a 90)

¿Qué se ha cargado?

OSM : Nodes / Ways / Rels / Roads

Geometrías : Point / Line / Polygon


Columna "way": geometrías con las que operar.

qGIS

Ver los datos de PostGIS

ST_TRANSFORM()

Cambiar el SRID PERMITIDO de la tabla a 4326 (Lat-Lon), para poder insertar geometrías en ese SRID.

pgAdminSELECT UpdateGeometrySRID( 'planet_osm_X' , 'way' , 4326 );

Reproyectar las geometrías a la referencia espacial 4326.

pgAdminUPDATE planet_osm_X SET way = ST_TRANSFORM( ST_SETSRID( way, 900913), 4326 );

Servidor Web

NodeJS

Servidor JavaScript y orientado a eventos.

  • Variables :
    var a = 2341;
    var b = 'texto';
    var c = "tengo " + 25 + " años";
    var d = 2.56;
    var e = 'Esto tam' + "bién " + 'es t' + "exto"
  • Arrays :
    var arr = [936.9, 234, 'hola', 58, 'adios'];
  • Objetos :
    var obj = {clave1 : val1, clave2 : val2, clave3 : func, clave4 : otraFunc};
  • Funciones :
    function nombre( parámetros ){ código de la función };

Javascript

  • Ejecutar función :
    nombreFuncion();
    nombreOtraFuncion( parametros );
  • Acceder posiciones array :
    array[0]; // PRIMERA posición del array
    array[4]; // QUINTA posición del array
  • Acceder valores objeto :
    objeto.clave1;
    objeto['clave2'];
    objeto[324]; // SIENDO 324 UNA DE LAS CLAVES
  • Funciones interesantes :
    Math.random(); // Número aleatorio de 0 a 1
    parseInt( XXXX ); // Intenta convertir lo introducido a número entero.
    parseFloat( XXXX ); // Intenta convertir lo introducido a número con coma.
    console.log( ALGO ); // Saca en la consola el valor introducido.

Consola de Chromium.

Escuchadores

Funciones que se quedan a la espera de que suceda un evento.

UnElemento.onclick = function mostrarMensaje(){
        console.log('He hecho click!');
}
  • onload
  • onclick
  • onmouseover
  • onmouseout
  • onfocus

¡¡¡ Métodos callback !!!

Funciones que se ejecutan de forma asíncrona o en un momento posterior. En lugar de detener el programa esperando al resultado de una función, a esa función se le indica a dónde tiene que llamar una vez ha terminado.

Sin callback:

Servidor var miNumero;
miNumero = dameNumero();
Base de datosfunction dameNumero() {
        // Tardo 20 segundos en obtener el número
        // Una vez lo tengo lo devuelvo a quien lo ha pedido
        return num;
}

¡¡¡ Métodos callback !!!

Con callback:

Servidor var miNumero;
dameNumero( devuelveseloAEste );

function devuelveseloAEste( num ){
        miNumero = num;
}
Base de datosfunction dameNumero( aQuienLeMando ) {
        // Tardo 20 segundos en obtener el número
        // Una vez lo tengo LLAMO A LA FUNCIÓN QUE ME HAN DICHO QUE LO RECIBE
        aQuienLeMando( num );
}

¡¡¡ Métodos callback !!!

Callback anidada:

Servidor var miNumero;
dameNumero( function devuelveseloAEste ( num ) { miNumero = num; } );
Base de datosfunction dameNumero( aQuienLeMando ) {
        // Tardo 20 segundos en obtener el número
        // Una vez lo tengo LLAMO A LA FUNCIÓN QUE ME HAN DICHO QUE LO RECIBE
        aQuienLeMando( num );
}

NodeJS + expressJS

Express es un framework construído sobre NodeJS para facilitar la creación de aplicaciones web.

NPM

Los módulos/paquetes en NodeJS se instalan mediante su gestor NPM (lista de todos los paquetes). Este viene ya incluído en NodeJS.

Preparar Geany

Build Commands : node "%f"

Ejecutar :

Preparando el servidor

Dentro de la carpeta servidor hay un archivo llamado server.js que será el servidor web.

Con una consola, navegar hasta la dirección de la carpeta servidor e instalar el módulo express mediante el comando:

consola npm install express

Todos los módulos que se instalen lo harán en la carpeta node_modules.

Levantando el servidor

server.js var express = require('express'); // Cargar express
var server = express(); // Crear un servidor

server.listen(3000); // Iniciar servidor en el puerto 3000

console.log('Servidor iniciado');

Interceptar peticiones

HTTP : GET, POST, PUT, DELETE

server.js server.get('/holaserver', function(req, res){
    res.send('Hola server');
});server.get( URL , CALLBACK )
http://localhost:3000/holaserver

Parámetros en las peticiones

server.js server.get('/:nombre', function(req, res){
    res.send('Hola ' + req.params.nombre);
});server.get( URL , CALLBACK )
http://localhost:3000/MiNombre

Cliente Web

Cliente dentro del servidor

El servidor va a ser el encargado de "servir" la web. Para ello, se define una carpeta pública en el servidor.

server.jsserver.use(express.static(__dirname + '/public'));

Todo lo que esté dentro de esa carpeta pública, se puede acceder sin necesidad de interceptar cada petición.

Además, al pedir la dirección raíz '/', vamos a decirle que devuelva el archivo index de la web.

server.jsserver.get('/', function(req, res) {
        fs.readFile(__dirname + '/public/index.html', 'utf8', function(err, html){
            res.send(html);
        });
    });

Web : http://localhost:3000

HTML5

index.html + codigo.js

Codigo inicial - index.html<!DOCTYPE html>
        <html>
        <head>
        <!--DATOS DE LA WEB-->
        <title>Cliente</title>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <!--CARGAMOS LOS CSS EXTERNOS-->

        </head>
        <body>
        <!--NUESTRO CODIGO HTML-->

        <!--CARGAMOS LOS JAVASCRIPTS EXTERNOS-->

        <!--CARGAMOS NUESTRO JAVASCRIPT-->
        <script src="codigo.js"></script>

        </body>
        </html>

LeafletJS

Librería Open-Source para mapas interactivos orientada a móviles.

CSS externo - index.html<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.css" />
JAVASCRIPT externo - index.html<script src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script>
HTML - index.html<div id="contenedorMapa" style="height: XXXpx;"></div>

Crear mapa vacío

L.map( ID del div , {opciones} )

codigo.js var map = L.map('contenedorMapa', {
        center: [41.98504, 2.82814]
        ,zoom: 14
        });

API de Leaflet

Capa OpenStreetMap

L.tileLayer( URL , {opciones} );

codigo.js var capaOSM = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 19,
        attribution: '© Colaboradores de OpenStreetMap'
});

var map = L.map('contenedorMapa', {
        center: [41.98504, 2.82814]
        ,zoom: 18
        ,layers : [capaOSM]
});layers : Capas que se cargarán al inicio.

Varias capas

codigo.js var capaSatelite = L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}.jpg', {
        maxZoom: 18,
        attribution: 'Mapas de ArcGIS Online'
});

var map = L.map('contenedorMapa', {
        center: [41.98504, 2.82814]
        ,zoom: 18
        ,layers : [capaOSM, capaSatelite]
});

Control de capas

L.control.layers( {Capas base} , {Capas superpuestas} )

codigo.js var map = L.map('contenedorMapa', {
        center: [41.98504, 2.82814]
        ,zoom: 18
        ,layers : [capaOSM, geometriasDibujadas, universidades, comer, ruta]
});

L.control.layers(
  {'OpenStreetMap': capaOSM,
  'Satélite ArcGIS' : capaSatelite},
  {'Área dibujada' : geometriasDibujadas,
  'Universidades' : universidades,
  'Dónde comer' : comer,
  'Ruta' : ruta}
).addTo(map);L.control.layers({Capas base}, {Capas sobrepuestas}) : Selector de capas.

Plugins

La filosofía de Leaflet se basa en mantener la librería ligera y añadir funcionalidades mediante plugins.

Leaflet Draw

Editor de geometrías y marcadores para Leaflet.

CSS externo - index.html<link rel="stylesheet" href="http://leaflet.github.io/Leaflet.draw/leaflet.draw.css"/>
JAVASCRIPT externo - index.html<script src="http://leaflet.github.io/Leaflet.draw/leaflet.draw.js"></script>

L.Control.draw( {opciones} );

codigo.js var drawControl = new L.Control.Draw({});
map.addControl(drawControl);

Opciones del plugin

codigo.js var drawControl = new L.Control.Draw({
        draw: {
                polygon: {
                        allowIntersection: false,
                        shapeOptions: {
                                color: 'black',
                                fillColor: 'none'
                                }
                        },
                rectangle: {
                        shapeOptions: {
                                color: 'black',
                                fillColor: 'none'
                                }
                        },
                polyline: false,
                circle: false,
                marker: false
                }
        });

Área dibujada

Las geometrías se crean en una capa pero no se añaden al mapa.

L.FeatureGroup() : Agrupador de capas

codigo.js var geometriasDibujadas = new L.FeatureGroup();
L.FeatureGroup() : Agrupador de capas.

Evento al dibujar una geometria

codigo.js map.on('draw:created', function (evento) {
        console.log('Geometria dibujada');
        geometriasDibujadas.addLayer(evento.layer);
});map.on( EVENTO, CALLBACK);

jQuery

Framework de Javascript libre para simplificar las funciones y el manejo del árbol DOM.

JAVASCRIPT externo - index.html<script src="http://code.jquery.com/jquery-latest.min.js"></script>

EL carácter $ es un alias del cual servirá para llamar a sus métodos.

Comunicación C/S

XML VS JSON

XML


        Tove
        Jani
        Reminder
        Don't forget me this weekend!
  • Más legible.
  • Extensible, SCHEMA para tipos de datos, valicación, etc.
  • Más pesado.

XML VS JSON

JSON

{ note : 
        { to : "Tove",
        from : "Jani",
        heading : "Reminder",
        text : "Don't forget me this weekend!"}
}
  • Limitado.
  • Más ligero.
  • Subconjunto del lenguaje Javascript.

JSON.parse()
+
JSON.stringify()

JSON, el mejor amigo de Javascript.

GeoJSON

Estándar para representación de elemento geográficos y sus atributos en formato JSON.

Point

{ "type": "Point", "coordinates": [100.0, 0.0] }  

LineString

{ "type": "LineString", "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] } 

Polygon

{ "type": "Polygon", "coordinates": [ [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ], [ [100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2] ] ] }  

ST_GeomFromGeoJSON()
+
ST_AsGeoJSON()

GeoJSON, el mejor amigo de Javascript para aplicaciones SIG.

Primera interacción

Aprovechando el evento de creación de geometría, pedir universidades.

codigo.js map.on("draw:created", function (evento) {
	geometriasDibujadas.clearLayers(); // Borrar la capa de geometrias dibujadas
        
        geometriasDibujadas.addLayer(evento.layer); // Cargar la geometria dibujada

	var geom = evento.layer.toGeoJSON().geometry; // Recoger el GeoJSON de la geometria dibujada
	
        $.getJSON('http://localhost:3000/universidades/' + JSON.stringify(geom), function (respuesta) {
        
                var capa = L.geoJson(respuesta.geojson);
                universidades.addLayer(capa);
        	
        });
});$.getJSON( URL, CALLBACK );

Consulta a la BD

Sentencia SQL

pgAdmin > Databases > app > Schemas > public > Tables

SELECT way FROM planet_osm_polygon WHERE amenity = 'university'

ST_AsGeoJSON()

¿Geometrías en formato binario? ST_AsGeoJSON(geom)

SELECT ST_AsGeoJSON(way) AS geometria FROM planet_osm_polygon WHERE amenity = 'university'

ST_INTERSECTS()

Solo el área seleccionada : ST_INTERSECTS(geom, geom)

SELECT ST_AsGeoJSON(way) FROM planet_osm_polygon 
WHERE amenity = 'university' AND ST_INTERSECTS(way, AREA SELECCIONADA)


Crear el área seleccionada :

SELECT ST_AsGeoJSON(way) FROM planet_osm_polygon 
WHERE amenity = 'university' AND ST_INTERSECTS(way, ST_SetSRID( ST_GeomFromJSON( json ), 4326 ) )

{"type":"Polygon","coordinates":[[[2.8161048889160156,41.980676549488464],[2.8161048889160156,41.98782219625381],[2.8290224075317383,41.98782219625381],[2.8290224075317383,41.980676549488464],[2.8161048889160156,41.980676549488464]]]}

Servidor

Interceptar la petición de universidades, pedirlas a la BD y devolverlas.

consola npm install pgCliente PostgreSQL para NodeJS.
server.js var pg = require('pg');
var conexion = "postgres://NOMBRE:PASSWORD@DIRECCION/BD";
        
var cliente = new pg.Client(conexion);
cliente.connect(function(error) {

        'INTENTO CONECTAR A LA BD';
        
});cliente.connect( CALLBACK )

Conectar a la BD

server.js cliente.connect(function(error) {
        if(error) {
        
                'NO SE HA PODIDO CONECTAR, MAS INFO EN ' + error;
                
        } else {
        
                'HEMOS CONECTADO, VAMOS A HACER LA CONSULTA';
                
        }
});Comprobar si la conexión ha dado error, sino hacer la consulta.

Consultar a la BD

server.js cliente.query( CONSULTA, function(error, resultado) {
        if(error) {
        
                'NO SE HA PODIDO HACER LA CONSULTA, MAS INFO EN ' + error;
                
        } else {
        
                '¡TENEMOS EL RESULTADO! ESTÁ DENTRO DE LA VARIABLE resultado';
                
        }
        cliente.end();
});Comprobar si la consulta ha dado error. Sino, devolver el resultado.

Recoger el resultado de la consulta

server.js } else {
        
        'RECORREMOS RESULTADO.ROWS';
        
        for (var i = 0; i < resultado.rows.length; i++){
        
                'CADA FILA QUE NOS HA DEVUELTO LA CONSULTA';
                
        }
}Los datos extraídos se encuentran dentro del array ROWS. Recorrerlo con un bucle.

Dentro de cada posición de ROWS, habrá un objeto clave-valor para las columnas u operaciones seleccionadas :
{col1 : dato1, col2 : dato2}

server.js for (var i = 0; i < resultado.rows.length; i++){
        
        'CADA COLUMNA ESTÁ DENTRO DE RESULTADO.ROWS[posicion].NOMBRECOLUMNA';
        
}

FeatureCollection

Leaflet necesita los datos como FeatureCollection http://geojson.org/geojson-spec.html#examples

server.js } else {
        
        var geojson = { "type": "FeatureCollection",
                "features": []
        };
        
        for (var i = 0; i < resultado.rows.length; i++){
                var feature = { "type": "Feature",
                        "geometry": resultado.rows[i].geometria,
                        "properties": {}
                        };
                
                geojson['features'].push(feature);
        }
        
        respuesta.geojson = geojson;
        
        res.send(respuesta);
}Devolver el resultado siguiendo la especificación de GeoJSON.

Comprobar el formato

Recibido VS Lo que queremos

-

JSON.parse();

...
"geometry": JSON.parse( resultado.rows[i].geometria )
...

Cliente

codigo.js var universidades = new L.FeatureGroup();
codigo.js map.on('draw:created', function (evento) {
        
       geometriasDibujadas.clearLayers(); // Borrar la capa de geometrias dibujadas
       universidades.clearLayers(); // Borrar las universidades descargadas
       comer.clearLayers(); // Borrar los puntos de comida descargados
       ruta.clearLayers(); // Borrar las rutas

       geometriasDibujadas.addLayer(evento.layer); // Cargar la geometria dibujada

       var geom = evento.layer.toGeoJSON().geometry; // Recoger el GeoJSON de la geometria dibujada
       $.get('http://localhost:3000/universidades/' + JSON.stringify(geom), function(respuesta){

              console.log('Datos descargados');

              // Una vez descargados los datos desde el servidor, crear una capa con ellos
              var capa = L.geoJson(respuesta.geojson);

              universidades.addLayer(capa);

       });
});

Estilo

Estilizando capas

Leaflet permite definir el estilo de una capa.

  • color
  • fillColor
  • weight
  • opacity
  • fillOpacity
  • dashArray
codigo.js var capa = L.geoJson(
        respuesta.geojson, 
        {style: {color: 'black', fillColor: 'blue', dashArray: '50, 25'}}
        );L.geoJson( DATOS , OPCIONES )

Estilizando geometrías

También se puede indicar una función, la cual recibirá por separado cada geometría, y generar en ella el estilo.

codigo.js var capa = L.geoJson(
        respuesta.geojson, 
        {style: generarEstilo}
        );L.geoJson( DATOS , style : función )
codigo.js function generarEstilo( elemento ) {
        return {
                fillColor: elemento.properties.fillColor || 'gray',
                weight: elemento.properties.weight || 2,
                pacity: elemento.properties.opacity || 0.8,
                color: elemento.properties.color || 'black',
                fillOpacity: elemento.properties.fillopacity || 0.5,
                dashArray: elemento.properties.dashArray
        };
}

Estilo desde el servidor

server.js var relleno = 'rgb(' + parseInt(Math.random()*255) + ',' + parseInt(Math.random()*255) + ',' + parseInt(Math.random()*255) + ')';
        
var borde = 'rgb(' + parseInt(Math.random()*255) + ',' + parseInt(Math.random()*255) + ',' + parseInt(Math.random()*255) + ')';

...

var feature = { "type": "Feature",
        "geometry": JSON.parse(resultado.rows[i].geometria),
        "properties": {color : borde, fillColor : relleno}
};

Popup

bindPopup() permite añadir un Popup a una geometría/capa.

Al igual que Style, existe onEachFeature que recibe cada feature al ser cargado.

codigo.js var capa = L.geoJson(
        respuesta.geojson, 
        {style: generarEstilo, onEachFeature: generarEventos}
);

Popup desde el servidor

codigo.js function generarEventos( elemento , capa ) {
   capa.on({
      click: function( evento ) {
         if ( evento.target.feature.properties && evento.target.feature.properties.popup ){
             capa.bindPopup( evento.target.feature.properties.popup );
        }
    }
});
}
server.js var feature = { "type": "Feature",
        "geometry": JSON.parse(resultado.rows[i].geometria),
        "properties": {
        
        color : borde, 
        fillColor : relleno, 
        popup : 'Universidad ' + i}
        
};

Popup "elegante"

server.js select st_asgeojson(way) AS geometria, NAME from planet_osm_polygon ...
server.js var popup = resultado.rows[i].name;
        
var feature = { "type": "Feature",
        "geometry": JSON.parse(resultado.rows[i].st_asgeojson),
        "properties": {color : borde, 
        fillColor : relleno, 
        popup : popup}
        
};

Más datos

Pidiendo más

Aprovechando el popup, solicitar más datos desde él.

server.js var popup = '<div align="center"><h1>' + resultado.rows[i].name + '</h1>' + 
        '<button onclick="buscarDondeComer()">Buscar lugares donde comer</button>' +
        '</div>';

Buscar a X distancia de la universidad.

Para ello es necesario enviar al servidor el ID de la universidad. Lo haremos mediante el popup.

Enviar ID desde el servidor :

server.js select st_asgeojson(way) AS geometria, name, OSM_ID from planet_osm_polygon ...

server.js var popup = '<div align="center"><h1>' + resultado.rows[i].name + '</h1>' + 
        '<button onclick="buscarDondeComer(' + resultado.rows[i].osm_id + ')">Buscar lugares donde comer</button>' +
        '</div>';

Cliente recibe el ID

Pedir al servidor pasándole el ID.

codigo.js function buscarDondeComer( idUniversidad ){
        $.get('http://localhost:3000/comer/' + idUniversidad, function(respuesta){
                var capa = L.geoJson(respuesta, {style: generarEstilo, onEachFeature : generarEventos});
                comer.addLayer(capa);
        });
}

Sentencia SQL

Conocer qué valores existen en la BD.

SELECT DISTINCT(amenity) FROM planet_osm_point;

Elegir todos los puntos que tengan alguno de los valores

SELECT ST_AsGeoJSON(way) AS geometria, name FROM planet_osm_point 
WHERE amenity IN ('restaurant', 'bar', 'fast_food', 'vending_machine');

Solo los cercanos a la universidad : ST_DISTANCE(geom, geom)

SELECT ST_AsGeoJSON(comer.way), name FROM
        
planet_osm_point AS comer,
( ... ) AS universidad

WHERE amenity IN ('restaurant', 'bar', 'fast_food', 'vending_machine') 
AND
ST_DISTANCE(comer.way, universidad.way) < 1000

Consultas anidadas

SELECT ST_AsGeoJSON(comer.way), name FROM
        
planet_osm_point AS comer,
( SELECT way FROM planet_osm_polygon WHERE osm_id = ID ) AS universidad

WHERE amenity IN ('restaurant', 'bar', 'fast_food', 'vending_machine') 
AND
ST_DISTANCE(comer.way, universidad.way) < 1000

ST_Distance

ST_Distance desconoce la proyección o el tipo de datos y únicamente devuelve la diferencia de un punto al otro.


Al estar los datos en Lat/Lon, está devolviendo la distancia en grados2.

Para indicar que los datos están en grados y se quiere conocer la distancia en metros, es necesario hacer CASTING al tipo geography.

SELECT ST_AsGeoJSON(comer.way), name FROM
        
planet_osm_point AS comer,
( SELECT way FROM planet_osm_polygon WHERE osm_id = ID ) AS universidad

WHERE amenity IN ('restaurant', 'bar', 'fast_food', 'vending_machine') 
AND
ST_DISTANCE(comer.WAY :: GEOGRAPHY , universidad.WAY :: GEOGRAPHY ) < 1000
server.js server.get('/comer/:id', function(req, res){
       
       ...
       
});

Overpass API

API para descargar datos directamente desde el servidor de OpenStreetMap.

Pros:

  • Sin necesidad de base de datos
  • Datos actualizados
  • Consultas complejas

Contras:

  • Lenguaje específico de consultas
  • Petición y Resultados en formato OSM
  • Número de funciones limitado
  • Para áreas pequeñas

Universidades en un área

Navegadorhttp://overpass-api.de/api/interpreter?data=
        [out:json];
        (way(41.9288,2.76796,42.001,2.86769)[amenity=university];>;
        rel(41.9288,2.76796,42.001,2.86769)[amenity=university];>>;);
        out;

Restaurantes a 1000m de universidades

Navegadorhttp://overpass-api.de/api/interpreter?data=
        [out:json];
        (way(41.9288,2.76796,42.001,2.86769)[amenity=university];>;
        rel(41.9288,2.76796,42.001,2.86769)[amenity=university];>>;);
        (node[amenity=restaurant](around:1000.0););
        out;

Geocoding

Geocoding

Obtener localización de un nombre o dirección.

Se puede conocer qué operaciones va a realizar en función de la búsqueda.

  • format=json
  • polygon_geojson=1
  • address_details=1
  • ...

http://nominatim.openstreetmap.org/ (Universidad de Deusto, Bilbao)

http://nominatim.openstreetmap.org/search?
        q=Universidad+de+Deusto,Bilbao&
        format=json&
        polygon_geojson=1&
        addressdetails=1

Geocoding inverso

Dada una localización, saber qué hay en ella.

Zoom indica el nivel de detalle.

Universidad de Deusto : -2.93868803825145, 43.27135975

http://nominatim.openstreetmap.org/reverse?
        format=json&
        lat=43.27135975&
        lon=-2.93868803825145&
        zoom=1&
        addressdetails=1

Añadiendo geocodificación

index.html

Este llama a la función geocodificar de codigo.js.

var lugar = $('#geocoding').val();
	
    $.get('http://nominatim.openstreetmap.org/search/' + lugar + '?format=json', function(respuesta){
		
	if(respuesta.length){
	
		map.setView(new L.LatLng(respuesta[0].lat, respuesta[0].lon), 14);
		
	} else {
	
		alert('No se ha encontrado el destino.');
		
	}
  });

Enrutador

pgRouting

Proceso de selección del camino más óptimo.

Se compone de un grafo, una medida de coste y distintos algoritmos.

  • A*
  • Dijkstra
  • Distancia accesible
  • ...

Añadir extensión pgRouting a la BD.

osm2pgrouting

Convertir datos de OpenStreetMap a gragos enrutables de pgrouting.

consola./osm2pgrouting -dbname app -user eghost -passwd eghost -conf mapconfig.xml -file euskadi.osm 

¿Qué se ha cargado?

Tabla 'ways' : grafo representando la red viaria.

  • gid : Id de cada arista.
  • length : largura de la arista.
  • to_cost : columna donde almacenar el coste.
  • reverse_cost : coste de volver hacia atrás por la arista.
  • source : punto de inicio.
  • target : punto de fin.
  • ...

qGIS

Existe un plugin de pgRouting para qGis.

Complementos > Obtener complementos de Python > Activar Experimentales

Preparación del plugin

  • edge_table = ways
  • geometry = the_geom
  • id = gid
  • source = source
  • target = target
  • cost = length

Qué hay por detrás

pgr_dijkstra('CONSULTA', inicio, fin, direcionado, coste revés);

pgr_dijkstra(
        
  'Sentencia SQL que tiene que devolver algo llamado : ID, SOURCE, TARGET Y COST',
  
  source, 
  target, 
  false, 
  false);

Tomamos los datos de nuestra tabla

pgr_dijkstra(
        
  'SELECT gid AS ID, source AS SOURCE, target AS TARGET, length AS COST FROM ways',
  
  7, 12, false, false);

Calcular la distancia a pie

Enviar el id de universidad (polígono) y restaurante (punto).

server.js server.get('/comer/:id', function(req, res){
        
  ...
  
  cliente.query('SELECT ST_AsGeoJSON(comer.way), name, COMER.OSM_ID ...')
  
  ...
  
  var popup = '

' + resultado.rows[i].name + '

' + ''+ '
';

Crear una función que calcule la distancia

server.js server.get('/distancia/:desdeId/:hastaId', function(req, respuesta){
  
  var idUniversidad = req.params.desdeId;
  var idComer = req.params.hastaId;
  var distanciaTotal = 0;

    
   'Hacemos las consultas'

});

Source o target mas cercano a la universidad

Para comenzar el enrutamiento desde él, pedir el source o target mas cercano.

server.js "SELECT vias.source, 
ST_DISTANCE( universidades.way::geography, ST_SETSRID( ST_MAKEPOINT( vias.x1 , vias.y1 ) , 4326 )::geography ) AS dist, 
ST_AsGeoJSON( ST_MAKELINE( ST_CLOSESTPOINT( universidades.way , vias.the_geom ) , ST_SETSRID( ST_MAKEPOINT( vias.x1 , vias.y1 ) , 4326 ) ) ) AS geometria 

FROM 

planet_osm_polygon AS universidades, ways AS vias 

WHERE 

universidades.osm_id = " + idUniversidad + "

ORDER BY dist ASC 

LIMIT 1";

Operador &&

Limitar a solo las vías cercanas a 100 metros

El operador && calcula si hay intersección entre las bbox.

server.js "SELECT vias.source, 
ST_DISTANCE( universidades.way::geography, ST_SETSRID( ST_MAKEPOINT( vias.x1 , vias.y1 ) , 4326 )::geography ) AS dist, 
ST_AsGeoJSON( ST_MAKELINE( ST_CLOSESTPOINT( universidades.way , vias.the_geom ) , ST_SETSRID( ST_MAKEPOINT( vias.x1 , vias.y1 ) , 4326 ) ) ) AS geometria 

FROM 

planet_osm_polygon AS universidades, ways AS vias 

WHERE 

universidades.osm_id = " + idUniversidad + "
AND 
ST_BUFFER( universidades.way::geography , 100)::geometry && vias.the_geom 

ORDER BY dist ASC 

LIMIT 1";

Procedimiento

  1. Source más cercano y distancia existente a la universidad.
  2. Target más cercano y distancia existente a la universidad.
  3. Source más cercano y distancia existente al punto de comida.
  4. Target más cercano y distancia existente al punto de comida
  5. Decidir si el target o source está más cerca de la universidad, guardar su ID para INICIO y sumar ya esa distancia a la total.
  6. Decidir si el target o source está más cerca del punto de comida, guardar su ID para FINAL y sumar ya esa distancia a la total.
  7. Con pgRouting, calcular la ruta desde el ID INICIO hasta el ID FINAL.

Gracias.

Tecnologías libres geoespaciales