Arxiu de la categoria: Joanillo Productions

Millorar les fotos del mòbil

Coll de JouEs tracta d’aplicar un fitre com els que trobem a Instagram. Si vull automatitzar-ho, puc fer servir Imagemagick, que és una navalla suïssa per a la transformació d’imatges.

Al final he trobat la pàgina de fmwconcepts.com, que ofereix molts filtres en forma de script. A mi el que m’interessa i que em pot donar molt bon resultat és un filtre que s’anomena levels, que es tracta de modificar (fer un stretch) dels nivells, igual que faríem amb Gimp: Colors > Nivells. Amb el Gimp també em dóna un bon resultat fer Colors > Corbes, i veig que també hi ha el script (filtre) curbes. Per tant, també el podria provar, aquest.

Vull automatitzar el procés de generar imatges millorades de les imatges contingudes en una carpeta. Per tant, el meu script haurà d’anar imatge per imatge (primer bucle). I dins de cada imatge, fer un escombrat de paràmetres, de manera que per cada imatge generem 4 fotos. Finalment, haurem d’escollir quina és la foto que ha quedat millor, que ha quedat més contrastada i amb colors vius.

El script:

RUTA=/home/joan/projectes/OSM/marker_animation/filtre_levels
RUTAPICS=/home/joan/projectes/OSM/marker_animation/res/ribes-taga-ogassa-camprodon-ribes/prov
RUTARESULTATS=/home/joan/projectes/OSM/marker_animation/res/ribes-taga-ogassa-camprodon-ribes/prov/resultats
FILTRE=levels

clear
cd $RUTAPICS

for f in *.jpg
do
	cp $RUTAPICS/$f $RUTARESULTATS
	LEFT=0
	RIGHT=100
	START=0
	i=$START
	echo $f
	name=$(echo "$f" | cut -f 1 -d '.')
	for i in 0 1 2 3
	do
		LEFT=$(($LEFT+5))
		RIGHT=$(($RIGHT-5))
		i=$(($i+1))
		your_options="-C global -i $LEFT,$RIGHT $RUTAPICS/$f $RUTARESULTATS/$name-$i.jpg"
		FILTREAMBOPCIONS="levels $your_options"
		$RUTA/$FILTREAMBOPCIONS

	done
done

Més informació:

Simular el posicionament GPS (problemes en el desplegament al núvol, OVH)

Tenim una REST API amb MongoDB. Tenim un front-end per generar posicions i gravar-les a la bd. I tenim un altre front-end per visualitzar les posicions i veure com van evolucionant.

La part de generar les posicions i enviar-les al servidor de moment no és una aplicació mòbil, sinó que és una pàgina web. Openlayers té la possibilitat de fer geolocation, i per tant tenim una precisió molt bona (uns 15 m). No cal per tant una aplicació mòbil. Ara bé, s’ha de fer amb 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.

Fins aquí cap problema quan es desenvolupa localment (localhost). Però el problema està quan es desplega al servidor, en aquest cas un VPS a OVH. Per tant, la part de generar les posicions GPS ha d’anar al servidor OVH on hi ha instal·lat el certificat SSL. Ara bé, no es poden generar certificats per al domini ovh.net (es denega perquè s’han passat de la quota, lògic). Així doncs, primer m’he hagut de registrar a noip.com i ara tinc el domini jquintana.ddns.net.

Posar el MongoDB al servidor no ha tingut cap problema. El problema està en la API REST, feta amb Express. La API REST s’ha hagut de fer segura (https), doncs no podia ser que cridéssim a la API des de https, i que aquest servidor respongués amb http. Aquest ha sigut el problema principal.

Finalment la part del front-end de visualitzar les posicions no ha tingut especial problema. Les crides a la API http://localhost:3000 s’han de canviar a les crides a la API https://jquintana.ddns.net:8080.

En resum, tu pots tenir una aplicació funcionant amb localhost sense especials problemes, i quan la vols desplegar al núvol comencen els problemes de CORS, seguretat, protocols que no t’havies plantejat.

