Suite

Intersection des lignes pour obtenir des croisements en utilisant Python avec QGIS ?

Intersection des lignes pour obtenir des croisements en utilisant Python avec QGIS ?


J'ai un ensemble de lignes représentant des lignes de bus. Certaines lignes se chevauchent et empruntent les mêmes routes.

Je suis capable d'extraire les nœuds.

Cependant, je souhaite extraire uniquement les croisements comme celui-ci :

Comment puis-je faire ceci? Je cherche des moyens avec QGIS ou Python.

J'ai essayé la méthode d'intersection de GDAL Python mais cela ne me renvoie essentiellement que les sommets.

La méthode Intersections de lignes de QGIS me renvoie les croisements si deux lignes se croisent. Cependant, dans le cas où deux lignes de bus parcourent une grande partie de leur trajet sur la même route, cela ne me donne pas le point de se confondre.


Les nœuds :

Vous voulez deux choses, les points d'extrémité des polylignes (sans nœuds intermédiaires) et les points d'intersection. Il y a un problème supplémentaire, certains points d'extrémité de polylignes sont également des points d'intersection :

Une solution consiste à utiliser Python et les modules Shapely et Fiona

1) Lisez le fichier de formes :

à partir de shapely.geometry import Point, shape import fiona lines = [shape(line['geometry']) pour la ligne dans fiona.open("your_shapefile.shp")]

2) Trouver les points d'extrémité des lignes (comment obtenir les points d'extrémité d'une polyligne ?) :

endpts = [(Point(list(line.coords)[0]), Point(list(line.coords)[-1])) for line in lines] # aplatit la liste résultante en une simple liste de points endpts= [ pt pour la sous-liste dans endpts pour pt dans la sous-liste]

3) Calculer les intersections (en itérant à travers des paires de géométries dans la couche avec le module itertools). Le résultat de certaines intersections est MultiPoints et nous voulons une liste de points :

import itertools inters = [] pour line1, line2 dans itertools.combinations(lines, 2) : si line1.intersects(line2) : inter = line1.intersection(line2) if "Point" == inter.type: inters.append( inter) elif "MultiPoint" == inter.type: inters.extend([pt for pt in inter]) elif "MultiLineString" == inter.type: multiLine = [line for line in inter] first_coords = multiLine[0]. coords[0] last_coords = multiLine[len(multiLine)-1].coords[1] inters.append(Point(first_coords[0], first_coords[1])) inters.append(Point(last_coords[0], last_coords[ 1])) elif "GeometryCollection" == inter.type: for geom in inter: if "Point" == geom.type: inters.append(geom) elif "MultiPoint" == geom.type: inters.extend([ pt pour pt dans geom]) elif "MultiLineString" == geom.type: multiLine = [line pour ligne dans geom] first_coords = multiLine[0].coords[0] last_coords = multiLine[len(multiLine)-1].coords [1] inters.append(Point(first_coords[0], first_coords[1])) inters.append(Point(last_coords[0], last_coords[1]))

4) Éliminez les doublons entre les points d'extrémité et les points d'intersection (comme vous pouvez le voir sur les figures)

result = endpts.extend([pt pour pt dans inters si pt pas dans endpts])

5) Enregistrez le fichier de formes résultant

from shapely.geometry import mapping # schéma du shapefile schema = {'geometry': 'Point','properties': {'test': 'int'}} # création du shapefile avec fiona.open('result.shp ','w','ESRI Shapefile', schema) en sortie : pour i, pt dans énumérer (résultat): output.write({'geometry':mapping(pt), 'properties':{'test':i }})

Résultat final:

Les segments de ligne

Si vous voulez également les segments entre les nœuds, vous devez "planariser" (Graphique planaire, aucune arête ne se croise) votre fichier de formes. Cela peut être fait par la fonction unary_union de Shapely

de shapely.ops import unary_union graph = unary_union(lines)


Calculer les intersections d'une polyligne et d'une ligne

J'ai écrit un script pour calculer/interpoler les nœuds d'une polyligne à une latitude donnée, input_lat . Cela fonctionne pour mon objectif, mais je me demande s'il existe un moyen meilleur et plus optimisé de le faire.

