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
   ...

Encodage de base de données Postgresql

Je suis tombé sur un problème d'encodage de ma base de données OSM dernièrement.  Mon besoin était pourtant très simple: je voulais imprimer sur ma carte les noms de rue en MAJUSCULES.  Je pensais donc utiliser la fonction upper() de postgresql via un mapfile, mais étrangement les caractères accentués ne suivaient pas:

osm= select upper('échap');
upper
-------
éCHAP
(1 row)

L'encodage da la base de données était bien UTF-8 puisque tous les caractères accentués apparaissaient convenablement.  Par contre le "Ctype" était à la valeur C. Dans la documentation de Postgresql on y mentionne que:

initdb initializes the database cluster's default locale and character set encoding. The character set encoding, collation order (LC_COLLATE) and character set classes (LC_CTYPE, e.g. upper, lower, digit) can be set separately for a database when it is created. initdb determines those settings for the template1 database, which will serve as the default for all other databases.

postges=l
Name    | Owner    | Encoding  | Collate |    Ctype
osm     | osm      | UTF8      | C       |    C

On a pas vraiment l'habitude de se soucier de cette valeur, mais elle affecte le fonctionnement de certaines fonctions qui traitent les caractères.  Pour contourner le problème, il y a deux options:

  1. Créer une nouvelle COLLATION dans Postgresql;
  2. Recréer le cluster de base de données;

Option 1

Une solution temporaire de contournement serait de créer une nouvelle COLLATION dans la base de données.  Avant on doit s'assurer d'avoir la Collation sur le serveur:

sudo locale-gen fr_CA.UTF-8 en_CA.UTF-8

Ensuite, on peux ajouter une nouvelle Collation correspondant dans la Base de données:

osm= CREATE COLLATION fr_ca_c (LOCALE='fr_CA.utf8');
osm= select upper('échap' COLLATE "fr_ca_c");;
upper
-------
ÉCHAP
(1 row)

Option 2

Cette option plus drastique sera plus robuste et efficiente à long terme.  Elle consiste à recréer le cluster de base de données pour supporter le bon encodage et la bonne Collation(Ctype). En principe, on doit être en mesure de créer la base de données avec les bons réglages de la façon suivante:

sudo su postgres -c'createdb -E UTF8 --lc-ctype en_CA.UTF-8 -T template0 template_postgis'
sudo su postgres -c'createlang -d template_postgis plpgsql;'
sudo su postgres -c'psql -U postgres -d template_postgis -c"CREATE EXTENSION postgis;"'
# # sudo su postgres -c'psql -U postgres -d template_postgis -c"select postgis_lib_version();"'
sudo su postgres -c'psql -U postgres -d template_postgis -c "GRANT ALL ON geometry_columns TO PUBLIC;"'
sudo su postgres -c'psql -U postgres -d template_postgis -c "GRANT ALL ON spatial_ref_sys TO PUBLIC;"'
sudo su postgres -c'psql -U postgres -d template_postgis -c "GRANT ALL ON geography_columns TO PUBLIC;"'
Par la suite, on doit refaire une nouvelle base de données avec ce template et le problème sera réglé: La création d'une nouvelle base de données avec le template UTF-8:
 sudo su postgres
 createdb -E utf8 -T template_postgis osm
 psql -d osm
 postgres= CREATE USER osm WITH PASSWORD 'osm';
 postgres= GRANT ALL PRIVILEGES ON DATABASE osm to osm;
 postgres=\l
 ----------------------------------
 Name | Owner | Encoding | Collate | Ctype
 osm | osm | UTF8 | C | en_CA.UTF-8
 postgres=\q
Il est possible d'avoir des problèmes de création de cette bd J'ai eu des problèmes de création d'une base de données template Postgresql supportant les caractères accentués et/ou l'encodage UTF-8.
sudo su postgres -c'createdb -E UTF8 --lc-ctype en_CA.UTF-8 -T template0 template_postgis'
createdb: database creation failed: ERROR:  invalid locale name en_CA.UTF-8

Comme mentionné plus haut, Postgresql va créer un moteur cluster (service, fichiers config, fichiers de données, logfile, etc) de base de données et ce moteur utilise les configurations d'encodages au moment de l'installation.  Si par mégarde, l'encodage UTF-8 n'était pas disponible, il faut détruire le cluster Postgresql avec pg_dropcluster, mettre à jour les variables locales d'encodage, et en créer un nouveau cluster avec pg_createcluster:

NOTE: Attention vous allez perdre toutes les BD de votre serveur!
sudo /etc/init.d/postgres stop
sudo su postgres
export LANGUAGE=en_CA.UTF-8
export LANG=en_CA.UTF-8
export LC_ALL=en_CA.UTF-8
locale-gen en_CA.UTF-8
pg_dropcluster --stop 9.1 main
pg_createcluster 9.1 main
exit
sudo /etc/init.d/postgres start
Juste pour être certain que tout est ok avec l'encodage de votre user :
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

Données ouvertes et cartographie Web

Voici donc un lien sur ma conférence à Vision Géomatique 2012:  données ouvertes et cartographie Web.  Vous pouvez accéder aux applications présentées via un lien ajouté sous le vidéo.  J'en profite également pour ajouter le lien de la présentation du conférencier qui me suivait, Nicolas Delffon.   Sa conférence, "La Datavisualisation ou l'art de présenter les données" est une suite logique de ma conférence.  Je vous invite à regarder les liens pertinents à la fin de sa présentation, si le sujet de "dataviz" vous intéresse.

Les 300 conférenciers qui ont assités à Vision Géomatique 2012 ont eu droit à un excellent colloque.  La série de conférences sur l'Open source organisées par OSGeo-qc a été une réussite sur toute la ligne et a attirée beaucoup de gens de différents milieux liés à la géomatique, comme on le souhaitait.

L’échec d’Apple Maps

On avait bien hâte de la voir cette nouvelle Apple Maps.  Étant moi-même maniaque de cartographie Web, j'étais impatient de voir le résultat.  Les aperçus qu'on nous avait servis étaient prometteurs et j'espérais avoir un produit épuré, basé sur des données Open Street Map complété intelligemment avec d'autres sources de données.

L'appréciation générale de la nouvelle application de cartographie semble catastrophique. Selon Snappli (qui se spécialise dans la compression de données pour appareils mobiles), il ne resterait que 4% d'utilisateurs de Apple Maps. J'ai donc fait quelques petites validations dans la région de Québec.

La liste bien garnie de fournisseurs de données fut donc ma première surprise.  Règle générale au Québec, TomTom est le fournisseur officiel pour le réseau routier.  Pas vraiment surprenant, car pour la fonction de navigation routière ("turn-by-turn") il fallait obligatoirement utiliser TomTom.  Pour le reste on ne retrouve absolument rien de Open Street Maps pour les informations de contexte dans la carte!

Le principe fondamental "homogénéité" des données est pourtant élémentaire en cartographie.  L'utilisation de couches généralisées (hydrographie, boisée, etc) sans niveau de précision suffisant à cette échelle est une erreur de débutant, pour ne pas dire inacceptable!  La rivière Chaudière n'aboutit même pas jusqu'au fleuve, puisqu'on devrait gérer le réseau hydrique filamentaire à cette échelle.   Avec un peu de déception, je n'ai même pas poussé plus loin l'analyse, je vous laisse le soin de vous amuser les nombreux exemples d'erreurs répertoriées sur ce site qui deviennent pour le moins gênants pour le géant Apple.  Forcé d'admettre l'échec lamentable, c'est sans surprise que Tim Cook lui-même a du fournir des excuses aux utilisateurs iOS 6!

Apple a été tenté par des fonctionnalités sexy comme la navigation "turn-by-turn", le 3D, la reconnaissance vocale Siri, etc.   Mais on a oublié de se concentrer sur la base: une belle carte pratique à utiliser et pertinente!  La compagnie TomTom n'a pas été à la hauteur pour fournir ce dont Apple avait besoin.  Ils font de bons appareils de navigation automobile, mais TomTom n'a pas ce qu'il faut pour produire une cartographie Web de qualité pour les appareils mobiles.   De mon avis personnel, l'esthétique de la cartographie Web de TomTom dans la région de Québec est loin du minimum requis qu'une carte doit avoir de nos jours.  Je vous laisse le soin de voir vous-mêmes.

Chargement Shapefiles dans PostGIS en batch

Il existe un moyen vraiment simple de charger le contenu d'un répertoire de shapefiles dans une base de données Posgresql/PostGIS avec les outils, shp2pgsql et psql. L'utilitaire permettant de charger les commandes SQL (psql) interdit la saisie d'un mot de passe en clair dans une ligne de commande. Pour contourner ce problème, on peut configurer un fichier .pgpass. Le contenu de ce fichier prend cette forme: nom_hote:port:database:nomutilisateur:motdepasse Il est impératif de changer les droits d'accès à ce fichier sinon psql n'en tiendra pas compte.
sudo vim ~/.pgpass
sudo chmod 0600 ~/.pgpass
Par la suite, j'utilise ce petit bash pour générer les lignes de commandes. Ici je transforme le nom du shapefiles en minuscule parce que Postrgresql est "case sensitive" et c'est plus propre de garder les noms de tables en minuscules:
vim shp2sqlfile.sh
---> ajouter ces lignes:
#!/bin/bash
for f in *shp
do
  name=$(basename $f .shp| tr '[A-Z]' '[a-z]')
  echo /usr/lib/postgresql/9.1/bin/shp2pgsql -c -I -W "latin1" -s "EPSG:4326" -g "the_geom" $f $name "| psql -d my_geodb -h localhost -U postgres -w"
done
Ne reste plus qu'a exécuter le batch et rediriger dans un fichier de commandes:
$ sh shp2sqlfile.sh >batch.sh

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.

Trop verte la carte de “La Route Verte”

L'organisme gouvernemental "Vélo Québec" a lancé dernièrement son nouveau site Web de cartographie dynamique "La Route Verte".  Loin de vouloir faire du négativisme et encore moins, prétendre que je suis mieux qu'un autre en ce domaine, mais pour ce billet je désire simplement émettre mon opinion "cartographique" de l'application Web de "La Route verte" sans même m'attarder au design du site Web.  Ceux qui me connaissent savent je suis maniaque de cartes et de vélo alors c'est un peu normal!

1 ) Les fonctionnalités d'itinéraires fait avec pgRouting dans la base de données Postgresql/PostGIS fonctionne à merveille.  Il est bon de souligner qu'il s'agit (à ma connaissance) d'un des premiers site Web "Grand public" à utiliser cette technologie avec la base de données Adresse Québec.  Le calcul de route est rapide et on a même une très bonne description d'itinéraire ("turn-by-turn"); un incontournable pour ce type de fonctionnalité.  De plus, on y retrouver quantité d'information utile est abondante.

2 ) L'idée de présenter une application selon les régions administratives est comment dire, très "fonctionnaire"! Le "Grand public", puisque c'est bien pour eux que le site est dédié, en a peu à faire... Présenter les régions de cette façon donne aussi la fausse impression que certaines régions sont très importantes.  Par exemple, la région "Saguenay" m'apparaît trop apparente et semble démontrer que c'est une grosse région densément habitée alors que ce n'est pas réellement le cas.  Le "Manicouagan" est une magnifique région à visiter certes, mais en vélo, à moins d'être fanatique du cardio (ou un expert) ça l'est beaucoup moins.  Il faudrait peut-être recentrer l'application sur "La Route Verte" et ne pas tenir compte des "Régions Administratives".

3 ) Y a beaucoup de vert dans la carte.  Si on passe tous les niveaux de zoom on pourra compter cinq couleurs de vert pour cartographier 3 types d'entités complètement différentes: des routes, des parcs, des régions administratives.  Si on désire mettre en évidence "La Route Verte", il faudrait à mon avis, ne garder que "La Route Verte" verte pour ne pas brouiller le message.  Tout le reste devrait être moins apparent pour l'oeil.

Pour qu'un utilisateur puisse bien gérer les informations cartographiées dans une application, on devrait se limiter à moins de cinq couleurs.  Vrai que c'est souvent difficile, mais on devrait toujours avoir cette cible.  D'ailleurs, Marie-Josée Proulx rappelle bien dans "Le Data-ink ratio appliqué à la cartographie" cette limite dans le contexte de choroplèthes, mais le même concept s'applique aussi pour ce genre de carte.

Le Web n'est pas comme une carte imprimée.  L'image qui suit est un bel exemple de la surutilisation du vert dans cette carte Web.  "La Route Verte" se perd dans un spaghetti de lignes grises, pleines et pointillés.  De plus je ne comprends pas l'utilité des limites territoriales  pointillées dans ce genre de carte, sinon de se confondrent avec les routes.

4 ) Toujours au sujet de  l'image précédente, on ne devrait peut-être éviter de présenter autant d'écussons de carte routière dans une carte de piste cyclable.  À cette échelle, nous avons 25 écussons de routes pour les voitures et seulement 3 pour "La Route Verte".  De plus, ça me semble trop d'informations à gérer pour l'oeil et on perd le l'objectif du sujet. On aurait peut-être dû utiliser les écussons que pour les numéros de "La Route Verte" et réduire tous les autres à de simples rectangles numérotés bleu et vert.   Enfin je suis surpris de voir de gros bateaux noirs à cette échelle!  Encore un fois ça surcharge la carte pour rien et à ce niveau nous n'avons pas besoin de les voir.

