Cartographier des numéros d’autoroutes multiples dans OpenStreetMap

J'ai eu dernièrement à extraire les numéros d'autoroutes des segments de géométries du réseau routier de OpenStreetMap. Il arrive régulièrement que des segments aient plus d'un numéro d'autoroute. Dans ce cas, les numéros seront listés dans le champ 'ref' de la géométrie. Par exemple:
SELECT type, oneway, CHAR_LENGTH(ref) AS reflen,ref
     FROM osm_roads
     WHERE type IN ('motorway', 'trunk')
       AND (ref IS NOT NULL AND ref != '')
AND ref like '%;%'
limit 10;
   type   | oneway | reflen |       ref
----------+--------+--------+-----------------
 trunk    |      1 |      9 | US 412;84
 motorway |      1 |     10 | I 72;US 36
 motorway |      1 |     10 | I 72;US 36
 trunk    |      0 |      9 | NH947;SH6
 motorway |      1 |      9 | I 55;I 69
 trunk    |      1 |     11 | US 10;WI 13
 motorway |      1 |     15 | I 55;I 70;US 40
 trunk    |      0 |     11 | US 278;MS 6
 motorway |      1 |     15 | I 55;I 70;US 40
 motorway |      1 |     11 | I 69;MS 304
(10 rows)
En utilisant un Regex et des array dans la base de données Postgresql/PostGIS, il sera possible d'isoler et multiplier les numéros sur autant de lignes que de numéros trouvés (NOTE: on retrouve une expression régulière semblable dans plusieurs scripts de traitement de données OSM sur le Web): Dans cet exemple ref = 'I 39;I 90'
SELECT 'segment 1' as id,
(regexp_matches('I 39;I 90',
                '([a-z,A-Z]+)?(\s|\-)?([0-9]+)[^;|/|\s]*',
                'g'))[1] as shield_class,
(regexp_matches('I 39;I 90',
                '([a-z,A-Z]+)?(\s|\-)?([0-9]+)[^;|/|\s]*',
                'g'))[3] as shield_no;

    id     | shield_class | shield_no
-----------+--------------+-----------
 segment 1 | I            | 39
 segment 1 | I            | 90
(2 rows)
Dans cet autre exemple ref = 'I 39- 90'
SELECT 'segment 2' as id,
(regexp_matches('I 39- 90',
                '([a-z,A-Z]+)?(\s|\-)?([0-9]+)[^;|/|\s]*',
                'g'))[1] as shield_class,
(regexp_matches('I 39- 90',
                '([a-z,A-Z]+)?(\s|\-)?([0-9]+)[^;|/|\s]*',
                'g'))[3] as shield_no;

    id     | shield_class | shield_no
-----------+--------------+-----------
 segment 2 | I            | 39
 segment 2 |              | 90
(2 rows)
Et enfin ref = 'I 39;US 90/S 109'
SELECT 'segment 3' as id,
(regexp_matches('I 39;US 90/S 109',
               '([a-z,A-Z]+)?(\s|\-)?([0-9]+)[^;|/|\s]*',
               'g'))[1] as shield_class,
(regexp_matches('I 39;US 90/S 109',
                '([a-z,A-Z]+)?(\s|\-)?([0-9]+)[^;|/|\s]*',
                'g'))[3] as shield_no;

    id     | shield_class | shield_no
-----------+--------------+-----------
 segment 3 | I            | 39
 segment 3 | US           | 90
 segment 3 | S            | 109
(3 rows)
En utilisant cette astuce, il sera donc possible de cartographier plusieurs numéros sur un même segment de route

Scribeui 0.7 beta

En 2013, Mapgears lance le projet ScribeUI destiné à simplifier la création de cartes pour le Web basé sur Mapserver et l'intégration du langage scribe pour mapserver.  L'objectif était vraiment simple: améliorer notre efficacité d'écriture d'un mapfile pour produire des cartes multiniveau tout en conservant la grande flexibilité de Mapserver.  La première version du projet était vraiment intéressante et on décide de faire un Google summer of code pour améliorer le design de l'outil.  Jessica Lapointe (de l'équipe Mapgears) avait d'ailleurs eu de bons mots à propos de son projet ScribeUI au GsoC 2013 provenant de la responsable de la fondation OSGeo.

Suite à des travaux importants réalisés chez Mapgears, je teste depuis plusieurs jours, une nouvelle version de ScribeUI et je suis vraiment épaté du résultat. Le projet n'est pas encore sur Github, mais il y sera prochainement.  On a remodelé l'interface, ajouté de nouvelles fonctions mais surtout changé le framework Flask pour Pyramid. L'application est maintenant beaucoup plus stable même si quelques petits bogues persistent. La bonne nouvelle est que Jessica Lapointe poursuivra bientôt les travaux entamés sur l'application beta pour améliorer l'utilisation générale du produit.

Personnellement, je ne peux plus me passer de l'outil pour produire des cartes web.  ScribeUI offre une méthode rapide, structurée et efficace pour faire un projet Web Mapping.  Combiné à Git, je peux transférer un projet d'une VM local à un serveur Cloud en quelques minutes, c'est vraiment pratique.

.

En plus de la syntaxe scribe très pratique, deux nouvelles fonctions vraiment cool arrivent avec cette version!  On a intégré un processus Git à la production carto avec ScribeUI. On se disait que produire une carte web est un peu comme faire de la programmation et que git mériterait d'être plus largement utilisé dans ce contexte.  C'est aussi une bonne pratique pour structurer le travail à faire, suivre l'évolution des versions de la carte et faciliter le transfert du projet cartographique d'un environnement à l'autre (DEV, TEST, PROD).  Directement dans l'application ScribeUI, on pourra maintenant, cloner puller et pusher toutes la config d'un projet carto sur un Git.  J'ai un projet complet (data et mapping ) pouvant être cloné ici : https://github.com/smercier/gitscribe.

On a ajouté un lien direct sur le fichier "readme.md" de votre projet pour favoriser le réflex de documentation. Deux clics suffisent pour éditer le "readme.md" de votre git avec la syntaxe mardown.  Pour ma part, c'est dans le readme que je documente les grandes lignes du makefile d'installation de données (quand c'est requis).

On a amélioré la coloration syntaxique et on pourra mieux apprécier la nouvelle option pour commenter de grande section de mapfile avec un code ouvrant (/*) et fermant  (*/) , ça j'aime VRAIMENT.  On a ajouté un lien direct sur l'aide en ligne(http://mapserver.org/mapfile/)  des mapfiles keywords en cliquant sur ALT et le keywords.

Écrire des mapfiles avec des variables et des codes de niveau de zoom, qui correspond bien plus à notre nouvelle réalité de production cartographique pour OpenLayers ou Leaflet.  Faire une carte avec les données OpenStreetMap devient soudainement bien plus plaisant.

L'autre fonction nouvellement ajoutée est un UI pour lancer des job Mapcache.  Le panorama permet de spécifier le Zoom levels à produire, le Metatile size, le Grid disponible et un Extent spécifique.  Ce dernier peut être dérivé du Map Extent de mapfile mais aussi à partir shapefile ou même d'une requête provenant d'un serveur Postgis.   L'intégration de nouvelles options sera documentée sur github.

J'ai aussi testé l'application avec des Mapfiles standard très volumineux (pour une carte maritime avec des données S-57) 5 mapfiles de 2000 lignes et plus dont un de 3042 lignes.  La navigation entre les groupes me permette de passer rapidement d'un include mapfile très rapidement et rend l'édition de la carte bien plus rapide.

À suivre donc, d'autres infos suivront prochainement.  Les idées ne manquent pas pour ce projet.

Un essai avec Vagrant et osmbase

Update le 30 décembre 2013
Suite à un billet de @alanboudrault, sur l'utilisation d'un environnement virtuel pour développer avec Mapserver, j'ai testé l'utilisation de Vagrant pour améliorer notre processus interne de tests unitaires sur nos projets Open Street Map.   J'ai tout de suite aimé la simplicité de l'outil et la rapidité qu'il me donne pour monter un environnement jettable à l'aide de quelques lignes de commandes.  Il faut voir Vagrant comme un outil exploitant un moteur de machine virtuel comme Virtual Box ou VMware Fusion.  Sa force sera d'être rapidement configurable et réutilisable avec un Vagrantfile.

Considérons que vous avez déjà installé Vagrant et VirtualBox. Dans une fenêtre terminale (Mac, Ubuntu ou Windows), on commence par télécharger une box vagrant de type Ubuntu et on va l'initialiser:

mkdir vagrant
cd vagrant
vagrant box add precise32 http://files.vagrantup.com/precise32.box
vagrant init precise32

L'initiation de la VM avec Vagrant, vient de créer un fichier Vagrantfile.  Je garde ce config hyper simple pour l'instant. Il me permettra de me connecter au besoin sur ma VM avec pgAdmin au serveur "localhost" sur le port 5555, et d'accéder au serveur Web localhost via le port 8080 (  http://localhost:8080):

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|

# Every Vagrant virtual environment requires a box to build off of.
config.vm.box = "precise32"

# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8080" will access port 80 on the guest machine.
config.vm.network :forwarded_port, guest: 5432, host: 5555
config.vm.network :forwarded_port, guest: 80, host: 8080
...
end
On va démarrer la VM et s'y connecter:
vagrant up
vagrant ssh
Utilisateur Windows: J'utilise ici un client ssh par défaut sur mon macbook. Pour les utilisateurs Windows, je conseille toujours d'utiliser le client ssh MobaXtrem parce qu'il est très pratique pour les moins habitués. L'utilitaire Putty peut aussi être utilisé. Vagrant donnera ce genre d'information (après vagrant up) pour faire la connexion avec MobaXtrem:
Host: 127.0.0.1
Port: 2222
Username: vagrant
Private key: C:/Documents and Settings/mapgears/.vagrant.d/insecure_private_key

J'utilise toujours le user par défaut de vagrant. Un fois connecté au serveur, configurer la langue du nouveau serveur dans le profile vagrant. Ça reste une étape toujours importante pour supporter convenablement les accents dans votre base de données Postgresql:

export LANGUAGE=en_CA.UTF-8
export LANG=en_CA.UTF-8
export LC_ALL=en_CA.UTF-8
sudo localedef -i en_CA -f UTF-8 en_CA
sudo locale-gen en_CA.UTF-8
sudo update-locale
sudo sudo dpkg-reconfigure locales
On doit maintenant faire la mise à jour du serveur avec un script VirtualBox et brancher le dépôt logiciels géomatiques UbuntuGIS (version precise):
sudo sh postinstall.sh
sudo apt-get update
sudo apt-get install python-software-properties
sudo add-apt-repository ppa:ubuntugis/ppa
sudo apt-get update
Installation de Postgresql/PostGIS
sudo apt-get install -y postgresql-9.1 postgresql-server-dev-9.1 postgresql-contrib-9.1 postgis
Installation d'outils
sudo apt-get install -y apache2 binutils gdal-bin checkinstall git vim screen make python-virtualenv python-pip python-all-dev osm2pgsql osmosis 
Installation Mapserver / Mapcache
sudo apt-get install -y cgi-mapserver mapserver-bin libmapcache mapcache-cgi mapcache-tools libapache2-mod-mapcache
sudo mkdir /tmp/ms_tmp
sudo chown www-data:www-data /tmp/ms_tmp
NOTE: Pour faire toutes les opérations en une seule étape, créer un fichier init.sh avec vi à l'aide de ce Gist init.sh

Par la suite on installe un nouvelle BD Postgresql/PostGIS OSM avec un encodage UTF-8 (voir ce billet pour instructions):

Ne reste plus qu'à faire un peu de magie avec ce repo osmbase. Le makefile contenu dans ce projet va télécharger les données requises et lancer imposm avec notre petit fichier OSM préféré de la Nouvelle-Calédonie pour construire ma carte QA et de plus j'active un environnement virtuel python. Enfin, simplement modifier les variables de départ du makefile pour vos besoins.

virtualenv venv
source venv/bin/activate
pip install imposm
git clone https://github.com/smercier/osmbase.git
cd osmbase
make
make imposm
Si l'installation se passe sans erreur, la URL suivante devrait fonctionner! http://localhost:8080/cgi-bin/mapserv?map=/home/vagrant/osmbase/osmbase.map&LAYERS=default&template=openlayers Une fois le QA terminé, on a qu'a détruire la VM et recommencer le processus à nouveau pour un autre besoin.  Très simple, j'aime vraiment :-)

Scribe – Une nouvelle façon de faire du mapfile

Créer une belle carte qui véhicule efficacement son message n'est pas chose facile. On dispose souvent d'une grande quantité de données qu'on ne souhaite pas afficher en tout temps ou qu'on souhaite afficher mais avec un style qui varie en fonction du niveau d'échelle. Lorsqu'on défini le style d'une carte, chaque petit détail est important. Le processus se fait généralement de facon itérative et implique beaucoup de modifications, à tous les niveaux d'échelle.

Dans Mapserver, la gestion de l'échelle se fait avec les tags MINSCALEDENOM et MAXSCALEDENOM au niveau du LAYER ou de la CLASS. Cela veut dire qu'aussitôt qu'un élément du style change pour un LAYER ou une CLASS donné, à un certain niveau d'échelle, un nouveau LAYER ou une nouvelle CLASS doit être créé. On se trouve ainsi a réécrire beaucoup de texte pour ne modifier peut-être qu'un seul paramètre. La tâche devient encore plus fastidieuse si on doit changer un paramètre (par exemple la couleur d'une route) définie dans 16 LAYERs différents.

"Scribe" est un outil en python que nous avons créé pour faciliter l'écriture d'un "mapfile" en utilisant des variables et des raccourcis pour la gestion des échelles via des niveaux (maintenant la norme).  Cette façon de faire s'apparente à "Basemaps" mais est plus simple d'utilisation et généralement moins verbeuse.

Gestion des niveaux d'échelle

LAYER {
    1-16 {
        NAME: 'land'
        TYPE: POLYGON
        @layerconfig
        DATA {
            1-4: '110m_physical/ne_110m_land'
            5-10: '50m_physical/ne_50m_land'
            11-16: '10m_physical/ne_10m_land'
        }
        CLASS {
            STYLE {
                COLOR {
                    1-6: '#EEECDF'
                    7-16: '#AAA89B'
                }
                OUTLINECOLOR: 200 200 200
                OUTLINEWIDTH: @land_ol_width
            }
         }
     }
 }

Dans l'exemple précédent, un LAYER appelé "land" est créé. Le tag "1-16" signifie que ce LAYER doit etre affiché des niveaux d'échelle 1 à 16. Le script traduit automatiquement ces niveaux en MINSCALEDENOM et MAXSCALEDENOM. De plus, les données utilisées (DATA) des niveaux 1 à 4 sont au 110m tandis qu'elles sont au 50m des niveaux 5 à 10 et au 10m des niveaux 11 à 16. La couleur (COLOR) elle aussi change en fonction de l'échelle. En utilisant cette nomenclature, il devient très facile de modifier un paramètre pour un ou plusieurs niveaux d'échelle sans avoir à réécrire beaucoup de texte ou à faire des modifications à plusieurs endroits.

Définition et utilisation de variables

"Scribe" ne permet pas seulement de gérer les échelles mais il permet aussi de définir des variables réutilisables. Dans l'exemple ci-haut, les variables "layerconfig" et "land_ol_width" sont appelées a l'aide d'un "@". Ces variables sont définies de la facon suivante:

VARIABLES {
    layerconfig {
        GROUP: 'default'
        STATUS: ON
        PROJECTION {{
            'init=epsg:4326'
        }}
        PROCESSING: 'LABEL_NO_CLIP=ON'
        PROCESSING: 'CLOSE_CONNECTION=DEFER'
     }
     land_ol_width: 1
 }

La variable "layerconfig" contient plusieurs paramètres utilisés dans la définition de presque tous les LAYERs. Ainsi, pour chaque nouveau LAYER, il suffit d'écrire "@layerconfig" pour que tous les paramètres entrent dans la définition du LAYER. L'autre variable 'land_ol_width' prend une valeur unique.

Il est à noter que dans la définition de la variable "layerconfig", PROJECTION est suivi de deux "{". Cette syntaxe permet de gérer les tags comme PROJECTION, METADATA, PATTERN etc qui ne contiennent aucun paramètre, seulement du texte.

Blocs de commentaires

"Scribe" permet également d'utiliser des blocs de commentaire, ce qui n'est pas possible dans Mapserver seulement. D'autres types de commentaire sur une seule ligne peuvent aussi être écrits et apparaîtront dans le "mapfile" résultant.

LAYER {
    1-16 {
        NAME: 'land'
        TYPE: POLYGON
        @layerconfig
        DATA {
            1-4: '110m_physical/ne_110m_land'
            5-10: '50m_physical/ne_50m_land'
            11-16: '10m_physical/ne_10m_land'
        }
        CLASS {
            STYLE {
                COLOR {
                    1-6: '#EEECDF'
                    7-16: '#AAA89B'
                }                
                ##Les commentaires précédés par ## apparaissent
                ##dans le mapfile résultant.
                ##Les blocs de commentaires entre /* */
                ## n'apparaissent pas dans le mapfile résultant.
                /*
                OUTLINECOLOR: 200 200 200
                OUTLINEWIDTH: @land_ol_width
                */                
             }
         }
     }
 }

Conclusion

Finalement, pour exécuter le script, il suffit d'utiliser une seule commande, configurable avec certaines options:

python scribe.py

Le résultat est un mapfile parfaitement indenté avec gestion des échelles et souvent beaucoup plus de ligne que ce qu'il a fallu écrire. "Scribe" représente donc une économie importante en temps et produire des cartes de qualité devient plus facile et agréable.

Il est fort probable que "Scribe" évoluera et que de nouvelles fonctionnalités s'ajouteront.

Pour plus d'information ou pour utiliser "Scribe", consulter l'adresse suivante :

https://github.com/solutionsmapgears/Scribe.git Pour tout commentaire ou pour rapporter un bug : Charles-Éric Bourget cbourget@mapgears.com

Cartographie d’un réseau routier avec MapServer: savoir gérer l’ordre d’affichage

Lorsqu’on veut cartographier un réseau routier, l’ordre d’affichage des routes est important pour que les voies les plus importantes ne soient pas coupées par des voies secondaires, par exemple.

Image 1 - résultat désiré

Image 2 - résultat brut

L'ordre d'affichage dépend de plusieurs facteurs.  Cet article explique le comportement de MapServer face à l'ordre d'affichage des entités géographiques afin de vous permettre d'obtenir un résultat "propre" dans l'affichage de votre réseau routier.  Il est important de noter que certaines des règles qui suivent ne s'appliquent qu'aux LAYERs de type LINE.

1) Premier lu - Premier dessiné

Le premier élément à considérer est, qu'à l'intérieur d'un même layer, les éléments sont dessinés selon l'ordre de lecture de la donnée indépendamment de la CLASS à laquelle ils appartiennent. En d'autres mots, le premier segment du réseau routier lu sera le premier dessiné. Il est donc nécessaire de classer la source. Par exemple, avec une BD Postgresql/PostGIS, on peut classer les données selon un z-index (z_order dans Open Street Map) et selon la longueur du segment :

DATA "geometry from (select osm_id, geometry, name, type
      from osm_roads
      where type in ('secondary', 'tertiary', 'residential')
      order by z_order asc, st_length(geometry) asc)
      as foo using unique osm_id using srid=900913"

Si vous utilisez un shapefile, l'utilitaire sortshp pourrait être très utile. 

NOTE: Cette règle s'applique à tous les types de layers.

2) Nombre de style dans la classe

Le nombre de STYLE dans une CLASS influence l'ordre d'affichage. Lorsqu'à l'intérieur d'un LAYER, des CLASS comprennent plus qu'un STYLE, le premier STYLE de chaque CLASS est dessiné avant de procéder au 2e STYLE des CLASS et ainsi de suite.

3) Outline en premier

Lorsque les premiers STYLE des CLASS comportent un OUTLINEWIDTH, les outlines de toutes les CLASS sont dessinés avant l'intérieur.  Cet aspet spécifique n'est pas le fruit du hasard et a été implanté dans la version 5.4 et documenté dans la RFC-49.

Exemple: mapfile incorrect

CLASS  # routes secondaires
  EXPRESSION /secondary/
  STYLE
    WIDTH 12
    COLOR 255 0 0 # rouge
    OUTLINEWIDTH 2
    OUTLINECOLOR 153 111 57 # brun
  END
  STYLE
    WIDTH 11
    COLOR 223 197 124 # orange
  END
END

CLASS # routes tertaires
  EXPRESSION /tertiary/
  STYLE
    WIDTH 11
    OUTLINEWIDTH 1
    OUTLINECOLOR 193 188 157 # gris
    COLOR 255 253 139 # jaune
  END
END

CLASS # routes résidentielles
  STYLE
    WIDTH 11
    OUTLINEWIDTH 1
    OUTLINECOLOR 103 181 157  # vert
  END
  STYLE
    WIDTH 11
    COLOR  238 225 226  # rose
  END
END

En considérant que la source a été triée dans cet ordre: résidentielles, tertiaires, secondaires, l'ordre d'affichage se déroule comme suit:

  1. Outline (vert) des routes résidentielles
  2. Outline (gris) des routes tertiaires
  3. Outline (brun) des routes secondaires
  4. Premier style (uniquement un outline) des routes résidentielles
  5. L'unique style (jaune) des routes tertiaires
  6. Premier style (rouge) des routes secondaires
  7. Deuxième style (rose) des routes résidentielles
  8. Deuxième style (orange) des routes secondaires
 

Exemple: mapfile correct

