9
Remplacer des courbes par un bloc de taille équivalente
Python, Rhino3D
25 mai 2017

Sert typiquement à remplacer des éléments (non-blocs) répétés à différentes tailles par un bloc de taille équivalente.

A l'origine je l'utilisais pour insérer un bloc d'arbre 3D à la place de chaque cercle de tronc du plan de géomètre, sachant que ces derniers sont de taille différentes. L'astuce pour obtenir exactement la même taille que le tronc en plan, est de fabriquer un bloc d'arbre 3D dont le tronc mesure exactement 1 m². Ainsi, on peut simplement mettre à l'échelle ce bloc par un facteur égal à la racine carrée de l'aire du tronc géomètre.

import rhinoscriptsyntax as rs
import math

curves = rs.GetObjects("Select closed curves ...")
blockName = rs.GetString("Name of block to copy ...")

if curves:
    for curve in curves:
        if rs.IsCurveClosed(curve):
            surface = rs.AddPlanarSrf(curve)
            area = rs.SurfaceArea(surface)[0] 
            scale = math.sqrt(area)
            point = rs.SurfaceAreaCentroid(surface)[0] 

            rs.DeleteObject(surface)
            rs.InsertBlock(blockName, point, [scale,scale,scale], 0)
        else:
            print "Not a curve or curve not closed."
8
Plaquer des objets sur une surface avec Rhino Python
Python, Rhino3D
1er mar 2017
PutOnSurface.py démo