5 ) "too-much-information"!  Ça vaut aussi pour les cartes.  Trop de routes, trop d'icônes un par dessus l'autres, et encore une fois 5 couleurs vertes à gérer pour l'oeil.   Trop d'informations et ça devient difficile à assimiler!  Toutes les routes autres que "cyclables" devraient devenir secondaires et s'effacer pour laisser parler "La Route Verte".

6 ) OÙ SONT LES CÔTES?  Si vous faites du vélo comme moi tout l'été vous savez très bien que c'est la première chose qu'on veut savoir! Ne pas mettre cette information capitale dans la carte pour vélo montre qu'on a oublié le public cible.  D'accord nous avons quelques icônes mais ils ne montrent pas toutes montées.  Ajouter les courbes de niveau, un fond 3D ou un graphique vertical d'élévation (dans l'itinéraire) auraient grandement aidé.

7 ) J'adore le fait qu'on trouver beaucoup d'informations utiles sur la carte.  C'est toujours très pratique pour un cycliste de savoir où il pourra se ravitailler en eau et se reposer.   Épiceries, dépanneurs, abreuvoirs et aires de repos, autant d'informations utiles à retrouver sur une carte de vélo.

8 ) Je comprends qu'on veut garder le focus sur "La Route Verte"  mais pourquoi ne pas ajouter les autres circuits de vélo?  C'est une bonne intention de publiciser "La Route Verte" à l'aide d'une application cartographique Web, mais sans les autres circuits, ça devient une application moins intéressante pour planifier un week-end vélo.  Dans les Cantons de l'Est on trouvera de magnifique parcours de vélo comme "La route des vins" par exemple.  Je trouverais interessant des les voir.

9 ) Autre question de cycliste: Y a t'il un accotement (2 à 3 pieds minimum) pour cycliste et quel est le débit de circulation sur cette route?  L'information n'a pas besoin d'être parfaite, mais elle donne une bonne indication du type de randonnée à prévoir.

10 ) La gestion des couches est à mon avis superflu et est contre indiquée pour cette carte.  Ce type de fonctionnalité  typiquement "géomatiques" me semble inutile et ne semble pas être en mesure de bien servir la clientèle intéressée.  Toutes ces informations de couches doivent se fondre à la carte en un tout cohérent.  Et puis qu'est-ce que c'est au juste un "Village-relais" et est-il d'intérêt pour le cycliste?

11 )  Une très bonne pratique,  c'est maintenant une orientation gouvernementale, serait de mettre un lien sur les sources de données ouvertes pour les mettre en valeur et permettre à d'autres de les utiliser.

imposm and PostGIS 2.0

I recently load OSM planet in PostGIS 2.0.  It works but you have to created manually your database:
 sudo su postgres
 createdb -E utf8 -O postgres osmplanet
 psql -d osmplanet -c "CREATE EXTENSION postgis;"
 createuser --no-superuser --no-createrole --createdb osm
 psql -d osmplanet -c "ALTER USER osm WITH PASSWORD 'osm';"
 psql -d osmplanet -f /usr/local/lib64/python2.7/site-packages/imposm/900913.sql
 psql -d osmplanet -c "ALTER TABLE geometry_columns OWNER TO osm;”
 psql -d osmplanet -c "ALTER TABLE spatial_ref_sys OWNER TO osm;"
 sudo vim /var/lib/pgsql/data/pg_hba.conf
 -->add this line "host osmplanet osm  127.0.0.1/32 md5"

 sudo virtualenv venv
 source venv/bin/activate
 venv/bin/pip install imposm
I use multiprocessing option with "imposm" in one line command read/write
 sudo imposm -c 8 --overwrite-cache --read --write --database osmplanet planet-latest.osm.bz2
Final database is 66 GB at this moment
postgres@osm:/home/smercier> psql -c "SELECT pg_size_pretty(pg_tablespace_size('pg_default'));" osm
 pg_size_pretty
----------------
 66 GB
(1 row)

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)

Script SQL Oracle Spatial – Chargement données test