L'idée est de d'abord vérifier s'il y a une correspondance exacte, c'est-à-dire s'il y a des nœuds à la latitude exacte donnée, donc je soustrait la latitude d'entrée à la liste des nœuds (la liste des latitudes) et s'il y a des zéros, je peux simplement lire la longitude de ceux-ci.

La plupart du temps, le croisement se fera entre 2 nœuds consécutifs, l'un étant en dessous (ou au-dessus) et le suivant étant de l'autre côté de l'input_lat (le nœud au-dessus conservera une valeur positive après avoir soustrait input_lat et le nœud en dessous d'un valeur négative) donc dans ma liste de latitudes je détecte cela en vérifiant un changement de signe entre deux éléments consécutifs, donc je multiplie chaque élément par le suivant : si le résultat est négatif, il y a un changement de signe.
Je le fais avec np.multiply(latitudes[1:], latitudes[:-1]) mais je me demande s'il existe un meilleur moyen.

Le script est censé s'exécuter à partir de QGIS, de sorte que toutes les bibliothèques qgis.* importées et les commandes Qgs sont spécifiques à QGIS.


Osmnx¶

Cette semaine, nous allons explorer un nouveau module Python passionnant appelé osmnx qui peut être utilisé pour récupérer, construire, analyser et visualiser des réseaux routiers à partir d'OpenStreetMap. En bref, il offre des fonctions très pratiques pour télécharger des données à partir d'une carte OpenStreet, analyser les propriétés des réseaux routiers OSM et effectuer un routage de réseau basé sur la marche, le vélo ou la conduite.

Il existe également un article scientifique décrivant le package :

  • Boeing, G. 2017. “OSMnx : nouvelles méthodes d'acquisition, de construction, d'analyse et de visualisation de réseaux routiers complexes.” Computers, Environment and Urban Systems 65, 126-139. doi:10.1016/j.compenvurbsys.2017.05.004

Vous pouvez essayer un filtrage passe-bas du signal d'entrée pour obtenir des passages par zéro plus fluides (ou même un filtrage passe-bande si vous avez une bonne idée de l'emplacement de la fréquence de l'onde sinusoïdale). Le risque est que si des informations de phase précises à l'échantillon sont essentielles à votre application, le décalage supplémentaire du filtre pourrait être un problème.

Autre approche : au lieu d'essayer de transformer l'onde sinusoïdale en onde carrée, que diriez-vous de faire en sorte qu'un oscillateur à onde carrée indépendant s'aligne en phase/fréquence avec l'onde sinusoïdale ? Cela peut être fait avec une boucle à verrouillage de phase.

Ce que vous avez montré est certainement un détecteur de passage par zéro. Quelques éléments me viennent à l'esprit qui pourraient améliorer votre situation :

Si vous avez du bruit en dehors de la bande de votre signal (ce qui est presque certainement le cas, puisque votre entrée est un son pur), alors vous pouvez améliorer le rapport signal/bruit en appliquant un filtre passe-bande autour du signal d'intérêt . La largeur de bande passante du filtre doit être choisie en fonction de la précision avec laquelle vous connaissez la fréquence sinusoïdale a priori. En réduisant la quantité de bruit présent sur la sinusoïde, le nombre de faux passages à zéro et leur gigue sur les temps de passage corrects seront réduits.

  • En passant, si vous n'avez pas de bonnes informations à l'avance, vous pouvez utiliser une technique plus sophistiquée connue sous le nom de amplificateur de ligne adaptatif, qui, comme son nom l'indique, est un filtre adaptatif qui améliore un signal d'entrée périodique. Cependant, il s'agit d'un sujet quelque peu avancé et vous avez généralement une assez bonne idée de la fréquence de votre signal pour que ce type d'approche ne soit pas nécessaire.

En ce qui concerne le détecteur de passage à zéro lui-même, vous pouvez ajouter une certaine hystérésis au processus. Cela empêcherait la génération de croisements mesurés parasites supplémentaires autour de l'instant de croisement correct. L'ajout d'hystérésis au détecteur peut ressembler à ceci :

Effectivement, vous ajoutez un état à votre détecteur de passage à zéro. Si vous pensez que le signal d'entrée a une valeur positive, vous avez besoin que le signal descende en dessous d'une valeur seuil choisie -T afin de déclarer un vrai passage à zéro. De même, vous exigez que le signal remonte au-dessus du seuil T pour déclarer que le signal est redevenu positif.

