IPython par l'exemple dans le cadre des données cartographiques (principalement)

Thomas Gratier - 18/10/2014 - WebGeoDataVore http://webgeodatavore.com

Ipython est défini sur Wikipédia comme "un terminal interactif, ou shell, pour le langage de programmation Python qui propose des fonctionnalités telles que l'introspection, une syntaxe additionnelle, la complétion et un historique riche"

Introduction

IPython permet de manière interactive de manipuler Python d'une manière beaucoup plus dynamique les modules comme les données que la console Python par défaut.

3 versions à retenir:

  • Ligne de commande
  • avec Notebook (qui inclue un serveur web et permet de manipuler Python depuis le navigateur)
  • avec QT (client lourd)

Installer IPython (avec le notebook

Sous Windows (non recommandé, vous allez avoir des problèmes sur des besoins avancés à cause de modules en C)

Sous Linux comme Mac

On suppose que vous savez utiliser pip, il faut faire en ligne de commande

pip install ipython[all]

Les trucs sympas

La liste des commandes magiques

Les commandes qui font que le IPython, c'est bon

In [17]:
%quickref
In [41]:
%%file filename.txt
Hello World!
Writing filename.txt

In [42]:
!cat filename.txt
Hello World!
In [2]:
%load filename.txt
In [3]:
Hello World!
  File "<ipython-input-3-8ddd8be4b179>", line 1
    Hello World!
              ^
SyntaxError: invalid syntax

Partager ou découvrir des notebooks

Le principe: si vous trouver un fichier de type ipynb (l'extension des notebooks) sur Github, vous pouvez voir le résultat qu'il donne quand il est exécuté http://nbviewer.ipython.org/github/ipython/ipython/blob/1.x/examples/notebooks/Part%205%20-%20Rich%20Display%20System.ipynb

Attention, les exemples avec bibliothèques exotiques ou avec rendus web passeront incognito.

Visualiser et analyser : différentes phases

Analyser comme visualiser de la donnée implique plusieurs phases :

  • Acquérir de la donnée
  • La transformer (formats)
  • L'analyser
  • Visualiser

Acquérir

Depuis des données publiques

Où chercher?

In [4]:
# Exemple de données des stations météo (source empruntée à Mathieu Leplatre)
#!wget ftp://ftp.wmo.ch/wmo-ddbs/VolA_New/Pub9volA141110x.flatfile


# Ouverture du navigateur sur une des pages de données du portail OpenData de Nantes
# "Localisation et caractéristiques des colonnes d'apport volontaire aériennes"
import webbrowser
webbrowser.open('http://data.nantes.fr/donnees/detail/localisation-des-colonnes-aeriennes-de-nantes-metropole/', new=2)
Out[4]:
True

Depuis d'autres sources

Grâce à des adresses ou depuis des coordonnées:

  • OpenStreetMap Nominatim
  • ESRI ArcGIS
  • Google Geocoding API (V3)
  • Baidu Maps
  • Bing Maps API
  • Yahoo! PlaceFinder
  • GeoNames
  • MapQuest
  • OpenMapQuest
  • OpenCage
  • SmartyStreets
  • geocoder.us
  • GeocodeFarm
In [7]:
# Geocodage depuis une adresse
from geopy.geocoders import Nominatim
geolocator = Nominatim()
location = geolocator.geocode("11 Impasse Juton, Nantes")
print(location.address)
print(location.latitude, location.longitude)
print(location.raw)

# Reverse geocoding
location = geolocator.reverse("47.21614, -1.54981")
print(location.address)
print(location.latitude, location.longitude)
print(location.raw)
11, Impasse Juton, Madeleine, Pôle de proximité Nantes-Loire, Nantes, Loire-Atlantique, Pays de la Loire, France métropolitaine, 44000;44100;44200;44300, France
(47.2118156, -1.5514692)
{u'display_name': u'11, Impasse Juton, Madeleine, P\xf4le de proximit\xe9 Nantes-Loire, Nantes, Loire-Atlantique, Pays de la Loire, France m\xe9tropolitaine, 44000;44100;44200;44300, France', u'importance': 0.421, u'place_id': u'25900717', u'lon': u'-1.5514692', u'lat': u'47.2118156', u'osm_type': u'node', u'licence': u'Data \xa9 OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright', u'osm_id': u'2445377878', u'boundingbox': [u'47.2117656', u'47.2118656', u'-1.5515192', u'-1.5514192'], u'type': u'house', u'class': u'place'}
Château des Ducs de Bretagne, 4, Place Marc Elder, Cathédrale, Pôle de proximité Nantes-Loire, Nantes, Loire-Atlantique, Pays de la Loire, France métropolitaine, 44000, France
(47.21609465, -1.54932117592013)
{u'display_name': u'Ch\xe2teau des Ducs de Bretagne, 4, Place Marc Elder, Cath\xe9drale, P\xf4le de proximit\xe9 Nantes-Loire, Nantes, Loire-Atlantique, Pays de la Loire, France m\xe9tropolitaine, 44000, France', u'place_id': u'103043416', u'lon': u'-1.54932117592013', u'osm_type': u'way', u'licence': u'Data \xa9 OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright', u'osm_id': u'142927223', u'lat': u'47.21609465', u'address': {u'city': u'Nantes', u'house_number': u'4', u'country': u'France', u'county': u'Nantes', u'pedestrian': u'Place Marc Elder', u'state': u'Pays de la Loire', u'city_district': u'P\xf4le de proximit\xe9 Nantes-Loire', u'postcode': u'44000', u'country_code': u'fr', u'castle': u'Ch\xe2teau des Ducs de Bretagne', u'neighbourhood': u'Cath\xe9drale'}}

Grâce à de l'aspiration de sites avec du WebScrapping (attention, limite légale à la pratique)

BeautifulSoup

In [13]:
from BeautifulSoup import BeautifulSoup
import requests

r = requests.get('http://leafletjs.com/plugins.html')
soup = BeautifulSoup(r.content)
tables = soup.findAll('table')
for table in tables:
    trs = table.findAll('tr')
    for i,row in enumerate(trs):
        if i:
            print i,row.findAll('td')[0].text
 1 Leaflet.FreeDraw
2 Leaflet.ellipse
3 Leaflet.plotter
4 Leaflet.markercluster
5 Leaflet.label
6 RaphaelLayer
7 Overlapping Marker Spiderfier
8 TileLayer.BoundaryCanvas
9 MaskCanvas
10 HeatCanvas
11 heatmap.js
12 Leaflet divHeatmap
13 WebGL Heatmap
14 Leaflet.MultiTileLayer
15 Leaflet.AnimatedMarker
16 Leaflet-semicircle
17 Leaflet.FunctionalTileLayer
18 Leaflet.geoCSV
19 Leaflet.PolylineDecorator
20 Leaflet.Sprite
21 Leaflet.BounceMarker
22 Leaflet.TextPath
23 Leaflet.Awesome-Markers
24 Leaflet Data Visualization Framework
25 TileLayer.Grayscale
26 TileLayer.GeoJSON
27 TileLayer.Zoomify
28 TileLayer.DeepZoom
29 Leaflet.Graticule
30 Leaflet.SimpleGraticule
31 leaflet-usermarker
32 Leaflet.EdgeMarker
33 Leaflet.Shapefile
34 Leaflet.FileGDB
35 Leaflet.Terminator
36 Leaflet.FeatureSelect
37 Leaflet.MakiMarkers
38 Leaflet.Editable.Polyline
39 leaflet.TileLayer.WMTS
40 leaflet-omnivore
41 Leaflet.TileLayer.IIP
42 Leaflet.ImageTransform
43 PruneCluster
44 Leaflet.Indoor
45 Leaflet.Geodesic
46 Leaflet.LineExtremities
47 Leaflet.VectorMarkers
48 Leaflet.MovingMarker
49 Leaflet Realtime
1 Proj4Leaflet
2 Plugins by Pavel Shramov
3 cartodb-leaflet
4 Leaflet Vector Layers
5 leaflet-tilejson
6 leaflet-providers
7 azgs-leaflet
8 Leaflet.encoded
9 Leaflet.Pouch
10 Leaflet Ajax
11 Leaflet GPX
12 Wicket
13 Leaflet.dbpediaLayer
14 Leaflet-2gis
15 Leaflet.KoreanTmsProviders
16 Leaflet.ChineseTmsProviders
17 Esri Leaflet
18 Leaflet.geojsonCSS
19 leaflet.wms
1 Leaflet GeoSearch
2 Leaflet Control OSM Geocoder
3 Leaflet Control Bing Geocoder
4 Leaflet Control Geocoder
5 Leaflet GeoIP Locator
6 Esri Leaflet Geocoder
7 Leaflet.OpenCage.Search
1 Leaflet Routing Machine
2 Leaflet.Routing
3 Route360°
1 Leaflet.AreaSelect
2 Leaflet.draw
3 Leaflet.utfgrid
4 L.EasyButton
5 Leaflet.EditableHandlers
6 L.LocationShare
7 Leaflet.Pancontrol
8 Leaflet.zoomslider
9 Leaflet.Locate
10 Leaflet.fullscreen
11 leaflet.zoomfs
12 leaflet.fullscreen
13 leaflet-search
14 leaflet-fusesearch
15 leaflet-locationfilter
16 Leaflet.MiniMap
17 Leaflet.Rrose
18 Leaflet.EditInOSM
19 Leaflet.Spin
20 Leaflet.RestoreView
21 Leaflet.FileLayer
22 Leaflet.Snap
23 Leaflet Time-Slider
24 Leaflet.RevealOSM
25 Leaflet.MousePosition
26 Leaflet.SelectLayers
27 Leaflet.StyledLayerControl
28 Leaflet.Coordinates
29 Leaflet.Elevation
30 Leaflet.Sync
31 Leaflet.GroupedLayerControl
32 Leaflet.BorderPan
33 Leaflet.TileLegend
34 LeafletPlayback
35 Leaflet.loading
36 Leaflet.viewcenter
37 Leaflet.contextmenu
38 Leaflet.MeasureControl
39 Leaflet.OverIntent
40 Leaflet.AlmostOver
41 Leaflet Control Order Layers
42 Leaflet.layerscontrol-minimap
43 leaflet-sidebar
44 sidebar-v2
45 leaflet-zoom-min
46 Leaflet.MagnifyingGlass
47 Leaflet.OpacityControls
48 Leaflet.StyleEditor
49 Leaflet.UniformControl
50 Leaflet.SimpleMarkers
51 Leaflet Panel Layers
52 Leaflet Categorized Layers
53 Leaflet Navigation Toolbar
54 Leaflet LimitZoom
55 Leaflet Coordinates Control
56 Leaflet GameController
57 Leaflet.MeasureAreaControl
58 Leaflet.twofingerZoom
59 Leaflet Control Compass
60 Leaflet.Editable
61 Leaflet.AccuratePosition
62 Leaflet Locationlist
63 Leaflet.defaultextent
64 Leaflet.buffer
65 Leaflet.Bookmarks
66 Leaflet.MapPaint
67 Leaflet.ShowAll
1 OSM Buildings
2 Maps Marker Pro
3 leaflet-hash
4 arc.js
5 Leaflet.Storage
6 Leaflet.CSS
7 Leaflet.LayerIndex
8 Leaflet-pip
9 Leaflet.GeometryUtil
10 Leaflet.i18n
11 Leaflet.print
12 Leaflet-easyPrint
13 Leaflet-active-area
14 WordPress Leaflet Map
15 MapBBCode-related leaflet plugins
16 Leaflet Yeoman Generator
17 Leaflet LayerConfig
18 Leaflet ZoomLevel CSS Class
19 Greiner-Hormann

Transformer / stocker

Spécial carto

Génériques mais pouvant être utilisé avec de la cartographie ou bien seuls

OGR / GDAL

In [14]:
# OGR2OGR (utilitaire en ligne de commande) depuis Ipython
!rm voronoi_stations.geojson
!ogr2ogr -f "GeoJSON" -lco ENCODING=UTF-8 voronoi_stations.geojson voronoi_stations.shp

Fiona

  • Surcouche à OGR, la partie vecteur de OGR / GDAL, la bibliothèque de manipulation de formats de données géographiques
In [15]:
import fiona
import json
import logging
from pyproj import Proj, transform

features = []
wgs84=Proj("+init=EPSG:4326")
with fiona.collection("COLONNES_AERIENNES_NM.shp", "r") as source:
    p_in = Proj(source.crs)
    for feat in source:
        try:
            assert feat['geometry']['type'] == "Point"
            new_coords = []
            feat['geometry']['coordinates'] = transform(p_in, wgs84, *feat['geometry']['coordinates'])
            features.append(feat)
        except Exception, e:
                logging.exception("Error processing feature %s:", feat['id'])

my_layer = {
    "type": "FeatureCollection",
    "features": features
}

with open("colonnes_aeriennes.geojson", "w") as f:
    f.write(json.dumps(my_layer))

Rasterio

Nous le mentionnons mais il est très spécifique.

Il est principalement destiné aux traitements d'images:

*il manipule des bandes

  • permet de déformer des images pour de la reprojection
  • fait aussi de la conversion de formats
  • peut être converti en array NumPy: tous les processus de traitement d'images de NumPy peuvent être utilisés

Son avantage principal: moins de "boilerplate" qu'avec GDAL

Shapely

C'est un module en Python basé sur Geos, une bibliothèque C++ dont la fonction principale est de pouvoir effectuer des opérations spatiales.

Quand on entend opération spatiale, il faut comprendre que dans les SIG, trois éléments de base:

  • le point,
  • la ligne,
  • le polygone

Selon le cas, on a:

  • besoin de construire des géométries
  • créer des nouvelles géométries (par croisement)
  • vérifier des relations entre des objets géométriques.
In [2]:
## Enable embedding Matplotlib in IPython
%matplotlib inline

# From http://py2gis.blogspot.com/2010/10/import-polygon-by-numpy.html
from osgeo import ogr
from matplotlib import pyplot
from numpy import asarray
from shapely.wkb import loads

#plot polygon only
source = ogr.Open("voronoi_stations.shp")
layer = source.GetLayerByName("voronoi_stations")
#print layer
fig = pyplot.figure(1, figsize=(8,4), dpi=150)
ax = fig.add_subplot(111)
def plot_poly(g):
    a = asarray(g.exterior)
    pyplot.plot(a[:,0], a[:,1])
while 1:
    feature = layer.GetNextFeature()
    if not feature:
        break
    geom = loads(feature.GetGeometryRef().ExportToWkb())
    #print geom
    if geom.geom_type == 'MultiPolygon':
        for g in geom:
            plot_poly(g)
    if geom.geom_type == 'Polygon':
        plot_poly(geom)
#pyplot.show()

Pandas

Pivot principal pour lire et écrire des données multidimensionnelles. Il inclue par exemple la gestion des séries temporelles. Vous pouvez penser "cube de données". Il se base sur des "data frames", un concept qui vient de R, un language orienté vers les statistiques.

Il fonctionne très bien en s'articulant avec des fichiers à plats, HDF5 comme des bases de données.

Pour la partie cartographie, il a une extension dénommée GeoPandas qui s'appuie principalement sur Fiona et Shapely. Pour ceux qui ont des besoins cartographiques alors qu'ils viennent plutôt du monde du calcul numérique, cela évite de repartir de zéro.

Analyser

Attention, même si nous inventorions une bonne partie des outils, notre métier nous amène rarement à dépasser de l'analyse descriptive. Si nous allons au delà avec par exemple de l'analyse spatiale, nous avons tendance à utiliser d'autres outils plus avancés (par exemple des bindings Python sur des logiciels SIG bureautiques genre QGIS ou Grass) ou bien à passer sur PostGIS (la cartouche spatiale du SGBD PostgreSQL)

Spécial cartographie

Composant quasi agnostique vis à vis des cartes

Visualiser

Trois sorties principales:

Matplotlib

Déjà vu conjointement avec Shapely

Un exemple ci-dessous dans un contexte non cartographique mais fortement lié car basé sur la démographie (tiré de https://commons.wikimedia.org/wiki/File:Iran-age-pyramid.svg)

In [51]:
# coding: utf-8
#!/usr/bin/env python
import matplotlib.pyplot as plt
import numpy as np
 
# first column is total number of people
# second column is percentage of the population
data = np.array([
[6232552, 8.29],
[5657791, 7.53],
[5671435, 7.55],
[6607043, 8.79],
[8414497, 11.20],
[8672654, 11.54],
[6971924, 9.28],
[5571018, 7.41],
[4906749, 6.53],
[4030481, 5.36],
[3527408, 4.69],
[2680119, 3.57],
[1862907, 2.48],
[1343731, 1.79],
[1119968, 1.49],
[913531, 1.22],
[919539, 1.22]])
 
labels = [
'0-4',
'5-9',
'10-14',
'15-19',
'20-24',
'25-29',
'30-34',
'35-39',
'40-44',
'45-49',
'50-54',
'55-59',
'60-64',
'65-69',
'70-74',
'75-79',
'80 and over']
 
# Prepare axis
fig = plt.figure(figsize=(9, 5))
ax = fig.add_subplot(111)
 
y_pos = np.arange(len(labels))
percentages = data[:, 1]
 
ax.barh(y_pos, percentages, align='center')
plt.yticks(y_pos, labels)
plt.axis('tight')
 
plt.title('Iranian Demography 2011')
plt.xlabel('Percentage')
plt.savefig('iran-demography.svg', transparent=True)
In [55]:
# Avec NetworkX, on génère une image
!python napoleon_russian_campaign.py
In [56]:
!cat napoleon_russian_campaign.py
#!/usr/bin/env python
"""
Minard's data from Napoleon's 1812-1813  Russian Campaign.
http://www.math.yorku.ca/SCS/Gallery/minard/minard.txt

"""
__author__ = """Aric Hagberg (hagberg@lanl.gov)"""
#    Copyright (C) 2006 by 
#    Aric Hagberg <hagberg@lanl.gov>
#    Dan Schult <dschult@colgate.edu>
#    Pieter Swart <swart@lanl.gov>
#    All rights reserved.
#    BSD license.

import string
import networkx as nx


def minard_graph():
    data1="""\
24.0,54.9,340000,A,1
24.5,55.0,340000,A,1
25.5,54.5,340000,A,1
26.0,54.7,320000,A,1
27.0,54.8,300000,A,1
28.0,54.9,280000,A,1
28.5,55.0,240000,A,1
29.0,55.1,210000,A,1
30.0,55.2,180000,A,1
30.3,55.3,175000,A,1
32.0,54.8,145000,A,1
33.2,54.9,140000,A,1
34.4,55.5,127100,A,1
35.5,55.4,100000,A,1
36.0,55.5,100000,A,1
37.6,55.8,100000,A,1
37.7,55.7,100000,R,1
37.5,55.7,98000,R,1
37.0,55.0,97000,R,1
36.8,55.0,96000,R,1
35.4,55.3,87000,R,1
34.3,55.2,55000,R,1
33.3,54.8,37000,R,1
32.0,54.6,24000,R,1
30.4,54.4,20000,R,1
29.2,54.3,20000,R,1
28.5,54.2,20000,R,1
28.3,54.3,20000,R,1
27.5,54.5,20000,R,1
26.8,54.3,12000,R,1
26.4,54.4,14000,R,1
25.0,54.4,8000,R,1
24.4,54.4,4000,R,1
24.2,54.4,4000,R,1
24.1,54.4,4000,R,1"""
    data2="""\
24.0,55.1,60000,A,2
24.5,55.2,60000,A,2
25.5,54.7,60000,A,2
26.6,55.7,40000,A,2
27.4,55.6,33000,A,2
28.7,55.5,33000,R,2
29.2,54.2,30000,R,2
28.5,54.1,30000,R,2
28.3,54.2,28000,R,2"""
    data3="""\
24.0,55.2,22000,A,3
24.5,55.3,22000,A,3
24.6,55.8,6000,A,3
24.6,55.8,6000,R,3
24.2,54.4,6000,R,3
24.1,54.4,6000,R,3"""
    cities="""\
24.0,55.0,Kowno
25.3,54.7,Wilna
26.4,54.4,Smorgoni
26.8,54.3,Moiodexno
27.7,55.2,Gloubokoe
27.6,53.9,Minsk
28.5,54.3,Studienska
28.7,55.5,Polotzk
29.2,54.4,Bobr
30.2,55.3,Witebsk
30.4,54.5,Orscha
30.4,53.9,Mohilow
32.0,54.8,Smolensk
33.2,54.9,Dorogobouge
34.3,55.2,Wixma
34.4,55.5,Chjat
36.0,55.5,Mojaisk
37.6,55.8,Moscou
36.6,55.3,Tarantino
36.5,55.0,Malo-Jarosewii"""

    c={}
    for line in cities.split('\n'):
        x,y,name=line.split(',')
        c[name]=(float(x),float(y))

    g=[]        

    for data in [data1,data2,data3]:
        G=nx.Graph()
        i=0
        G.pos={} # location
        G.pop={} # size
        last=None
        for line in data.split('\n'):
            x,y,p,r,n=line.split(',')
            G.pos[i]=(float(x),float(y))
            G.pop[i]=int(p)
            if last is None:
                last=i
            else:
                G.add_edge(i,last,{r:int(n)})
                last=i
            i=i+1
        g.append(G)        

    return g,c            

if __name__ == "__main__":

    (g,city)=minard_graph()

    try:
        import matplotlib.pyplot as plt
        plt.figure(1,figsize=(11,5))
        plt.clf()
        colors=['b','g','r']
        for G in g:
            c=colors.pop(0)
            node_size=[int(G.pop[n]/300.0) for n in G]
            nx.draw_networkx_edges(G,G.pos,edge_color=c,width=4,alpha=0.5)
            nx.draw_networkx_nodes(G,G.pos,node_size=node_size,node_color=c,alpha=0.5)
            nx.draw_networkx_nodes(G,G.pos,node_size=5,node_color='k')

        for c in city:
            x,y=city[c]
            plt.text(x,y+0.1,c)
        plt.savefig("napoleon_russian_campaign.png")
    except ImportError:
        pass


In [54]:
from IPython.display import Image

Image(filename='napoleon_russian_campaign.png')
Out[54]:

Basemap

Il nécessite de reprendre les informations des stations téléchargées antérieurement: elles ont des valeurs de type degrés, minutes, secondes (DMS). Les outils utilisés attendent des degrés décimaux.

In [4]:
import csv

def dms2dec(value):
    """
    Degres Minutes Seconds to Decimal degres
    """
    degres, minutes, seconds = value.split()
    seconds, direction = seconds[:-1], seconds[-1]
    dec = float(degres) + float(minutes)/60 + float(seconds)/3600
    if direction in ('S', 'W'):
        return -dec
    return dec

csv_file = 'Pub9volA141110x.flatfile'

with open('new_file.csv', 'wb') as csvfile:
    reader = csv.DictReader(open(csv_file, 'rb'), delimiter="\t")
    writer = csv.writer(csvfile, delimiter=';',
                            quotechar='"', quoting=csv.QUOTE_MINIMAL)
    for line in reader:
        lng = dms2dec(line.pop('Longitude'))
        lat = dms2dec(line.pop('Latitude'))
        wmoid = int(line.pop('StationId'))
        name = line.pop('StationName').title()
        writer.writerow([wmoid, name, lng, lat])
    
In [5]:
import numpy as np
# On ne garde que les colonnes numériques
lat, lon = np.loadtxt('new_file.csv', delimiter=';', unpack=True, usecols=[2, 3])

print lat
[  5.88333333   5.88333333   6.9        ..., -58.85        19.           2.        ]

In [3]:
from mpl_toolkits.basemap import Basemap
from matplotlib import pyplot

#basemap_graph = Basemap(projection='robin', lat_0=45.0, lon_0=0.0)
basemap_graph = Basemap(projection='ortho',lon_0=0,lat_0=45,resolution='l')
In [7]:
## Enable embedding Matplotlib in IPython
%matplotlib inline

pyplot.figure(figsize=(16,16))
basemap_graph.drawcoastlines()
basemap_graph.drawcountries()
basemap_graph.fillcontinents()
basemap_graph.drawmeridians(np.arange(-180,180,20))
basemap_graph.drawparallels(np.arange(-90,90,20))


x, y = basemap_graph(lat, lon)
basemap_graph.plot(x, y, 'o', markersize=4, color='red')
pyplot.show()

CartoPy

In [61]:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from cartopy.io.shapereader import Reader
from cartopy.feature import ShapelyFeature

fname = 'voronoi_stations.shp'

plt.figure(figsize=(18,18))
ax = plt.axes(projection=ccrs.PlateCarree())
ax.stock_img()
shape_feature = ShapelyFeature(Reader(fname).geometries(),
                                ccrs.PlateCarree(), facecolor='none')
ax.add_feature(shape_feature)
plt.show()

Folium

Création simple de cartes web depuis la ligne de commandes

Pratique pour l'exploration de données rapide

In [5]:
import folium
from IPython.display import HTML

map_1 = folium.Map(location=[47.2199, -1.5499], zoom_start=11,
                   tiles='Stamen Toner')
map_1.geo_json(geo_path='colonnes_aeriennes.geojson')

map_1.create_map(path='terrain.html')


# Open Browser in a new tab
import webbrowser
webbrowser.open('terrain.html', new=2)
Out[5]:
True
In [6]:
from IPython.display import HTML

leaflet_file = open("leaflet_geojson.html").read()

HTML(leaflet_file)
Out[6]:
Leaflet GeoJSON Example
In [8]:
%%javascript
console.log(map._layers)
In [46]:
from pygments_magic import *

%highlight_html leaflet_geojson.html
<!DOCTYPE html>
<html>
<head>
    <title>Leaflet GeoJSON Example</title>
    <meta charset="utf-8" />

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <link rel="stylesheet" href="leaflet/leaflet.css" />
</head>
<body>
    <div id="map" style="width: 600px; height: 400px"></div>

    <script src="leaflet/sample-geojson.js" type="text/javascript"></script>
    <script src="leaflet/leaflet.js"></script>

    <script>
        var map = L.map('map').setView([39.74739, -105], 13);

        var mapquestUrl = 'http://otile{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png',
        subDomains = ['1','2','3','4'],
        mapquestAttrib = 'Data, imagery and map information provided by <a href="http://open.mapquest.co.uk" target="_blank">MapQuest</a>,<a href="http://www.openstreetmap.org/" target="_blank">OpenStreetMap</a> and contributors.';
        var mapquestLayer = new L.TileLayer(mapquestUrl, {
            maxZoom: 18,
            opacity: 0.4,
            attribution: mapquestAttrib,
            subdomains: subDomains
        });

        mapquestLayer.addTo(map);

        var baseballIcon = L.icon({
            iconUrl: 'leaflet/images/baseball-marker.png',
            iconSize: [32, 37],
            iconAnchor: [16, 37],
            popupAnchor: [0, -28]
        });

        function onEachFeature(feature, layer) {
            var popupContent = "<p>I started out as a GeoJSON " +
                    feature.geometry.type + ", but now I'm a Leaflet vector!</p>";

            if (feature.properties && feature.properties.popupContent) {
                popupContent += feature.properties.popupContent;
            }

            layer.bindPopup(popupContent);
        }

        L.geoJson([bicycleRental, campus], {

            style: function (feature) {
                return feature.properties && feature.properties.style;
            },

            onEachFeature: onEachFeature,

            pointToLayer: function (feature, latlng) {
                return L.circleMarker(latlng, {
                    radius: 8,
                    fillColor: "#ff7800",
                    color: "#000",
                    weight: 1,
                    opacity: 1,
                    fillOpacity: 0.8
                });
            }
        }).addTo(map);

        L.geoJson(freeBus, {

            filter: function (feature, layer) {
                if (feature.properties) {
                    // If the property "underConstruction" exists and is true, return false (don't render features under construction)
                    return feature.properties.underConstruction !== undefined ? !feature.properties.underConstruction : true;
                }
                return false;
            },

            onEachFeature: onEachFeature
        }).addTo(map);

        var coorsLayer = L.geoJson(coorsField, {

            pointToLayer: function (feature, latlng) {
                return L.marker(latlng, {icon: baseballIcon});
            },

            onEachFeature: onEachFeature
        }).addTo(map);

    </script>
</body>
</html>

Mapnik

Un exemple avec libellé et contours départementaux

In [8]:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

# get extent from shp (for mapnik)
from ogr_extent import *

# Retrieve extent and projection using gdal
shp_extent , proj4 = extent_and_proj('departement.shp', sourcetype = 'ESRI Shapefile')


import cairo
from mapnik import Style, Rule, Color, Filter, LineSymbolizer, PolygonSymbolizer, TextSymbolizer, label_placement, Shapefile, SQLite, Layer, Map, render, Shapefile, Expression, save_map

map_output = 'france'
m = Map(300, 300, proj4)

m.background = Color('white')

t = TextSymbolizer(Expression('[CODE_DEPT]'), 'DejaVu Sans Book', 8, Color('black'))

f = Expression("[CODE_DEPT]<>'75' and [CODE_DEPT]<>'92' and [CODE_DEPT]<>'93' and [CODE_DEPT]<>'94'")
t.allow_overlap = 1
t.label_placement = label_placement.POINT_PLACEMENT
s1 = Style()
r1 = Rule()
r1.symbols.append(t)
r1.filter = f
s1.rules.append(r1)
m.append_style('Text', s1)

s = Style()
r = Rule()
r.symbols.append(PolygonSymbolizer(Color('#f2eff9')))
r.symbols.append(LineSymbolizer(Color('rgb(50%,50%,50%)'), 0.1))
s.rules.append(r)
m.append_style('My Style', s)

lyr = Layer('france', proj4)
import os

shp = Shapefile(base='.',file='departement') 

lyr.datasource = shp
#lyr.datasource = SQLite(base=os.getcwd(), file = sqlitedatabase, table = tablename, geometry_field = 'Geometry', key_field = 'ID_GEOFLA', extent = shp_extent, wkb_format = 'spatialite')

lyr.styles.append('My Style')
lyr.styles.append('Text')

m.layers.append(lyr)
m.zoom_to_box(lyr.envelope())

file_formats = {'svg': cairo.SVGSurface,
                       }

for type_format in file_formats:
    print '// --  Rendering %s -----------------------------' % type_format
    file_open = open('%s.%s' % (map_output, type_format), 'w')
    surface = file_formats[type_format](file_open.name, m.width, m.height)
    render(m, surface)
    save_map(m,"tmp_map.xml")
    surface.finish()
// --  Rendering svg -----------------------------

In [9]:
from IPython.display import SVG

SVG(filename='france.svg')
Out[9]:
In [19]:
# Pour aller plus loin, quelques bibliothèques non citées
from IPython.display import SVG

SVG(filename='pydata_stack_columns.svg')
Out[19]:
Produced by OmniGraffle 6.0.4 2014-05-01 18:20ZPyData StackLayer 1PySparkimpylapig (UDF)scikit-learnscipystatsmodelSymPynltkMLlibMongoDBRedisCassandraCouchDBSQLAlchemyPandasNumPyPyTablesIOProIPythonVisualizationSQL/NoSQLProcessingData IOHadoop?InterfaceMatplotlibBokehpygalvincentpynvd3ggplotfoliumNetworkXPlot.ly

Côté carto à tester (non testé mais avec un existant)

Autres (pour mémoire/information):

In []: