6

Original problem : I tend to often invert X & Y in WGS84 lat/long CSV import.

I'm wondering now if there is a way not to reimport the original file (as this is the basic solution for points) and invert geometries . My idea : a query on the geometry field may work to create a new layer based on the first one, but entering y,x coordinates instead of x,y in the geometry creation process, but I don't know yet how to achieve this.

Do you have an idea how I could use the powerful QGIS query engine to create new features from my original layer with inverted X & Y ? Answer can be specific for point/line/polygon as each of them may have a different solution type.

I'm using QGIS LTR (2.14) and latest (2.18)

  • @gisinside, a PyQGIS solution would be of interest? – mgri Apr 10 '17 at 15:46
  • 1
    Why not ! In any case, it's not a urgent matter, rather a geogeek challenge :) For points, I'm looking towards something like geom_from_wkt( 'Point ('|| y ($geometry ) || ','|| x ($geometry ) ||')') – gisnside Apr 10 '17 at 15:47
  • @gisinside do you prefer the creation of a new layer or the edit of the same layer? I don't know if I can do it, but I will try. – mgri Apr 10 '17 at 15:51
  • @mgri The easiest for you ! I'm having a try with Pygis too :) – gisnside Apr 10 '17 at 15:56
  • @mgri i was think we could even adapt the script to run on multiple vector layers :) – gisnside Apr 11 '17 at 9:01
11

I propose a solution using PyQGIS: the several codes instantly work from the Python Console, but they can easily be implemented in separate scripts (see the edit at the end of the answer). Each script will return a new memory vector layer storing the inverted lon/lat coordinates for the geometries.

I tested the solution on several datasets available from Natural Earth Data.


Solution for point vector layers

Code

layer = iface.activeLayer()
crs = layer.crs().toWkt()

# Create the output layer
outLayer = QgsVectorLayer('Point?crs='+ crs, 'inverted' , 'memory')
prov = outLayer.dataProvider()
fields = layer.pendingFields()
prov.addAttributes(fields)
outLayer.updateFields()

for feat in layer.getFeatures():
    attrs = feat.attributes()
    geom = feat.geometry()
    coords = geom.asPoint()
    new_coords = (QgsPoint(coords[1], coords[0]))
    geom=QgsGeometry.fromPoint(new_coords)

    outGeom = QgsFeature()
    outGeom.setGeometry(geom)
    outGeom.setAttributes(attrs)
    prov.addFeatures([outGeom])

# Add the layer to the Layers panel
QgsMapLayerRegistry.instance().addMapLayer(outLayer)

Graphical example

Before:

enter image description here

After:

enter image description here


Solution for line vector layers

Code

layer = iface.activeLayer()
crs = layer.crs().toWkt()

# Create the output layer
outLayer = QgsVectorLayer('LineString?crs='+ crs, 'inverted' , 'memory')
prov = outLayer.dataProvider()
fields = layer.pendingFields()
prov.addAttributes(fields)
outLayer.updateFields()

for feat in layer.getFeatures():
    attrs = feat.attributes()
    geom = feat.geometry()
    coords = geom.asMultiPolyline()
    if coords:
        new_coords = [[QgsPoint(y, x) for x, y in z] for z in coords]
        geom=QgsGeometry.fromMultiPolyline(new_coords)
    else:
        coords = geom.asPolyline()
        new_coords = [QgsPoint(y, x) for x, y in coords]
        geom=QgsGeometry.fromPolyline(new_coords)

    outGeom = QgsFeature()
    outGeom.setGeometry(geom)
    outGeom.setAttributes(attrs)
    prov.addFeatures([outGeom])

# Add the layer to the Layers panel
QgsMapLayerRegistry.instance().addMapLayer(outLayer)

Graphical example

Before:

enter image description here

After:

enter image description here


Solution for polygon vector layers

Code

layer = iface.activeLayer()
crs = layer.crs().toWkt()

# Create the output layer
outLayer = QgsVectorLayer('Polygon?crs='+ crs, 'inverted' , 'memory')
prov = outLayer.dataProvider()
fields = layer.pendingFields()
prov.addAttributes(fields)
outLayer.updateFields()

for feat in layer.getFeatures():
    attrs = feat.attributes()
    geom = feat.geometry()
    coords = geom.asMultiPolygon()
    if coords:
        new_coords=[[[QgsPoint(y, x) for x, y in z] for z in coord] for coord in coords]
        geom=QgsGeometry.fromMultiPolygon(new_coords)
    else:
        coords = geom.asPolygon()
        new_coords=[[QgsPoint(y, x) for x, y in z] for z in coords]
        geom=QgsGeometry.fromPolygon(new_coords)

    outGeom = QgsFeature()
    outGeom.setGeometry(geom)
    outGeom.setAttributes(attrs)
    prov.addFeatures([outGeom])

# Add the layer to the Layers panel
QgsMapLayerRegistry.instance().addMapLayer(outLayer)

Graphical example

Before:

enter image description here

After:

enter image description here


EDIT

The codes above were implemented by @gisnside in a separate script that can be added to the Processing Toolbox (it substantially adapts the proper code to the geometry type).

#!/usr/bin/python
# encoding: utf-8
# Created by mgri
# Adapted for processing toolbox by gisnside

# This script inverts X and Y coordinates of a vector layer

##Vector_tools=group
##Invert_XY=name

##vector_layer=vector

from qgis.core import *

layer = processing.getObject(vector_layer)
crs = layer.crs().toWkt()
geometryType = layer.geometryType()

if geometryType == QGis.Point:
    # Create the output layer
    outLayer = QgsVectorLayer('Point?crs='+ crs, 'inverted' , 'memory')
    prov = outLayer.dataProvider()
    fields = layer.pendingFields()
    prov.addAttributes(fields)
    outLayer.updateFields()

    for feat in layer.getFeatures():
        attrs = feat.attributes()
        geom = feat.geometry()
        coords = geom.asPoint()
        new_coords = (QgsPoint(coords[1], coords[0]))
        geom=QgsGeometry.fromPoint(new_coords)

        outGeom = QgsFeature()
        outGeom.setGeometry(geom)
        outGeom.setAttributes(attrs)
        prov.addFeatures([outGeom])

    # Add the layer to the Layers panel
    QgsMapLayerRegistry.instance().addMapLayer(outLayer)

elif geometryType == QGis.Line:

    # Create the output layer
    outLayer = QgsVectorLayer('LineString?crs='+ crs, 'inverted' , 'memory')
    prov = outLayer.dataProvider()
    fields = layer.pendingFields()
    prov.addAttributes(fields)
    outLayer.updateFields()

    for feat in layer.getFeatures():
        attrs = feat.attributes()
        geom = feat.geometry()
        coords = geom.asMultiPolyline()
        if coords:
            new_coords = [[QgsPoint(y, x) for x, y in z] for z in coords]
            geom=QgsGeometry.fromMultiPolyline(new_coords)
        else:
            coords = geom.asPolyline()
            new_coords = [QgsPoint(y, x) for x, y in coords]
            geom=QgsGeometry.fromPolyline(new_coords)

        outGeom = QgsFeature()
        outGeom.setGeometry(geom)
        outGeom.setAttributes(attrs)
        prov.addFeatures([outGeom])

    # Add the layer to the Layers panel
    QgsMapLayerRegistry.instance().addMapLayer(outLayer)

elif geometryType == QGis.Polygon:

    # Create the output layer
    outLayer = QgsVectorLayer('Polygon?crs='+ crs, 'inverted' , 'memory')
    prov = outLayer.dataProvider()
    fields = layer.pendingFields()
    prov.addAttributes(fields)
    outLayer.updateFields()

    for feat in layer.getFeatures():
        attrs = feat.attributes()
        geom = feat.geometry()
        coords = geom.asMultiPolygon()
        if coords:
            new_coords=[[[QgsPoint(y, x) for x, y in z] for z in coord] for coord in coords]
            geom=QgsGeometry.fromMultiPolygon(new_coords)
        else:
            coords = geom.asPolygon()
            new_coords=[[QgsPoint(y, x) for x, y in z] for z in coords]
            geom=QgsGeometry.fromPolygon(new_coords)

        outGeom = QgsFeature()
        outGeom.setGeometry(geom)
        outGeom.setAttributes(attrs)
        prov.addFeatures([outGeom])

    # Add the layer to the Layers panel
    QgsMapLayerRegistry.instance().addMapLayer(outLayer)
  • 1
    Wow great job and "bravo"...Thanks a lot ! – gisnside Apr 11 '17 at 8:30
  • i created a script for the processing toolbox with all 3 solutions. See below my question the script. I didn't know If I could edit your answer so I put it up there. Hope you don't mind :) – gisnside Apr 11 '17 at 8:56
  • @gisnside I'm glad it was helpful... And you created a nice solution for implementing my codes inside a Processing loop, thanks! However, I prefer that my solution still remains thought for single layers because it is more readable and because I hope that the inversion of the lon/lat coordinates wouldn't happen many times. =) – mgri Apr 11 '17 at 9:36
  • @gisnside, if you prefer, however, I may add your code at the end of my answer, so I would be easier to read (instead of having it inside the question). If it is ok for you, I apply the edit. – mgri Apr 11 '17 at 10:36
  • No problem for me, as long as people can find it :) – gisnside Apr 11 '17 at 12:13
9

You can use the Affine Transformation plugin to do this.

It's not particularly fast, so you might want to try on some fairly simple geometries to begin with. Also, it only works with editable layers, so you will need to convert your delimited file into an editable format.

Once installed, the plugin goes under Vector > Geoprocessing tools > Affine Transformation

Note! this plugin edits the layer in-place, rather than creating a new layer. So I recommend you run it on a copy.

Next, use the following settings...

x' = 0.0 x + 1.0 y + 0.0
y' = 1.0 x + 0.0 y + 0.0

as seen below...

enter image description here

Here, I've swapped the x and y values for the UK, which sends it out into the Indian ocean, consistent with swapping the X and Y.

enter image description here

  • Seems like a working solution too :) Thanks for your research, I completely forgot about this plugin ! – gisnside Apr 11 '17 at 8:31
3

Just to be complete on the question : there is a "Swap XY" plugin in QGIS plugin list. It didn't work at first on my computer so I discounted it in my question, but I solved this. It can do the X/Y inversion too. One just need to look for SWAP XY in the plugin repository.

It adds a button one can click. It will then modify the current selected layer and invert coordinates. The creator added a warning it can sometime corrupt the data or not invert all coordinates.

SWAP XY

1

A native new solution in QGIS 3.2, thanks to Nyall Dawson (North Road) :

Feature: Swap x/y coordinate values algorithm

This algorithm swaps the X and Y coordinate values in input geometries. It can be used to repair geometries which have accidentally had their latitude and longitude values reversed.

Source (changelog)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.