Script permettant de faire « tomber » des objets sur une face, c'est à dire les placer automatiquement au point d'intersection avec la surface, comme s'ils étaient physiquement posés dessus. L'intersection se calcule verticalement (vecteur Z) mais on peut choisir d'utiliser le z d'un plan de construction. On a également la possibilité d'ajouter une marge supplémentaire (offset) à la chute pour masquer certaines parties de l'objet (racines d'un arbre par exemple).

import rhinoscriptsyntax as rs

objectIds = rs.GetObjects("Select objects")
projectionSrfId = rs.GetObject("Select surface or polysurface to project to", rs.filter.polysurface | rs.filter.surface)

planes = []
planes.append("WorldXY") # add World XY

cplanes = rs.NamedCPlanes() # add named CPlanes
if cplanes:
    for cplane in cplanes:
        planes.append(cplane)

projectionPlane = rs.GetString("Plane to use for direction of projection (World or named CPlanes)", "WorldXY", planes)
offset = rs.GetReal("z Offset", 0)

if projectionSrfId:
    if objectIds:
        for objectId in objectIds:
            box = rs.BoundingBox(objectId,rs.NamedCPlane(projectionPlane), True) #if CPlane name is not found, falls back on WorldXY

            if box:
                bottomRightPt = [0,0,0]
                topLeftPt = [0,0,0]
                bottomLeftPt = [0,0,0]
                for i, point in enumerate(box):
                    if i==1:
                        bottomRightPt = point
                    if i==7:
                        topLeftPt = point
                    if i==3:
                        bottomLeftPt = point
                diagonal = rs.AddLine(topLeftPt, bottomRightPt)
                centroidPt = rs.CurveMidPoint(diagonal)
                rs.DeleteObject(diagonal)

                zVector = rs.VectorCreate(bottomLeftPt, topLeftPt)
                zVector = rs.VectorDivide(zVector, 2)
                zVectorU = rs.VectorUnitize(zVector)
                offsetVector = rs.VectorScale(zVectorU,-offset)

                bottomCentroidPt = [centroidPt[0] + zVector[0], centroidPt[1] + zVector[1], centroidPt[2] + zVector[2]]

                projectedPts = rs.ProjectPointToSurface(bottomCentroidPt, projectionSrfId, zVector)

                if projectedPts:
                    nearestProjectedPtId = rs.PointArrayClosestPoint(projectedPts, bottomCentroidPt)
                    destinationPt = projectedPts[nearestProjectedPtId]
                    rs.AddPoint(destinationPt)

                    projectVector = rs.VectorCreate(destinationPt,bottomCentroidPt)
                    if offset != 0:
                        projectVector = rs.VectorAdd(projectVector,offsetVector)
                    rs.MoveObject(objectId, projectVector)
                else:
                    print "Object cannot be projected on surface."
            else:
                print "Bounding-boxes couldn't be created."

    else:
        print "No object selected."
else:
    print "No surface selected."
7
Nettoyer le Rubberband de PyQGIS
Qgis, Python
18 jan 2016

Avec PyQGIS on fait souvent appel à l'élément RubberBand en phase de test pour afficher un aperçu de ce que l'on calcule. Le problème c'est qu'il reste affiché d'une exécution du code à l'autre. C'est alors pratique d'une cette fonction, à lancer en début de script, pour nettoyer ces « élastiques » avant chaque nouveau lancement.

def removeRubberbands():
    rbb_items = [ i for i in iface.mapCanvas().scene().items() if issubclass(type(i), qgis.gui.QgsRubberBand)]

    for rbb in rbb_items:
        if rbb in iface.mapCanvas().scene().items():
            iface.mapCanvas().scene().removeItem(rbb)
6
Filtrer les clés d'une archive OSM
Qgis, Osm
16 sept 2016

Dans cette note j'expliquais comme on peut réduire à une zone un fichier OSM. De même il est souvent utile de réduire ce fichier aux clés qui nous intéressent uniquement. Pour ce faire, on a le programme osmfilter.

C'est un exécutable qui s'utilise de la même manière que osmconvert, via l'invité de commande en plaçant le programme et le fichier OSM dans le même dossier.

Pour sélectionner les clés OSM, vous pouvez consultez la liste des contenus OpenStreetMaps avec leur nom. Dans mon cas je veux extraire uniquement les villages et hameaux, soit les entrées place:village et place:hamlet. La commande pour mon fichier OSM pour le Luberon sera :

osmfilter luberon.osm --keep="place=village place=hamlet" -o=luberon_village_hamlet.osm

On peut utiliser un astérisque pour pour prendre toute les valeurs d'une clé. Pour toutes les place par exemple on écrira -keep="place=*".

Une fonction autre utile de ce programme permet de lister les clés présentes sur un fichier avec leur quantité. On utilisera la commande suivante :

osmfilter luberon.osm --out-count

On peut également regarder à l'intérieur d'une clé, ici place

osmfilter luberon.osm --out-count=place

Pour ceux qui utilisent l'invité de commande natif de Windows, vous serez peut-être embettez par les très longues listes de clés si vous appliquez la fonction à un fichier OSM complet. La barre de défilement va être bloquée à un certain niveau. Pour visualiser la totalité de la liste, vous devrez utiliser un autre invité de commande, comme ComEmu.

5
Importer des données OSM dans QGIS
Osm, Qgis
9 sept 2016

QGIS s'utilise très bien avec OpenStreetMaps mais pour importer ses données OSM dans QGIS, ce n'est pas aussi direct qu'on pourrait le penser.

La première étape consiste à se procurer des données OSM brutes, via les extraits Geofabrik. L'extension qui nous intéresse est .osm.pbf. Choissez la zone la plus réduite possible selon vos besoins. Les fichiers sont lourds et on a tout à gagner à travailler avec des zones resserrées. Pour affiner son fichier, on peut encore réduire à un cadre géographique précis ou filtrer les clés qui nous intéressent.

En utilisant osmconvert on convertit l'archive OSM .osm.pbf en .osm (décompressé) via la commande suivante :

osmconvert alsace-latest.osm.pbf -o=alsace-latest.osm

Pour importer ce fichier dans QGIS, on va dans Vecteur > OpenStreetMaps > Importer la topologie depuis un XML. Sélectionnez votre fichier .osm et faites OK, puis Fermer. Cette opération créera une base de donnée lisible par QGIS de votre fichier sous l'extension .osm.db.

Pour afficher cette base de donnée sur votre carte, cherchez Vecteur > OpenStreetMaps > Exporter la topologie OSM vers Spatialite. Sélectionnez le fichier .osm.db, cliquez sur Charger depuis une base pour afficher les étiquettes disponibles. Faites votre choix, séléctionnez un type d'export et cliquez sur OK.

Si vous avez des difficultés avec la projection, sachez qu'OpenStreetMaps utilise Pseudo-Mercator, code EPSG:3857.

4
Extraire une zone géographique d'une archive OSM
Qgis, Osm
7 sept 2016

Les extraits Geofrabrik en format .osm.pbf quoique disponibles selon plusieurs découpages administratifs sont généralements très lourds. Il est alors utile de réduire ce fichier à la zone qui vous intéresse. Pour ce faire, on peut utiliser osmconvert.

On commence par noter les coordonnées de la zone qui nous intéresse. Dans mon cas la vallée du Luberon : 43.79, 5.03 (coin sud-ouest, Orgon) à 43.98, 5.56 (coin nord-est, Simiane-la-Rotonde).

On télécharge osmconvert et on met l'éxecutable dans un dossier de travail avec l'archive OSM, dans mon cas france-latest.osm.pbf. Les applications osmconvert et osmfilter s'utilisent dans la console. On utilisera la commande suivante :

osmconvert france-latest.osm.pbf -b=5.03,43.79,5.56,43.98 -o=luberon.osm

Il faut inverser latitude et longitude et placer les coordonnées dans l'ordre sud-ouest puis nord-est après -b.

3
SVG en-ligne comme fond d'un élément avec CSS3 et Javascript
Web, Javascript
16 août 2016

Depuis CSS3 on peut définir une image SVG comme fond d'un élément sans URL, en passant directement l'encodage de l'image. On peut ainsi éviter de charger un fichier externe.

div {
  width: 50vw;
  height: 50vh;
  background-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNCIgaGVpZ2h0PSI2IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0wIDMgTDMgMCBMNCAxIEwyIDMgTDQgNSBMMyA2IFoiIGZpbGw9InJnYmEoMjAwLDIwMCwyMDAsMC43NSkiLz48L3N2Zz4=");
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
}

L'image SVG est encodée en base64 pour l'insérer en ligne directement dans le CSS. Des outils tel que celui-ci permettent cet encodage.

On peut aussi utiliser Javascript pour cela à l'intérieur d'un script. A ce moment là on peut même éviter d'avoir un fichier CSS.

Ici, par exemple, la même image en SVG HTML5 que j'encode et assigne avec Javascript.

var svgMarkup = "<svg width="4" height="6" xmlns="http://www.w3.org/2000/svg"><path d="M0 3 L3 0 L4 1 L2 3 L4 5 L3 6 Z" fill="rgba(200,200,200,0.75)"/></svg>";

var svg64 = window.btoa(svgMarkup);

document.querySelector("div").style.backgroundImage = "url('data:image/svg+xml;base64,"+svg64+"')";
2
Boucle avec arrêt sur pression du bouton Échap avec pyRhino
Python, Rhino3D
19 mai 2016

La bonne astuce pour éviter de bloquer Rhino avec une fonction infinie ou boguée.

import scriptcontext as sc

i = 0
while not sc.escape_test(False):
    print i
    i+=1
1
Assigner à script pyRhino à un bouton dans l'interface de Rhino
Rhino3D, Python
4 2014

Clic droit sur la barre horizontale sous les onglets : « nouveau bouton ».

Insérer le code Python dans la case commande pour le clic droit ou gauche. Le code doit être inséré dans une macro comme suit :

! _NoEcho -_RunPythonScript (

#insert Python code here

)

On peut ajouter un icône pour personnaliser le botuon, par défaut on a droit à un émoticône sourillant.