CLASS  # routes secondaires
  EXPRESSION /secondary/
  STYLE
    WIDTH 12
    COLOR 255 0 0 # rouge
    OUTLINEWIDTH 2
    OUTLINECOLOR 153 111 57 # brun
  END
  STYLE
    WIDTH 11
    COLOR 223 197 124 # orange
  END
END

CLASS # routes tertaires
  EXPRESSION /tertiary/
  STYLE
    WIDTH 11
    OUTLINEWIDTH 1
    OUTLINECOLOR 193 188 157 # gris
  END
  STYLE
    WIDTH 11
    COLOR 255 253 139 # jaune
  END
END

CLASS # routes résidentielles
  STYLE
    WIDTH 11
    OUTLINEWIDTH 1
    OUTLINECOLOR 103 181 157  # vert
  END
  STYLE
    WIDTH 11
    COLOR  238 225 226  # rose
  END
END

Produire un effet de “couleur dégradée” avec Mapserver

Nous avons  travaillé sur un projet de Web mapping dernièrement dans lequel on voulait produire un effet de "couleur dégradée" (fade to white) entre la terre ferme et l'océan. J'ai trouvé dernièrement une superbe carte dans l'"Atlas of Design" (que je recommande fortement pour les maniaques comme moi) de la NACIS (North American Cartographic Information Society).

À la page 64 on trouvera le genre d'effet de "couleur dégradée" en question:

On a l'habitude de voir ce genre d'effet dans un atlas et il s'agit la plupart du temps d'une retouche "photoshop". Il est par contre possible de reproduire l'effet avec une suite de buffers "concentrique" ou "excentrique" autour des entités d'une classe de provinces ou de continents par exemple pour "dégrader" la couleur de l'eau. Je me suis inspiré de d'une recette sur le forum SIG pour le faire dans PostGIS.

La méthode pourrait certe être plus optimale et performante si elle était gérée nativement dans Mapserver (nous acceptons volontier le financement pour le faire), par contre le résultat est superbe.

Donc première étape: chargement... shp2pgsql -s 3163 -c -g the_geom -I -W "latin1" QUARTIER_POLYGON.shp nc_noumea | psql -d nc -h 192.168.6.20 -p 5432 -U nc

Pour créer les anneaux "excentriques" j'ai utilisé ces suites de requêtes SQL. On devra jouer avec la largeur du buffer si on doit afficher le "fade effect" à plusieurs échelles et créer plus d'anneaux. Dans l'exemple suivant (de 0 a 35 mètres) on comprendra que l'effet ne sera visible qu'à très petite échelle. Si on veut appliquer la recette à plus grande échelle, il suffira simplement d'ajouter des anneaux supplémentaires ET à des intervalles plus larges.

NOTE: Merci à Vincent Picavet de la firme Oslandia pour le conseil de l'incroyable commande generate_series(), que je connaissais pas! Vraiment bien cette commande. Cette instruction SQL très compacte, va même créer la table pour moi! drop table nc_noumea_step; create table nc_noumea_step as select 0 as gid,0 as step,the_geom from nc_noumea; insert into nc_noumea_step (gid,step,the_geom) select 1 as gid,5 as step,ST_Difference(ST_Buffer(st_multi,5),ST_Buffer(the_geom,0)) from nc_noumea; insert into nc_noumea_step (gid,step,the_geom) select 2 as gid,10 as step,ST_Difference(ST_Buffer(st_multi,10),ST_Buffer(the_geom,5)) from nc_noumea; insert into nc_noumea_step (gid,step,the_geom) select 3 as gid,15 as step,ST_Difference(ST_Buffer(st_multi,15),ST_Buffer(the_geom,10)) from nc_noumea; insert into nc_noumea_step (gid,step,the_geom) select 4 as gid,20 as step,ST_Difference(ST_Buffer(st_multi,20),ST_Buffer(the_geom,15)) from nc_noumea; insert into nc_noumea_step (gid,step,the_geom) select 5 as gid,25 as step,ST_Difference(ST_Buffer(st_multi,25),ST_Buffer(the_geom,20)) from nc_noumea; insert into nc_noumea_step (gid,step,the_geom) select 6 as gid,30 as step,ST_Difference(ST_Buffer(st_multi,30),ST_Buffer(the_geom,25)) from nc_noumea; insert into nc_noumea_step (gid,step,the_geom) select 7 as gid,35 as step,ST_Difference(ST_Buffer(st_multi,35),ST_Buffer(the_geom,30)) from nc_noumea;
SELECT i as step, ST_Difference(ST_Buffer(st_multi,i),ST_Buffer(st_multi,i-5))
INTO nc_noumea_step
FROM nc_noumea n, generate_series(5,35,5) AS i;

On pourra utiliser un buffer négatif pour produire le même effet mais en dégradant la couleur vers l'intérieur (anneaux concentriques):

...
insert into communes_50_step (gid,cod_commun,step,the_geom) select gid,cod_commun,200 as step,ST_Difference(ST_Buffer(the_geom,-100),ST_Buffer(the_geom,-200)) as the_geom from communes_50;
insert into communes_50_step (gid,cod_commun,step,the_geom) select gid,cod_commun,300 as step,ST_Difference(ST_Buffer(the_geom,-200),ST_Buffer(the_geom,-300)) as the_geom from communes_50;
...

J’ai fait plusieurs essais dont des buffers successifs qui embarquent les uns sur les autres, mais l'effet est moins propre et le résultat moins prévisible. Le meilleur résultat et le plus flexible est vraiment une suite d'anneaux (buffers polygone) successifs qui n'empiètent pas les uns sur les autres. De cette façon on peut gérer la transparence avec couleur de fond pour chaque classe ET/OU des couleurs uniques ET/OU une orthophoto en fond de carte, tout est possible.