Vous pouvez choisir les seuils comme vous le souhaitez, mais pour un signal équilibré comme une sinusoïde, il est logique qu'ils soient symétriques par rapport à zéro. Cette approche peut vous aider à obtenir une sortie plus nette, mais elle ajoutera un certain délai en raison du fait que vous mesurez en fait des franchissements de seuil non nuls au lieu de passages à zéro.

Comme les pichenettes l'ont suggéré dans sa réponse, une boucle à verrouillage de phase serait probablement la meilleure solution, car une PLL fait à peu près exactement ce que vous essayez de faire. En bref, vous exécutez un générateur d'ondes carrées qui fonctionne en parallèle avec la sinusoïde d'entrée. La PLL effectue des mesures de phase périodiques sur la sinusoïde, puis filtre ce flux de mesures afin de piloter la fréquence instantanée du générateur d'onde carrée. À un moment donné, la boucle se verrouillera (espérons-le), auquel point l'onde carrée devrait être verrouillée en fréquence et en phase avec la sinusoïde de l'entrée (avec une certaine quantité d'erreur, bien sûr, rien en ingénierie n'est parfait).


Syntaxe

Les entités à double ligne en entrée, telles que les enveloppes de route, à partir desquelles les lignes médianes sont dérivées.

Classe d'entités en sortie à créer.

Définit la largeur maximale des entités à deux lignes pour dériver la ligne d'axe. Une valeur doit être spécifiée et elle doit être supérieure à zéro. Vous pouvez choisir une unité préférée, la valeur par défaut est l'unité caractéristique.

Définit la largeur minimale des entités à deux lignes pour dériver la ligne d'axe. La largeur minimale doit être supérieure ou égale à zéro, et elle doit être inférieure à la largeur maximale. La valeur par défaut est zéro. Vous pouvez spécifier une unité préférée, la valeur par défaut est l'unité de fonction.


2 réponses 2

En ce qui concerne votre question, je pense que si vous essayez de croiser vos axes z, $z_1 imes z_2$, vous obtiendrez l'axe x que vous recherchez (orthogonal aux deux axes). Étant donné que les deux axes z se croisent, votre valeur $r$ ou $a$, selon votre notation, sera zéro car le nouvel axe x se trouve sur votre axe z précédent.

La règle de la main droite vous aidera ici. Pour une articulation i donnée : votre pouce correspondra à l'axe Zi-1, votre index correspondra à l'axe Zi et votre majeur correspondra à l'axe Xi.


2 réponses 2

Il existe un long chemin avec TimeSeriesThread ou l'interpolation et la soustraction des courbes pour trouver les passages à zéro, mais il existe également un raccourci où vous pouvez extraire les lignes du tracé et obtenir immédiatement les intersections :

. et si vous êtes d'accord avec l'utilisation de fonctions non documentées, vous pouvez également écrire :

Trouvez où les différences entre deux séries temporelles changent de signe en soustrayant la deuxième série de la première. Les transitions montrent les dates où les deux séries se croisent.

