Jessica Sena
Software engineer. Design and development of web and mobile applications
#LasCallesDeZaragoza
@jsenag
github.com/jessisena
Ingeniera informática Posgraduada UX
Para conseguirlo:
Crear un mapa mundial.
Representar las calles con nombres de mujer y de hombre.
Enlazar cada calle de mujer con su artículo en Wikipedia
Identificar quiénes no tienen entrada
2. Tratamiento de datos
3. Generar el visor
1. Extracción de datos
OBJETIVO:
Obtener fichero con SÓLO las CALLES de una ciudad
Herramientas:
Tile Calculator - OSM QA Tiles - TileReduce - Overpass API
Implementa MapReduce
(computación paralela)
Análisis geoespacial distribuido
JavaScript + Mbtiles
Gestión de Tiles Gestión resultados parciales y final
"MapReduce" versión "GEO"
reduce.js
Ejecución de operaciones y análisis
map.js
Tile
resultado
Definir BBOX y path a MBTILES
reduce.js
const opts = {
bbox: [-84.12, 9.9, -84.02, 9.96],
log: true,
zoom: 12,
sources: [
{
name: 'osm',
mbtiles: path.join(__dirname, 'data/latest.planet.mbtiles')
}
],
maxWorkers: 4,
map: __dirname+'/map.js'
};
.on('reduce', function(result, tile){
num++;
finalGeojson.features = finalGeojson.features.concat(result.features) ;
})
.on('end', function(error){
(...)
});
Generar Geojson final solo con calles
module.exports = function(tileLayers, tile, writeData, done){
const osmRoads = cleanGeoms(normalize(flatten(clip(tileLayers.osm.osm, tile))));
done(null, osmRoads);
};
PASO 1: Clip + flatten + normalize
Turf.js
+
@mapbox/Tilebelt
map.js
geojson-flatten
+
geojson-normalize
PASO 2: CleanGeoms
LineString MultiLineString
Polygon
module.exports = function(tileLayers, tile, writeData, done){
const osmRoads = cleanGeoms(normalize(flatten(clip(tileLayers.osm.osm, tile))));
done(null, osmRoads);
};
map.js
PASO 2: CleanGeoms
Polygon
module.exports = function(tileLayers, tile, writeData, done){
const osmRoads = cleanGeoms(normalize(flatten(clip(tileLayers.osm.osm, tile))));
done(null, osmRoads);
};
map.js
FIXED!
function isValidSquare(geom) {
return (
geom.geometry.type === 'Polygon' &&
geom.properties.highway &&
geom.properties.highway === 'pedestrian'&&
geom.properties.area &&
geom.properties.area === 'yes'
&& geom.properties.name !== undefined
);
}
PASO 2: CleanGeoms
module.exports = function(tileLayers, tile, writeData, done){
var osmRoads = cleanGeoms(normalize(flatten(clip(tileLayers.osm.osm, tile))));
done(null, osmRoads);
};
map.js
PASO 2: CleanGeoms
geom.properties = {
name: geom.properties.name,
id: geom.properties['@id'],
wikipedia_link: '',
gender: 'unknown',
scale: ''
};
module.exports = function(tileLayers, tile, writeData, done){
var osmRoads = cleanGeoms(normalize(flatten(clip(tileLayers.osm.osm, tile))));
done(null, osmRoads);
};
map.js
SOLUCIÓN:
CLIP!
Calles fuera
del límite
CLIP:
OSM ID
Geojson Boundary
flatten + booleanContains
Calles fuera
dentro del límite
Nombre "limpio" para clasificación
const filterList = ['Paseo','Río', 'Avenida', 'Hacienda', 'Puerto', 'Callejón', 'Calle', 'Calzada', 'Camino', 'Av.','Paso', 'Cañada', 'Minas', 'Cerrada',
'Puebla', 'Principal', 'Central','Primera', 'Segunda', 'Portón', 'Lateral', 'Calz.', 'Corrido', 'Casa', 'Villa', 'Mejía',
'Vía', 'Via', 'Real', 'Isla', 'Avendida', 'Marisma', 'Rada', 'Raudal', 'Ribera', 'Embocadura', 'Cataratas', 'Médanos',
'Mirador', 'Av', 'Jardín', 'A.', 'Circuito','Gral.', 'Rincón', 'Calz', 'Rinconada', 'Periférico', 'Cda', 'Jardin',
'C.', 'Callejon', 'Colegio', 'Valle', 'avenida', 'camino', 'calle', 'Calle', 'Rotonda', 'Parqueo', 'Parque', 'entrada',
'Entrada', 'sendero', 'Sendero', 'Pasaje', 'pasaje', 'Puerto', 'Ciudad', 'Puente', 'Boulevard', 'Agrosuperior', 'Bodegas',
'Autobanco', 'SkyTrace', 'Plaza', 'Motel', 'C/', 'Rotonda', 'Drive', 'Residencial', 'Automac',
'Auto', 'Transcersal', 'Inter', 'Pasillo', 'Centro', 'Caminito', 'Arandas', 'Proveedores', 'Cajero', 'Zona', 'Primer', 'Res.'
];
prefijos de tipos calles
idiomas!
RESULTADO FINAL:
OBJETIVOS:
SOLUCIÓN 1:
Limitaciones de uso
Lento (petición/nombre)
Parseo "Nombre + Apellido"
API
SOLUCIÓN 2:
Listado CSV (fuente INE)
+25.000 nombres clasificados
Diccionario local
github.com/marcboquet/spanish-names
Cargar en memoria diccionarios
Coger nombre "limpio de CSV"
Jacint (Verdaguer)
Aparece en diccionario nombres mujer?
Aparece en diccionario nombres hombre?
NO
SI
es MUJER
SI
es HOMBRE
NO
Descartamos calle
new Set();
SOLUCIÓN 1:
Lenta (petición/calle)
Parseo resultado complejo
API de MediaWiki
SOLUCIÓN 2:
Wikidata Wikiquote Wikinoticas Wikisource
68.000 entradas
SOLUCIÓN 2:
68.000 entradas
https://fusejs.io/
Las calles son de persona
Clasificación F/M
Enlaces a Wikipedia
nombreciudad_streets.geojson
¿Cómo?
Generar
GEOSJON FINAL
Generar
GEOSJON FINAL
const streeMap = new Map();
(...)
streeMap.set(splitLine[0], {
url: url,
gender: splitLine[4],
scale: ""
});
var finalGeojson = {
"type": "FeatureCollection",
"features": []
};
for (var key in geojson.features) {
if(streeMap.has(geojson.features[key].properties.name)){
(...)
finalGeojson.features.push(geojson.features[key]);
}
}
Generar
ESTADÍSTICAS
if(!tratadosList.has(geojson.features[key].properties.name)){
tratadosList.add(geojson.features[key].properties.name);
if (objValues.url !== ('' && null && undefined) && objValues.gender.toLowerCase() === FEMALE){
stats.numLink++;
stats.numFemale++;
}else if (objValues.gender.toLowerCase() === FEMALE){
stats.numFemale++;
stats.numNoLink++;
noLinkList += `\n${geojson.features[key].properties.name}`;
}else{
stats.numMale++;
}
}
Problemas/errores conocidos.
this.map = new mapboxgl.Map({
container: 'map',
style,
center: initCenter, // [lng, lat]
zoom: 1,
});
Crear objeto mapa (MyMap.js)
map.addLayer({
id: `${sourcename}-line`,
type: 'line',
source: {
type: 'geojson',
data: geojson,
},
layout: {
'line-join': 'round',
'line-cap': 'round',
},
paint: {
'line-blur': ['case', ['==', ['get', 'wikipedia_link'], ''], 4, 1],
'line-color': [
'case',
['==', ['get', 'gender'], FEMALE],
'#ffca3a',
'#00B99E',
],
'line-width': [
'case',
['==', ['get', 'gender'], FEMALE],
widthFemale,
widthMale,
],
},
});
Añadir capa líneas:
map.addLayer({
id: `${sourcename}-fill`,
type: 'fill',
source: {
type: 'geojson',
data: geojson,
},
layout: {},
paint: {
'fill-color': ['case', ['==', ['get', 'gender'], FEMALE], '#ffca3a', '#00B99E'],
'fill-opacity': ['case', ['==', ['get', 'wikipedia_link'], ''], 0.2, 0.6],
},
filter: ['==', '$type', 'Polygon'],
});
Añadir capa polígonos:
Cómo añadir una nueva ciudad?
Ficheros resultado scripts
github.com/geochicasosm/
Intrucciones en README.md
https://geochicasosm.github.io/lascallesdelasmujeres/ https://github.com/geochicasosm
@geochicasosm
www.geochicas.org
Jessica Sena - @jsenag
By Jessica Sena
Las #CallesDeLasMujeres es un proyecto de ‘GeoChicasOSM’, que surgió en el marco del 8M de 2018. En la charla se explica el proceso técnico llevado a cabo para su realización, desde la obtención y tratamiento de los datos, hasta la elaboración del mapa para su visualización, haciendo hincapié en los problemas técnicos y limitaciones encontradas, y cómo se han solventado.
Software engineer. Design and development of web and mobile applications