Enfin, dans le mapfile j'ai cartographié dans plusieurs CLASS chacun des anneaux désirés avec une transparence spécifique en dégradé :

LAYER
    TYPE POLYGON
    STATUS ON
    GROUP "zone"
    NAME "nc_contour_2_16"
    PROJECTION
        "init=epsg:3163"
    END
    MINSCALEDENOM 4096
    MAXSCALEDENOM 8192
    DATA "DATA_VDN/nc_noumea_step.shp"
    CLASSITEM "step"
    CLASS
        EXPRESSION "0"
        STYLE
            COLOR 255 255 255
        END
    END
    CLASS
        EXPRESSION "5"
        STYLE
            COLOR "#3fc1b7"
            OPACITY 80
        END
    END
    CLASS
        EXPRESSION "10"
        STYLE
            COLOR "#3fc1b7"
            OPACITY 60
        END
    END
    CLASS
        EXPRESSION "15"
        STYLE
            COLOR "#3fc1b7"
            OPACITY 50
        END
    END
    CLASS
        EXPRESSION "20"
        STYLE
            COLOR "#3fc1b7"
            OPACITY 40
        END
    END
    CLASS
        EXPRESSION "25"
        STYLE
            COLOR "#3fc1b7"
            OPACITY 30
        END
    END
    CLASS
        EXPRESSION "30"
        STYLE
            COLOR "#3fc1b7"
            OPACITY 20
        END
    END
    CLASS
        EXPRESSION "35"
        STYLE
            COLOR "#3fc1b7"
            OPACITY 10
        END
    END
END
LAYER
    TYPE POLYGON
    STATUS ON
    GROUP "zone"
    NAME "nc_contour_1_17"
    PROJECTION
        "init=epsg:3163"
    END
    MINSCALEDENOM 2048
    MAXSCALEDENOM 4096
    DATA "DATA_VDN/nc_noumea_step.shp"
    CLASSITEM "step"
    CLASS
        EXPRESSION "0"
        STYLE
            COLOR 255 255 255
            OUTLINECOLOR "#3fc1b7"
            WIDTH 0.4
        END
    END
    CLASS
        EXPRESSION "5"
        STYLE
            COLOR "#3fc1b7"
            OPACITY 50
        END
    END
    CLASS
        EXPRESSION "10"
        STYLE
            COLOR "#3fc1b7"
            OPACITY 30
        END
    END
    CLASS
        EXPRESSION "15"
        STYLE
            COLOR "#3fc1b7"
            OPACITY 10
        END
    END
END
LAYER
    TYPE POLYGON
    STATUS ON
    GROUP "zone"
    NAME "nc_contour_1_18"
    PROJECTION
        "init=epsg:3163"
    END
    MINSCALEDENOM 1500
    MAXSCALEDENOM 2048
    DATA "DATA_VDN/nc_noumea_step.shp"
    CLASSITEM "step"
    CLASS
        EXPRESSION "0"
        STYLE
            COLOR 255 255 255
            OUTLINECOLOR "#3fc1b7"
            WIDTH 0.4
        END
    END
    CLASS
        EXPRESSION "5"
        STYLE
            COLOR "#3fc1b7"
            OPACITY 50
        END
    END
    CLASS
        EXPRESSION "10"
        STYLE
            COLOR "#3fc1b7"
            OPACITY 20
        END
    END
END
LAYER
    TYPE POLYGON
    STATUS ON
    GROUP "zone"
    NAME "nc_contour_1_18"
    PROJECTION
        "init=epsg:3163"
    END
    MINSCALEDENOM 0
    MAXSCALEDENOM 1500
    DATA "DATA_VDN/nc_noumea_step.shp"
    CLASSITEM "step"
    CLASS
        EXPRESSION "0"
        STYLE
            COLOR 255 255 255
            OUTLINECOLOR "#3fc1b7"
            WIDTH 0.4
        END
    END
    CLASS
        EXPRESSION "5"
        STYLE
            COLOR "#3fc1b7"
            OPACITY 50
        END
    END
END

Transférer une symbologie “ArcGIS font Symbol” à Mapserver

Voici donc une procédure toute simple pour transformer une symbologie complexe d'une ligne basée sur l'utilisation de font dans ArcGIS vers un mapfile pour Mapserver. Partant de votre panorama de "Symbol Property Editor" dans ArcGIS:   on trouvera un symbole utilisant deux symboles de font. Cliquer sur "Symbol...", pour avoir accès à plus d'options. Dans ce panorama, on pourra récupérer le "Unicode" du symbole dans le fichier de font. Dans cet exemple on pourra donc utiliser le code 99 de la font "MapGenFont" Ce code est l'identifiant unique permettant d'identifier le symbole dans cette font. Toujours dans cet exemple, l'autre caractère utilisé dans cette symbologie complexe est dans la font "ESRI Default Marker" unicode 181. Pour utiliser cette font avec Mapserver on devra copier les fichiers de font dans l'arborescence de fichiers de configuration Mapserver. On trouvera facilement le fichier en naviguant dans le répertoire de fonts Windows "C:/Windows/Fonts". Ensuite on va ajouter la font dans le fichier fontset.
  #FONTSET File
