Archivo por meses: mayo 2022

Simular el posicionamient GPS (problemas en el despliegue a la nube, OVH)

Tenemos una REST API con MongoDB. Tenemos un front-end para generar posiciones y grabarlas en la bd. Y tenemos otro front-end para visualizar las posiciones y ver cómo van evolucionando.

La parte de generar las posiciones y enviarlas al servidor de momento no es una aplicación móvil, sino que es una página web. Openlayers tiene la posibilidad de hacer geolocation, y por tanto tenemos una precisión muy buena (unos 15 m). No es necesario por tanto una aplicación móvil. Ahora bien, hay que hacerlo con https.

(ref): https://stackoverflow.com/questions/39366758/geolocation-without-ssl-connection:

Warning by Chrome : getCurrentPosition() and watchPosition() no longer work on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS.

Hasta aquí ningún problema especial cuando se desarrolla localmente (localhost). Pero el problema esté cuando se despliega en el servidor, en este caso un VPS en OVH. Por tanto, la parte de generar las posiciones GPS tiene que ir al servidor OVH donde hay instalado el certificado SSL. Ahora bien, no se pueden generar certificados para el dominio ovh.net con noip.com (se han pasado de la cuota, lógico). Así pues, primero me he tenido que registrar en noip.com y ahora tengo el dominio jquintana.ddns.net.

Poner el MongoDB en el servidor no ha sido ningún problema. El problema está en la API REST, hecha con Express. La API REST se ha tenido que hacer segura (https), pues no podía ser que llamásemos a la API desde https, y que este servidor respondiese com http. Este ha sido el problema principal.

Finalmente la parte del front-end de visualizar las posiciones no ha tenido especial problema. Las llamadas a la API http://localhost:3000 se han de cambiar a les llamadas a la API https://jquintana.ddns.net:8080.

En resumen, tú puedes tener una aplicación funcionando con localhost sin especiales problemas, y cuando la quieres desplegar en la nube empiezan los problemas de CORS, seguridad, protocolos que no te habías planteado.

Ahora el último paso es tener un código que funcione igual de bien en localhost y en la nube, que esto en principio no será problema. Y la reflexión es que cuando se desarrolla un proyecto, no hay que esperar al final de tot para desplegarlo a la infraestructura de la nube. Hay que hacerlo ya en las primeras fases: es el despliegue continuo. Aunque la aplicación no tenga muchas funcionalidades, ya se ha podido afrontar todos los problemas de seguridad e infraestructura al intentar desplegar en un entorno real en las primeras fases. Y así nos ahorramos problemas al final del proyecto.

Programando el tracking de rutas

Programando casa-insitutoAyer por la noche estaba programando, y estaba grabando posiciones simuladas desde casa. Como en casa no me muevo, tuve que simular cierto movimiento. Los datos se graban en una base de datos MongoDB.

Hoy estaba continuando un rato, desde el instituto, y voilà, hay un salto desce casa al instituto. Lógico pero divertido.

Openlayers: Point, LineString, MultiLineString, Icon. Ejemplo canónico

Ejemplo canónico OpenlayersVamos a mostrar un ejemplo mínimo de Openlayers que aclare los conceptos de dibujar un punto, una línea, una multilínea y un icono. Y cómo aplicar estilos a estos elementos.

Definimos primero estos elementos (puntos, líneas, multilíneas, iconos) a partir de los datos de ejemplo (coordenadas). Entonces definimos features a partir de estos elementos, que aañadimos a un array de features. Cada feature puede tener su estilo.

Definimos después el vectorSource a partir del array de features. Después definimos el VectorLayer a partir del vectorSource. Y finalmente añadimos al mapa los layers que queremos pintar.

Este es el codi inicial y previo para un pequeño proyecto que quiero hacer, que se trata de una aplicación mòvil para enviar las coordenadas de posición a una RestAPI donde hay una base de datos (MongoDB), y con una aplicación web (front-end, que es la part que aquí se empieza a explicar) poder visualizar el recorrido del ciclista (la idea del proyecto es poder hacer el tracking de un ciclista en una primera etapa; y en una etapa posterior poder hacer el tracking de todos los ciclistas que participan en una carrera o salida).

El código final:

import 'ol/ol.css';
import Feature from 'ol/Feature';
import Map from 'ol/Map';
import VectorSource from 'ol/source/Vector';
import View from 'ol/View';
import {Icon, Circle as CircleStyle, Fill, Stroke, Style} from 'ol/style';
import {LineString, MultiLineString, Point} from 'ol/geom';
import {getVectorContext} from 'ol/render';
import {fromLonLat} from 'ol/proj';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
import OSM from 'ol/source/OSM';