Aucune des valeurs d'intersection n'est égale (aucune des différences n'est nulle).

Trouvez les dates avant et après chaque valeur de transition. Obtenez les valeurs pour chaque date à partir des valeurs lissées. Tracez les dates avec le tracé de données lissé.

Voici les dates avant et après chaque intersection entre les séries temporelles.


6 réponses 6

[J'ai donné une réponse similaire il y a quelque temps dans StackOverflow ou MSE mais maintenant je ne peux pas la trouver.]

Une façon consiste à suivre la solution de l'ODE qui dépasse la différence if[x]-g[x] . Utilisez WhenEvent pour enregistrer les croisements d'axes. Cela trouvera tous les zéros qui n'ont pas de multiplicité (c'est-à-dire qui se croisent transversalement). Devrait également trouver ceux qui sont de multiplicité impaire puisqu'ils traversent toujours l'axe, bien qu'avec une pente de zéro.

Je suppose que la question essentielle est de savoir comment résoudre une équation dans laquelle un terme implique une InterpolatingFunction . Si c'est simplement pour tracer les points, alors j'utiliserais la méthode d'André. Sinon, j'utiliserais une approche comme celle de Daniel Lichtblau avec une petite modification. Le reste est essentiellement un commentaire étendu à la réponse de Daniel et au commentaire de George2079.

NDSolve est particulièrement bien adapté à cette tâche. Les raisons sont multiples. Premièrement, le processus d'intégration s'étend sur tout l'intervalle et est susceptible de découvrir toutes les racines où un changement de signe se produit. Deuxièmement, les algorithmes de recherche de racine sont invoqués lorsqu'un événement dans WhenEvent est détecté. De plus, la condition initiale d'un événement sera proche de la racine et les algorithmes de recherche de racines convergeront relativement rapidement, si rapidement que dans l'exemple de l'OP, le temps qu'il a fallu à NDSolve pour trouver toutes les racines était à peu près le même que l'utilisation de FindRoot sur chacune d'entre elles. Donc NDSolve est utilisé pour suivre la fonction et invoquer Enfin, si la fonction est hautement oscillante, NDSolve adaptera sa taille de pas de sorte que les passages par zéro ne soient pas (probablement) manqués.

Vous obtenez des résultats plus précis si l'équation à résoudre est transmise à WhenEvent au lieu de y[x] == 0 . Cela semble un principe important, même si la fonction d'interpolation n'est qu'approximative. Le symbole y[x] représente une autre couche d'approximation, il semble plus clair d'utiliser l'équation réelle dont la solution est souhaitée. Cela prend un peu moins de temps si vous demandez qu'aucune solution ne soit retournée. Dans l'exemple ci-dessous, j'ai ajouté un composant oscillatoire.

L'utilisation de l'équation réelle au lieu de y[x] produit des résultats plus précis :


2 réponses 2

Je reçois également la même chose lors de l'ouverture directe des fichiers .shp et de la sélection de qgis-bin comme programme à utiliser. Mais je charge toujours QGIS via l'icône du bureau. Je ne sais pas exactement pourquoi, mais je suppose que l'icône du bureau exécute simultanément les 2 fichiers suivants afin de charger QGIS (c'est la cible que vous pouvez voir dans les propriétés de l'icône):


"C:Program FilesQGIS Valmierabinnircmd.exe" exec hide C:PROGRA

Mon conseil est de charger QGIS via l'icône du bureau (qui porte généralement un nom comme "QGIS Desktop 2.4.0"), puis de faire glisser/déposer votre fichier de formes de cette façon.

Comme Steve l'a déjà découvert, il est possible de faire glisser votre shapefile vers le fichier "qgis.bat" qui chargera QGIS et le shapefile lui-même.

Joseph, ça marche ici ! J'ai maintenant également re-mappé les fichiers .shp directement sur qgis.bat afin que je puisse les cliquer directement.

Brillant! Tu viens de m'apprendre quelque chose aussi :)

Mapper le fichier de formes sur QGIS est génial.

@Branco, c'est certainement mon pote ! Cela ne fait que 9 mois que j'ai commencé à utiliser des logiciels SIG de toutes sortes (j'ai choisi QGIS) et il y a encore beaucoup de choses à apprendre :)

J'ai eu ce problème récemment. Aucune des réponses ici n'a fonctionné pour moi.

Lorsque j'utilise QGIS 2.4, je travaille généralement avec mon ordinateur portable branché sur un écran LCD plus grand. Ensuite, je projette le tout dans le deuxième moniteur à l'aide d'un adaptateur HDMI. Ainsi, lorsque j'ai éteint le deuxième moniteur, QGIS a commencé à afficher le message 'qgis_core.dll missing'.

Pour résoudre ce problème, j'ai appuyé sur la touche « Fn + F1 » (ordinateur portable Dell) et j'ai choisi la première option pour projeter les images uniquement sur le premier moniteur. Ensuite, le QGIS a de nouveau fonctionné.


5 réponses 5

La seule raison pour laquelle deux lignes ne se coupent pas est si elles sont parallèles. Être parallèle revient à avoir la même pente. Ainsi, dans votre exemple, la ligne $1$ a une pente

Étant donné que ces deux nombres ne sont pas les mêmes, les lignes ne sont pas parallèles et elles se coupent quelque part.

Maintenant, si ce que vous considérez n'est que le segment de droite entre les deux points, je considérerais d'abord les pentes comme nous venons de le faire. S'ils sont parallèles, vous savez certainement qu'ils ne se coupent pas. Si, toutefois, les pentes sont différentes, elles se couperont en un seul point. Vous devez ensuite trouver ce point et voir s'il se produit dans les limites données sur les valeurs $x$.

Ainsi, lorsque vous constatez que les lignes se coupent au point $(35, 19,5)$, alors cette intersection est en dehors des limites données, puisque, par exemple, votre première ligne ne va que de $x = 15$ à $x = 30$ . Il n'atteint jamais $x = 35$.

Je suppose que vous pouvez déjà vérifier l'intersection des lignes, car cela a déjà été répondu et vous semblez bien le comprendre.

Les segments sont $AB$ et $CD$. Ils se coupent si leur point d'intersection se trouve dans le rectangle central plus sombre (c'est-à-dire la zone dans l'espace qu'ils occupent tous les deux). En d'autres termes, si le point d'intersection est $(x,y)$, alors $x$ doit être inférieur à la plus petite valeur de droite (la coordonnée $x$ de $AH$ ici), et plus grand que la plus petite valeur à gauche ($GB$). Ici, la vérification $x>GB_x$ échoue, donc ils ne se croisent pas. L'idée est similaire pour les valeurs $y$, et elle doit passer tous les 4 tests (deux pour $x$ et deux pour $y$) avant de pouvoir conclure qu'ils se croisent.

Il y a deux cas à considérer pour déterminer si deux segments de droite $AB$ et $CD$ se coupent : (1) Les segments de droite sont ne pas colinéaire (trois images du haut dans la figure suivante) (2) les segments de ligne sont colinéaires (deux images du bas).

Le standard $y = mx + b$ n'est généralement pas utile car il omet les lignes verticales. Ici, il est préférable de considérer ce qui suit fonction implicite $h(P)$ pour une ligne passant par $A$ et $B:$ $ h(P) = (B - A) imes (P - A)= 0 $ où $U imes V = U_x cdot V_y - U_y cdot V_x.$ Notez que $h(P)$ définit un demi-espace en déterminant où se trouve le point $P$ par rapport à la ligne frontière passant par $AB:$ $ egin h(P) > 0 & mbox <$P$ dans un demi-espace positif> h(P) = 0 & mbox <$P$ sur la ligne> h(P) < 0 & mbox < $P$ dans un demi-espace négatif>end $ Ainsi, nous savons si les points $C$ et $D$ chevauchent la droite (infinie) passant par $AB$ si $h(C)$ et $h(D)$ sont non nuls et de signes opposés. Dans le cas général, nous savons que les segments de droite $AB$ et $CD$ se coupent si les points $C$ et $D$ chevauchent la droite passant par $AB$ et les points $A$ et $B$ chevauchent la ligne passant par $CD.$

Nous devons d'abord traiter le cas colinéaire où $h(C) = 0$ et $h(D) = 0.$ Ici nous avons une intersection ssi $ min(C_x,D_x) leq max(A_x,B_x) wedge max(C_x,D_x) geq min(A_x,B_x) $ et $ min(C_y,D_y) leq max(A_y,B_y) wedge max(C_y,D_y) geq min( A_y,B_y) $

Sinon, dans le cas général, nous utilisons nos équations du demi-espace. Le demi-espace $g(x)$ défini par $CD$ est $ g(P) = (D - C) imes (P - C) = 0. $ On a une intersection où $ (h(C) cdot h(D) leq 0) coin (g(A) cdot g(B) leq 0). $

Si vous souhaitez connaître le point d'intersection réel, vous branchez le équation paramétrique $L(t)$ pour une ligne passant par $AB$ $ L(t) = (B - A)cdot t + A, -infty < t < infty $ dans $g$ et résoudre pour $ t:$ $ g(L(t)) = (D - C) imes (L(t) - C) = 0 (D - C) imes ((B - A)cdot t + A - C) = 0 t = frac<(C - A) imes (D - C)> <(D - C) imes (B - A)>$ Rebranchez cette valeur pour $t$ dans $L (t)$ et vous avez votre point d'intersection. Bien sûr, cela suppose que $(D - C) imes (B - A) eq 0$ où les lignes sont ne pas parallèle.


Voir la vidéo: Python + Qgis = More time in Life.