Ce script très simple de chargement de données test, permettra de démarrer rapidement avec Oracle Spatial. Je l'utilise régulièrement pour développer, tester certaines requêtes ou procédures et pour ce blog.  Les données test sont des points d'éoliennes (ZZ_EOLIENNES_P) liés par une Foreign key à une surface ville (ZZ_VILLE_S)
-- #########################################
-- Table ZZ_EOLIENNES_P

CREATE TABLE ZZ_EOLIENNES_P
(
 EOLIENNES_ID NUMBER(10),
 VILLE_ID NUMBER(10),
 DESC_ VARCHAR2(200),
 GEOM MDSYS.SDO_GEOMETRY
);
ALTER TABLE ZZ_EOLIENNES_P  ADD (CONSTRAINT ZZ_EOLIENNES_P_PK PRIMARY KEY (EOLIENNES_ID));

INSERT INTO USER_SDO_GEOM_METADATA (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID)VALUES ('ZZ_EOLIENNES_P', 'GEOM', MDSYS.SDO_DIM_ARRAY(MDSYS.SDO_DIM_ELEMENT('X',-83,-54, 0.000005),MDSYS.SDO_DIM_ELEMENT('Y',43, 63,0.000005)),4326);

DROP INDEX ZZ_EOLIENNES_P_IS;
CREATE INDEX ZZ_EOLIENNES_P_IS ON ZZ_EOLIENNES_P("GEOM") INDEXTYPE IS MDSYS.SPATIAL_INDEX;

-- Options possibles
-- PARAMETERS (' geodetic=false layer_gtype=COLLECTION SDO_COMMIT_INTERVAL=100 tablespace=MY_TABLE_SPACE INITIAL=200K NEXT=64K MINEXTENTS=1 MAXEXTENTS=200 PCTINCREASE=0');