//dades
var json_points = [
{'lon':1.4234,'lat':41.2344,'name':'punt 0'},
{'lon':1.4235,'lat':41.2345,'name':'punt 1'},
{'lon':1.4235,'lat':41.2346,'name':'punt 2'},
{'lon':1.4236,'lat':41.2346,'name':'punt 3'},
{'lon':1.4236,'lat':41.2347,'name':'punt 4'},
{'lon':1.4238,'lat':41.2349,'name':'punt 4'},
{'lon':1.4239,'lat':41.2350,'name':'punt 4'}
];

// estils
var styles = {
'Point': new Style({
image: new CircleStyle({
fill: new Fill({
color: 'rgba(100,100,100,.8)',
}),
radius: 3,
stroke: new Stroke({
color: '#000000',
width: 2,
}),
}),
}),
'LineString': new Style({
stroke: new Stroke({
color: '#f00',
width: 3,
}),
}),
'MultiLineString': new Style({
stroke: new Stroke({
color: '#000000',
width: .8,
}),
}),
'icona': new Style({
image: new Icon({
anchor: [0.5,20],
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
src: 'img/circle_red_8.png'
})
})
};

// points
var features_points = new Array();

var feature_point0 = new Feature({
'geometry': new Point(fromLonLat([json_points[0].lon,json_points[0].lat])),
'name': json_points[0].name,
});

var feature_point1 = new Feature({
'geometry': new Point(fromLonLat([json_points[1].lon,json_points[1].lat])),
'name': json_points[0].name,
});

var feature_point2 = new Feature({
'geometry': new Point(fromLonLat([json_points[2].lon,json_points[2].lat])),
'name': json_points[0].name,
});

var feature_point3 = new Feature({
'geometry': new Point(fromLonLat([json_points[3].lon,json_points[3].lat])),
'name': json_points[0].name,
});

var feature_point4 = new Feature({
'geometry': new Point(fromLonLat([json_points[4].lon,json_points[4].lat])),
'name': json_points[0].name,
});

features_points.push(feature_point0)
features_points.push(feature_point1)
features_points.push(feature_point2)
features_points.push(feature_point3)
features_points.push(feature_point4)

// línies
//Create a feature and add geometry as a thing
var track1 = new MultiLineString([
[[json_points[0].lon,json_points[0].lat],[json_points[1].lon,json_points[1].lat]],
[[json_points[1].lon,json_points[1].lat],[json_points[2].lon,json_points[2].lat]],
[[json_points[2].lon,json_points[2].lat],[json_points[3].lon,json_points[3].lat]],
[[json_points[3].lon,json_points[3].lat],[json_points[4].lon,json_points[4].lat]]
]).transform('EPSG:4326','EPSG:3857');

var feature_multilinestring = new Feature({
geometry: track1,
name: 'nom track 1'
});

var line1 = new LineString([
[json_points[5].lon,json_points[5].lat],[json_points[6].lon,json_points[6].lat]
]).transform('EPSG:4326','EPSG:3857');

var feature_linestring = new Feature({
geometry: line1,
name: 'nom línia 1'
});

var features_strings = new Array();

features_strings.push(feature_multilinestring)
features_strings.push(feature_linestring)

//icones
var features_icones = new Array();

let feature_icona1 = new Feature({
geometry: new Point(fromLonLat([json_points[5].lon,json_points[5].lat])),
name: 'Inici',
});

let feature_icona2 = new Feature({
geometry: new Point(fromLonLat([json_points[6].lon,json_points[6].lat])),
name: 'Final',
});

feature_icona1.setStyle(styles.icona); //no cal si defineixo el estil en el layer.
feature_icona2.setStyle(styles.icona); //però definiré l'estil en la icona si cada icona ha de tenir un estil diferent

features_icones.push(feature_icona1);
features_icones.push(feature_icona2);

// vectorSources
var vectorSourcePoints = new VectorSource({
features: features_points,
wrapX: false,
});

var vectorSourceStrings = new VectorSource({
features: features_strings,
wrapX: false,
});

var vectorSourceIcones = new VectorSource({
features: features_icones,
wrapX: false,
});

// layers
var layerPoints = new VectorLayer({
source: vectorSourcePoints,
style: styles.Point,
});

var layerStrings = new VectorLayer({
source: vectorSourceStrings,
style: styles.MultiLineString,
});

var layerIcones = new VectorLayer({
source: vectorSourceIcones,
style: styles.icona,
});

var map = new Map({
layers: [
new TileLayer({
source: new OSM({
layer: 'OSM'
})
}),
layerPoints,
layerStrings,
layerIcones
],
target: 'map',
view: new View({
center: fromLonLat([1.4234, 41.2344]),
zoom: 18
})
});