Ara l’últim pas és tenir un codi que funcioni igual de bé a localhost i al núvol, que això en principi no serà problema. I la reflexió és que quan desenvolupes un projecte, no s’ha d’esperar al final de tot a desplegar-lo a la infraestructura del núvol. S’ha de fer ja en les primeres fases: és el desplegament continu. Encara que l’aplicació no tingui moltes funcionalitats, ja s’ha pogut afrontar tots els problemes de seguretat i infraestructura al provar de desplegar-ho en l’entorn real en les primeres fases. I així ens estalviem problemes al final del projecte.

Programant el tracking de rutes

Exemple canònic OpenlayersAhir al vespre estava programant, i estava gravant posicions simulades des de casa. Com a casa no em moc, vaig simular un cert moviment. Les dades es graven en una base de dades MongoDB.

Avui estava continuant una mica, des de l’institut, i voilà, hi ha un salt de casa a l’institut. Lògic però divertit.

Openlayers: Point, LineString, MultiLineString, Icon. Exemple canònic

Exemple canònic OpenlayersAnem a mostrar un exemple mínim d’Openlauers que aclareixi els conceptes de dibuixar un punt, una línia, una multilínia i una icona. I com aplicar estils a aquests elements.

Definim primer aquests elements (punts, línies, multilínies, icones) a partir de les dades d’exemple (coordenades). Aleshores definim features a partir d’aquests elements, que afegim a un array de features. Cada feature pot tenir el seu estil.

Definim després el vectorSource a partir de l’array de features. Després definim el VectorLayer a partir del vectorSource. I finalment afegim al mapa els layers que volem pintar.

Aquest és el codi inicial i previ per a un petit projecte que vull fer, que tracta d’una aplicació mòbil per enviar les coordenades de posició a una RestAPI on hi ha una base de dades (MongoDB), i amb una aplicació web (front-end, que és la part que aquí es comença a explicar) poder visualitzar el recorregut del ciclista (la idea del projecte és poder fer el tracking d’un ciclista en una primera etapa; i en una etapa posterior poder fer el tracking de tots els ciclistes que participen en una cursa o sortida).

El codi 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
})
});

Curs MOOC ABP: Implementació del projecte a l’Aula

En aquesta part final del curs ens demanen que fem un Elevator pitch, que ve a ser un video promocional i motivador, per tal d’explicar als alumnes què farem en aquest projecte.
Comencem amb el títol del projecte, per tal de què quedi clar, i una pregunta motivadora que atraigui l’atenció dels alumnes. En el nostre cas, la pregunta seria: ”
Saps que per al 2025 s’obligarà als municipis de més de 20.000 habitants tenir Zones de Baixes Emissions. Saps quins són aquests municipis. Saps si el teu municipi haurà de complir aquesta ordenança?” Doncs si en el projecte volem aprendre a programar un mapa interactiu, ho hem de fer dins d’un context, en aquest cas saber quins són els municipis que hauran de complir la normativa de ZBE.

Hem d’ubicar el projecte dins de la programació del mòdul (cicle de DAW, mòdul M06: Javascript).

Hem de parlar de les fases. En el curs hem après que hem de fer 6 fases:

  • disseny i presentació del projecte
  • primeres activitats
  • execució del projecte
  • conclusions de l’alumnat
  • conclusions del docent
  • difusió

Finalment hem de parlar de com farem la difusió del projecte, i finalment com avaluarem aquest projecte.

I amb això ja està tot. Aquest curs s’acaba i crec que ja no ens faran fer cap més tasca. Ara només toca aplicar-ho a l’aula!

BDQuest: Quests per al llenguatge SQL

Aquest any faig M02 a 1r de DAM (Bases de Dades). Sempre he treballat amb bases de dades, amb els alumnes, però aquesta assignatura feia molt temps que no la feia.

Em vaig animar a l’estiu de fer una eina web per tal de què els alumnes poguessin testejar les seves sentències SQL de forma automàtica, i que també fos un recurs per avaluar i auto-avaluar. Aquesta aplicació és BDQuest, el nom no és molt original, però a aquestes altures ja no m’atraveixo a canviar-lo.