-- Ajout de quelques données
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (0, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.59743928909302,48.79196964612326,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (1, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.59743928909302,48.79196964612326 ,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (2, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.59606599807739,48.79290616097424 ,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (3, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.59743928909302,48.79196964612326 ,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (4, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.59836196899414,48.79070090566935,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (5, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.59883940219879,48.789729007576526,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (6, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.59963870048523,48.78895854407517,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (7, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.60334014892578,48.78498940933159,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (8, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.60491728782654,48.784289564951145,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (9, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.6097023487091,48.78265655677682,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (10, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.61019051074982,48.781429998379544,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (11, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.6094663143158,48.78035187474781,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (12, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.60951459407806,48.77851370892812,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (13, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.61433720588684,48.77799406138623,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (14, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.6143479347229,48.77916414452893,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (15, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.61436402797699,48.78015038677945,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (15, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.69213199615479,48.76361501275503,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (16, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.69311904907227,48.76106899360166,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (17, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.69861221313477,48.759909097641696,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (18, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.69869804382324,48.7569809484446,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (19, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.69979238510132,48.75425068455974,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (20, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.70485639572144,48.75188810849875,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (21, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.7077317237854,48.74953956949669,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (22, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.71073579788208,48.74755878862284,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (23, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.71384716033936,48.74552133255116,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (24, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.7171516418457,48.740667892655736,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (25, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.72170066833496,48.74253574424521,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (26, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.72356748580933,48.745761869860054,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (27, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.73457527160645,48.75017623248125,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (28, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.73459672927856,48.75318966115,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (29, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.74504661560059,48.75215691017849,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (30, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.73092746734619,48.755467297157985,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (31, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.72560596466064,48.75379798397896,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (32, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.72288084030151,48.758805757115624,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (33, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.72876024246216,48.773232142600904,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (34, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.72545576095581,48.771464426020316,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (35, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.7218508720398,48.76937136911909,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (36, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.71783828735352,48.76710850675318,NULL), NULL, NULL));
INSERT INTO ZZ_EOLIENNES_P (EOLIENNES_ID, GEOM) VALUES (37, SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-67.71483421325684,48.76515670605726,NULL), NULL, NULL));

SELECT count(*) from ZZ_EOLIENNES_P;
------
-- 38 rec

-- #########################################
--Table VILLE_S
DROP TABLE ZZ_VILLE_S;
CREATE TABLE ZZ_VILLE_S
(
 VILLE_ID NUMBER(10),
 NOM VARCHAR2(200),
 GEOM MDSYS.SDO_GEOMETRY
);
ALTER TABLE ZZ_VILLE_S ADD (CONSTRAINT VILLE_S_PK PRIMARY KEY (VILLE_ID));

INSERT INTO USER_SDO_GEOM_METADATA (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID)VALUES ('ZZ_VILLE_S', 'GEOM', MDSYS.SDO_DIM_ARRAY(MDSYS.SDO_DIM_ELEMENT('X',-83,-54, 0.000005),MDSYS.SDO_DIM_ELEMENT('Y',43, 63,0.000005)),4326);

DROP INDEX VILLE_S_IS;
CREATE INDEX ZZ_VILLE_S_IS ON ZZ_VILLE_S("GEOM") INDEXTYPE IS MDSYS.SPATIAL_INDEX ;
--Options possibles
-- PARAMETERS (' geodetic=false layer_gtype=POLYGONE SDO_COMMIT_INTERVAL=100 tablespace=MY_TABLESPACE INITIAL=200K NEXT=64K MINEXTENTS=1 MAXEXTENTS=200 PCTINCREASE=0')

-- Ajout de quelques données
INSERT INTO ZZ_VILLE_S VALUES(10, 'polygon_sud',  SDO_GEOMETRY( 2003, 4326, NULL,
                                              SDO_ELEM_INFO_ARRAY(1,1003,1),
                                            SDO_ORDINATE_ARRAY(-67.75,48.739,-67.596,48.739, -67.596,48.760,-67.750,48.760,-67.75,48.739)));
INSERT INTO ZZ_VILLE_S VALUES(20, 'polygon_nord',  SDO_GEOMETRY( 2003, 4326, NULL,
                                              SDO_ELEM_INFO_ARRAY(1,1003,1),
                                            SDO_ORDINATE_ARRAY(-67.750,48.760,-67.596,48.760,-67.596,48.793,-67.750,48.793,-67.750,48.760)));

SELECT count(*) from ZZ_VILLE_S;
------
-- 2 rec

-- #########################################
-- Test requêtes spatiales Licence Oracle Locator

SELECT p.EOLIENNES_ID,v.NOM FROM ZZ_EOLIENNES_P p, ZZ_VILLE_S v
WHERE SDO_RELATE(v.GEOM, p.GEOM, 'mask=anyinteract  querytype=window') = 'TRUE'
AND v.VILLE_ID=10;
------
--16 rec

SELECT p.EOLIENNES_ID,v.NOM FROM ZZ_EOLIENNES_P p, ZZ_VILLE_S v
WHERE SDO_RELATE(v.GEOM, p.GEOM, 'mask=anyinteract  querytype=window') = 'TRUE'
AND v.VILLE_ID=20;
------
--22 rec

-- retourner les 7 points les plus proche de lat/long spécifique
SELECT e.EOLIENNES_ID
FROM ZZ_EOLIENNES_P e
WHERE SDO_NN(e.geom,mdsys.sdo_geometry(2001, 4326,
mdsys.sdo_point_type(-67.530932 ,48.843439,NULL), NULL,
   NULL),  'sdo_num_res=7') = 'TRUE';

-- Générer un buffer de 1 kilomètre autour des eoliennes.
SELECT e.EOLIENNES_ID, SDO_GEOM.SDO_BUFFER(e.GEOM, m.diminfo, 1, 'unit=km arc_tolerance=0.05')
 FROM ZZ_EOLIENNES_P e, user_sdo_geom_metadata m
 WHERE m.table_name = 'ZZ_EOLIENNES_P'
 AND m.column_name = 'GEOM';

-- #########################################
-- Appliquer un Foreign Key aux EOLIENNES_P vs la ville

UPDATE ZZ_EOLIENNES_P SET VILLE_ID=10 WHERE eoliennes_id IN (
SELECT p.eoliennes_id FROM ZZ_EOLIENNES_P p, ZZ_VILLE_S v
WHERE SDO_RELATE(v.GEOM, p.GEOM, 'mask=anyinteract  querytype=window') = 'TRUE'
AND v.VILLE_ID=10);
---
--16 lignes mises à jour

UPDATE ZZ_EOLIENNES_P SET VILLE_ID=20 WHERE eoliennes_id IN (
SELECT p.eoliennes_id FROM ZZ_EOLIENNES_P p, ZZ_VILLE_S v
WHERE SDO_RELATE(v.GEOM, p.GEOM, 'mask=anyinteract  querytype=window') = 'TRUE'
AND v.VILLE_ID=20);
---
--22 lignes mises à jour

ALTER TABLE ZZ_EOLIENNES_P ADD
      CONSTRAINT ZZ_EOLIENNES_P_FK
      FOREIGN KEY (VILLE_ID)
      REFERENCES ZZ_VILLE_S(VILLE_ID)
      ON DELETE CASCADE;