mgfont                ./fonts/MGfont.ttf
esri_default_marker   ./fonts/esri_11.ttf
...
Enfin on va créer le symbole de cette façon:
 #symbolset File
  SYMBOL
    NAME       "rock-1"
    TYPE       TRUETYPE
    FONT       "mgfont"
    CHARACTER  "c"
    GAP        3
  END
  SYMBOL
    NAME       "rock-2"
    TYPE       TRUETYPE
    FONT       "esri_default_marker"
    CHARACTER  "µ"
  END
  #Mapfile
  ...
    TYPE                    LINE
    CLASS
      NAME                  "Sand"
      STYLE
        SYMBOL              "rock-1"
        COLOR               78 78 78
        SIZE                7
      END
      STYLE
        SYMBOL              "rock-2"
        COLOR               78 78 78
        SIZE                7
        OFFSET              3 3
      END
    END
   ...

Investissements du Québec en santé

english version follow...
Nous avons réalisé dernièrement une nouvelle application de visualisation de données sur les "Investissements du Québec en santé".  Nous avons trouvé l'application vraiment "cool" alors on a décidé de la diffuser durant la présente campagne électorale 2012 parce qu'il est intéressant de pouvoir visualiser la taille de ces investissements.
Pour cette application, nous avons utilisé l'information publique du "Ministère de la Santé et des Services sociaux" diffusée via le site de données ouvertes du gouvernement du Québec, et des outils Web de visualisation de données pour mettre en relief le poids des investissements en santé versus la population et le budget total de la province de Québec.
Pour réaliser cette application, nous avons utilisé la librairie d3.js, Leaflet, puis Mapserver pour tuiler ( Mapcache ) et diffuser notre carte.
------------------------------
We recently launched our new data vizualisation application "Investissement du Québec en santé".  We find it cool so we decided to publish it.  It's interesting to visualize the size of these investments in this Quebec election campaign.
For this Web application, we used public data from the Government of Quebec Opendata Web site. We used data from "Ministery of Health and Social Services" and data visualization tools to highlight the amount of investments in health versus population of Quebec and the total budget of the province of Quebec.
To build this application, we used d3.jsLeaflet, and we used Mapserver to tiled (Mapcache) and served our map.

MS4W et Oracle Spatial

Même si en principe, l'installation et la configuration pour connecter un layer Oracle Spatial d'un mapfile est vraiment simple,  j'ai passé quelques jours dernièrement à chercher une erreur de configuration un peu stupide!  J'ai cru bon en faire un résumé en lien avec la documentation Mapserver sur le sujet.

À mon avis, la façon la plus simple d'utiliser MS4W avec Oracle Spatial sur votre Windows, est d’utiliser le Instant Client basic light.  Pas besoin de polluer votre setup avec la grosse installation du client complet.  La procédure qui suit montre comment créer à partir de zéro  1) une table avec un champ SDO_GEOMETRY sur Oracle, 2) l’enregistrer dans le catalogue Oracle, 3) installer le client Oracle, 4) créer le mapfile et 5) des astuces pour tester votre config.

Chargement d'un jeux de données test

Il sera très utile d'avoir un jeux de données testes sur votre serveur Oracle.  J'utilise régulièrement ce script pour charger des données rapidement et sans effort.

Installer le InstantClient Oracle

0) Le package MS4W est un build 32bit.  Il est très important de télécharger le InstantClient-basiclight 32bits sur le site d’Oracle même si votre serveur Windows est de type 64bit.   Pour installer le client Oracle, simplement décompresser le fichier et placer dans le répertoire de son choix.  Pour cet exemple utilisons:

 c:instantclient_11_2

1) Il est IMPÉRATIF de placer ce répertoire dans la Variable d’Environnement  PATH du système.  C’est la SEULE variable d’environnement que vous devez configurer.   Les variables ORACLE_HOME, ORACLE_BASE, TNS_ADMIN, ORACLE_SID ou LD_LIBRARY_PATH ne sont pas requises pour MS4W.

2)  On doit déplacer le fichier libmap.dll dans le répertoire cgi-bin de Apache et le fichier ogr_OCI.dll dans le répertoire de gdalplusgins.  NOTE: Il serait préférable d’arrêter le service MS4W Apache sur votre serveur s’il est déjà en route avant de faire la copie des fichiers:

c:ms4wApachecgi-binignored-libmaporacle11glibmap.dll --> c:ms4wApachecgi-bin
c:ms4wgdalpluginsignoredoracle-11gogr_OCI.dll --> c:ms4wgdalplugins
Ne reste plus qu’à redémarrer Apache via un Prompt DOS ou dans le gestionnaire de services Windows: c:ms4wapache-restart
IMPORTANT:

Je suggère ici de vous assurer que le PATH contient bien votre répertoire du client Oracle.  On peut regarder dans un Prompt DOS avec la commande set mais vous devez aussi vérifier qu’elle sera aussi disponible dans Apache.  Vous pouvez valider avec la commande phpinfo.php.  On doit trouver à la rubrique “Environment” la valeur de la variable PATH qui doit inclure le répertoire du client Oracle.  Il se peut très bien que la valeur de PATH du système Windows ne soit pas la même dans Apache et dans DOS.

ogrinfo

Avant de faire des essais avec Mapserver, il est fortement recommandé de tester la connexion Oracle avec ogrinfo parce que moins de composantes sont requises.  J'utilise une connexion sans TNSNAMES.ORA parce que c'est plus propre et plus simple.  Ouvrir un Prompt Dos et faire les commandes suivantes: 

C:ms4w>setenv
C:ms4w>set PATH=C:instantclient_11_2;%PATH%
C:ms4w>ogrinfo OCI:user/pw@my_serveur_name_or_ip:port/my_sid_db_name
-----
INFO: Open of `OCI:user/pw@my_serveur_name_or_ip/my_sid_db_name'
using driver `OCI' successful.
1: ZZ_EOLIENNES_P
2: ZZ_VILLE_S 
IMPORTANT:

En cas de problème, utiliser cette astuce pour avoir un log complet des problèmes potentiels.

C:ms4w>set CPL_DEBUG=on
C:ms4w>ogrinfo OCI:user/pw@my_server_name_or_ip:port/my_sid_db_name

mapfile

J'ai ici mon layer très simple pour me connecter à Oracle.
   LAYER
     NAME "zz_ville_s"
     TYPE POLYGON
     CONNECTIONTYPE oraclespatial
     CONNECTION "user/pw@my_server_name_or_ip:port/my_sid_db_name"
     DATA "GEOM FROM (SELECT geom FROM ZZ_VILLE_S)"
     PROCESSING "CLOSE_CONNECTION=DEFER"
     CLASS
       NAME "Ville"
       STYLE
         COLOR 25 200 20
         OUTLINECOLOR 80 80 80
       END
     END
   END

shp2img

Par la suite on peut produire un mapfile très très simple pour tester un layer Oracle.   Encore une fois on peut essayer avec l’utilitaire shp2img qui a l’avantage de ne pas passer par la composante Apache.  Ça peut être fort utile pour débuger un service qui ne veut pas démarrer.

C:ms4w>setenv
C:ms4w>set PATH=C:instantclient_11_2;%PATH%
C:ms4w>shp2img -m C:/view-oracle2.map -i png -o C:/temp/test-oracle.png -map_debug 5 -e -67.7 48.7 -67.5 48.8 -l ZZ_VILLE_S
----------
msDrawMap(): rendering using outputformat named png (AGG/PNG).
msDrawMap(): WMS/WFS set-up and query, 0.000s
msDrawMap(): Layer 0 (ZZ_EOLIENNES_P), 0.674s
msDrawMap(): Drawing Label Cache, 0.000s
msDrawMap() total time: 0.677s
msSaveImage(C:/temp/test-oracle.png) total time: 0.079s
msFreeMap(): freeing map at 020BC200.

mapserv via Apache

Sous Windows avec MS4W, il n’y a pas vraiment de configuration à faire.  Le fait d’ajouter dans la Variable d’Environnement PATH, le répertoire du client Oracle ferra en sorte que  celle-ci se retrouvera dans la Variable d’Environnement PATH de Apache.

http://127.0.0.1/cgi-bin/mapserv.exe?map=C:/view-oracle2.map&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&LAYERS=ZZ_VILLE_S&STYLES=&SRS=EPSG:4326&BBOX=-67.7,48.7,-67.5,48.8&WIDTH=400&HEIGHT=300&FORMAT=image/png

mapserv via commande DOS

Autre astuce, on peut aussi utiliser la commande mapserv pour tester la URL avec l'option QUERY_STRING.

C:ms4w>setenv
C:ms4w>set PATH=C:instantclient_11_2;%PATH%
C:ms4w>mapserv “QUERY_STRING=map=C:/view-oracle2.map&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&LAYERS=ZZ_VILLE_S&STYLES=&SRS=EPSG:4326&BBOX=-67.7,48.7,-67.5,48.8&WIDTH=400&HEIGHT=300&FORMAT=image/png”
Simon Mercier (smercier at mapgears.com)

Publish your MBTiles map with Mapcache

I recently tested a feature of MapCache that can let you publish an MBTiles map build with TileMill but without TileStream Server.   So all you have to do is to install Apache module Mapcache from Mapserver web site, add your mbtiles map in your config file and publish your map with OpenLayers.  I use this tutorial to install MapCache it from trunk.

My Mapcache config for my hexagonal GHS map is here.
<cache name="mbtiles" type="mbtiles">
    <dbname_template>/my/path/mbtiles/GasEmission-v5.mbtiles</dbname_template>
</cache>
<tileset name="gazemi">
    <cache>mbtiles</cache>
    <format>PNG</format>
    <grid>g</grid>
</tileset>

I specified a predefined grid "g" as the default "GoogleMapsCompatible" grid. You don't have to specified this gridset it in your mapcache.xml config file.  Look in the mapcache.xml.sample to find out the complete specification of this predefined grid.

In OpenLayers, I use a WMTS Layer for my MBTiles map.
var wmts = new OpenLayers.Layer.WMTS({
 name: "Gas emission (MBTile)",
 url: "http://simonmercier.net/mapcache/wmts/",
 layer: "gazemi",
 matrixSet: 'g',
 format: 'image/png',
 style: 'default',
 gutter:0,buffer:0,isBaseLayer:true,transitionEffect:'resize',
 resolutions:[156543.03392804099712520838,78271.51696402048401068896,39135.75848201022745342925,19567.87924100512100267224,9783.93962050256050133612,4891.96981025128025066806,2445.98490512564012533403,1222.99245256282006266702,611.49622628141003133351,305.74811314070478829308,152.87405657035250783338,76.43702828517623970583,38.21851414258812695834,19.10925707129405992646,9.55462853564703173959,4.77731426782351586979,2.38865713391175793490,1.19432856695587897633,0.59716428347793950593],
 zoomOffset:0,
 units:"m",
 maxExtent: new OpenLayers.Bounds(-20037508.342789,-20037508.342789,20037508.342789,20037508.342789),
 projection: new OpenLayers.Projection("EPSG:900913".toUpperCase()),
 sphericalMercator: true

 });
This is the same mbtiles file but publish with TileStream server.