Jessica Sena
Software engineer. Design and development of web and mobile applications
#TheStreetsOfWomen
@jsenag
github.com/jessisena
Computer Engineer Posgraduate UX
2. Process data
3. Generate the map
1. Extract data
GOAL:
To create a file with ONLY the STREETS of a city
Tools:
Tile Calculator - OSM QA Tiles - TileReduce - Overpass API
Implements MapReduce
(parallel computing)
Distributed spatial analysis
JavaScript + Mbtiles
Manages tiles Manages partial results + the final one
"GEO" version of
"MapReduce"
reduce.js
Exects operations and analisys
map.js
Tile
resultt
BBOX definition and MBTILES path
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){
(...)
});
Generate the final Geojson including only streets
module.exports = function(tileLayers, tile, writeData, done){
const osmRoads = cleanGeoms(normalize(flatten(clip(tileLayers.osm.osm, tile))));
done(null, osmRoads);
};
STEP 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
);
}
STEP 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
SOLUTION:
CLIP!
Streets out
of boundaries
CLIP:
OSM ID
Geojson Boundary
flatten + booleanContains
Streets
out inside boundaries
"Clean" name for the classification
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.'
];
of street prefixes
languages!
FINAL RESULT:
GOALS:
SOLUTION 1:
Use limitations
Slow (1request/name)
Parse "Name + Surname"
API
SOLUTION 2:
CSV list (source INE)
+25.000 classified names
Local Dictionary
github.com/marcboquet/spanish-names
Load the dictionaries in memory
Take the "clean name" column in CSV
Jacint (Verdaguer)
Does it appear in the women's dictionary?
Does it appear in the men's dictionary?
NO
YES
is a WOMEN
YES
is a MAN
NO
Discard the street
new Set();
SOLUTION 1:
Slow (request/street)
Complex results parse
MediaWiki API
SOLUTION 2:
Wikidata Wikiquote Wikinoticas Wikisource
68.000 resources
68.000 resources
https://fusejs.io/
SOLUTION 2:
Streets named after a person
F/M Classification
Wikipedia links
nombreciudad_streets.geojson
How?
Generate FINAL GEOSJON
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]);
}
}
Generate FINAL GEOSJON
Generata
STATS
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++;
}
}
(*known errors)
this.map = new mapboxgl.Map({
container: 'map',
style,
center: initCenter, // [lng, lat]
zoom: 1,
});
Create map object (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,
],
},
});
Add line layer:
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'],
});
Add polygon layer:
How to add a new city?
Scripts results files
github.com/geochicasosm/
Intrucciones en README.md
Still a lot of room
for improvement!
Prepare the project to accept other languages
Improve de criteria to remove or keep streets:
Define a women's taxonomy:
Analyze the streets assignation:
Revert data:
How to properly scale the project:
Improve data analysis:
Find a better way to find the wikipedia links:
Define a template for a social campaign to change the street names:
PUT YOUR IDEA HERE
https://geochicasosm.github.io/lascallesdelasmujeres/ https://github.com/geochicasosm
@geochicasosm
www.geochicas.org
By Jessica Sena
#LasCallesDeLasMujeres is a project of 'GeoChicasOSM', emerged during the 8M of 2018. The talk explains the technical process carried out for its realisation, from the collection and data processing, to the elaboration of the map for its visualisation, emphasising the technical problems and limitations found, and how they have been solved.
Software engineer. Design and development of web and mobile applications