Vam començar el curs i a la UF1 no es fa res de SQL. Però a la UF2, que és la troncal del curs i que ja estem acabant, els alumnes han estat utilitzant l’eina, per tal de fer selects bàsiques, inserts, updates, deletes, create, alter i drop tables, create i drop views, selects complexes, etc.

Els alumnes han fet de conillets d’índies i la versió 10, que és l’actual, l’he anat actualitzant al llarg del trimestre. I funciona molt bé!

I ara que estem acabant la UF2 i falta res per començar la UF3, he adaptat l’aplicatiu per poder fer create i drop users, grant privilegis, etc; i també create procedures, etc. I els triggers ja vindran. És a dir, que l’objectiu inicial de què no només servís per fer selects s’haurà acomplert amb escreix.

Fins ara no havia escrit res ni aquí ni a la wiki. Ja és hora de documentar una mica el projecte i donar-lo a conèixer. A veure si algú en pot treure profit.

A l’aplicació encara li falten moltes coses, però allò important és que aquest any ha sigut funcional. És una típica aplicació web amb molt de Javascript i PHP-MySQL, amb un disseny de moment pelat. L’aplicactiu hauria de créixer per aquí: millorar el disseny; i utilitzar algun framework en el cantó del client i del servidor. I també alguna refactorització del codi i millora d’unes quantes coses.

CatGeoQuest: aplicació per fer tests de geografia

En això estic aquests darrers dies. M’he donat pressa per poder fer una demo que es pugui ensenyar i que ja sigui jugable.

Tinc moltes idees al voltant de l’aplicació, pot créixer bastant. El que encara no tinc clar és si li posaré una capa de back-end amb bases de dades, o bé ho deixaré com una SPA (Single Page Application) amb només front-end i Javascript.

He intentat treure el màxim profit de la llibreria de Openlayers, com sempre, m’ha donat algun mal de cap però també he après bastant pel camí. Ara que ja tinc una versió jugable, el camí per continuar serà més plàcid i tranquil…

Geometria per acabar l’any

Per acabar l’any, el repte de fer i entendre una demostració de geometria.
El problema: donat un polígon regular de N costats (per exemple un heptàgon), demostrar que els angles que formen les diagonals que surten d’un vèrtex, tenen tots aquests angles el mateix valor, i dir quin valor és.

La demostració del repte està en el pdf de sota. No és una demostració evident, és un d’aquells casos en què l’enunciat és molt fàcil, penses que la demostració serà ràpida i fàcil, i després no ho és. En aquest cas, he hagut d’aprofundir en els quadrilàters inscrits en una circumferència (que també s’anomenen cíclics), i les seves propietats: concretament, en un polígon inscrit els angles oposats són suplementaris (sumen 180º).

Per fer la demostració és necessària una cadena de resultats i demostracions prèvies, algunes evidents, i d’altres no tant.

Bon any 2022 i moltes matemàtiques per al nou any!

La demostració: demostració de què en un polígon regular les diagonals que parteixen d’un vèrtex tenen el mateix angle: 180/N.

rutesgps, versió 16

Aquí una nova versió de rutesgps.joanillo.org, implementant unes poques opcions que tenia ganes de fer:

  • Cada excursió té una url que la identifica. Per exemple, la última ruta que he fet: Igualada-Calaf-Manresa-Igualada: igualada-manresa-igualada
  • Un botonet per copiar directament l’enllaç de l’excursió.
  • He afegit dos markers, per tal de què quedi clar on comença la ruta i on acaba

Referències:

Geometria computacional

Fa un parell d’anys vaig estar treballant amb el TSP (Travelling salesman problem), i vaig fer una ruta visitant arbres singulars de Barcelona. La unió dels arbres es feia amb línies rectes.

Partint d’aquella idea, vull fer un camí que uneixi tots els punts, però amb suavitat. Per això es fa servir la interpolació (com les cubic-splines, per exemple). Vaig amb la idea de fer dibuixos d’un sol traç (single line drawing, dibuixos que tessel·len tota una superfície amb suavitat. Hi ha artistes que estan fent dibuixo seguint aquesta idea. I això es podria traslladar a la CNC per fer coses xules i divertides.