Ortho Projection produce artefatti


14

Sto cercando di creare una visione a sfera usando qgis e la proiezione "world from space" http://spatialreference.org/ref/sr-org/6980/ (essenzialmente una orto-proiezione). ArcGIS avvolge le forme correttamente ma QGIS (2.01) produce artefatti cattivi.

inserisci qui la descrizione dell'immagine

Devo produrre i globi su base regolare con diverse angolazioni, quindi qualcuno là fuori ha idea di come risolvere questo problema?


1
bug report relativo a QGIS: hub.qgis.org/issues/2703
— naught101

È un problema tecnico troppo grande avere una proiezione ortografica precaricata, che può essere ricentrata su qualsiasi vista?

Questo non risponde alla domanda. Fai il tour per imparare a porre una domanda mirata.
— John Powell,

Risposte:


23

Come ha detto Andre, affinché questo funzioni, dovrai ritagliare il tuo livello prima di proiettarlo. Andre descrive un metodo manuale , che funziona bene per molti casi: proietta il tuo shapefile su una proiezione equidistante azimutale con gli stessi parametri della proiezione ortografica, crea un cerchio di ritaglio che copre l'emisfero che sarà visibile nella proiezione ortografica, e tagliare il file di forma con quello. Tuttavia, questo metodo richiede un discreto sforzo manuale e non funziona per tutti i parametri di proiezione, poiché la proiezione su una proiezione equidistante azimutale può portare a problemi simili come la proiezione su una proiezione ortografica.

Ecco uno script (ora disponibile anche come plug-in Clip to Hemisphere QGIS ) che ha un approccio leggermente diverso: uno strato di ritaglio viene creato nel sistema di riferimento di coordinate del file di forma originale proiettando un cerchio dal CRS ortografico a quello di origine, ma in aggiunta assicurandosi di coprire l'intero emisfero visibile, compreso il polo visibile.

Ecco come appare lo strato di ritaglio per una proiezione ortografica centrata su 30 ° N, 110 ° E:

Lo script quindi taglia il livello attualmente selezionato con il livello di ritaglio e aggiunge il livello risultante al progetto. Tale strato può quindi essere proiettato sulla proiezione ortografica, sia al volo sia salvandolo nel CRS ortografico:

Ecco la sceneggiatura. Assicurati di salvarlo nel tuo percorso Python, ad esempio come 'cliportho.py'. Quindi è possibile importarlo nella console QGIS Python utilizzando import cliportho. Per tagliare un livello, chiama cliportho.doClip(iface, lat=30, lon=110, filename='A.shp').


import numpy as np
from qgis.core import *
import qgis.utils

import sys, os, imp


def doClip(iface, lat=30, lon=110, filename='result.shp'):
    sourceLayer = iface.activeLayer()

    sourceCrs = sourceLayer.dataProvider().crs()

    targetProjString = "+proj=ortho +lat_0=" + str(lat) + " +lon_0=" + str(lon) + "+x_0=0 +y_0=0 +a=6370997 +b=6370997 +units=m +no_defs"
    targetCrs = QgsCoordinateReferenceSystem()
    targetCrs.createFromProj4(targetProjString)

    transformTargetToSrc = QgsCoordinateTransform(targetCrs, sourceCrs).transform

    def circlePolygon(nPoints=20, radius=6370000, center=[0,0]):
        clipdisc = QgsVectorLayer("Polygon?crs=epsg:4326", "Clip disc", "memory")
        angles = np.linspace(0, 2*np.pi, nPoints, endpoint=False)
        circlePoints = np.array([ transformTargetToSrc(QgsPoint(center[0]+np.cos(angle)*radius, center[1]+np.sin(angle)*radius)) for angle in angles ])
        sortIdx = np.argsort(circlePoints[:,0])
        circlePoints = circlePoints[sortIdx,:]
        circlePoints = [ QgsPoint(point[0], point[1]) for point in circlePoints ]
        circlePoints.extend([QgsPoint(180,circlePoints[-1][1]), QgsPoint(180,np.sign(lat)*90), QgsPoint(-180,np.sign(lat)*90), QgsPoint(-180,circlePoints[0][1])])
        circle = QgsFeature()
        circle.setGeometry(QgsGeometry.fromPolygon( [circlePoints] ) )
        clipdisc.dataProvider().addFeatures([circle])
        QgsMapLayerRegistry.instance().addMapLayer(clipdisc)
        return clipdisc

    auxDisc = circlePolygon(nPoints = 3600)

    ###### The clipping stuff
    ## Code taken from the fTools plugin

    vproviderA = sourceLayer.dataProvider()
    vproviderB = auxDisc.dataProvider()

    inFeatA = QgsFeature()
    inFeatB = QgsFeature()
    outFeat = QgsFeature()

    fitA = vproviderA.getFeatures()

    nElement = 0  
    writer = QgsVectorFileWriter( filename, 'UTF8', vproviderA.fields(),
                                  vproviderA.geometryType(), vproviderA.crs() )

    index = QgsSpatialIndex()
    feat = QgsFeature()
    index = QgsSpatialIndex()
    fit = vproviderB.getFeatures()
    while fit.nextFeature( feat ):
        index.insertFeature( feat )

    while fitA.nextFeature( inFeatA ):
      nElement += 1
      geom = QgsGeometry( inFeatA.geometry() )
      atMap = inFeatA.attributes()
      intersects = index.intersects( geom.boundingBox() )
      first = True
      found = False
      if len( intersects ) > 0:
        for id in intersects:
          vproviderB.getFeatures( QgsFeatureRequest().setFilterFid( int( id ) ) ).nextFeature( inFeatB )
          tmpGeom = QgsGeometry( inFeatB.geometry() )
          if tmpGeom.intersects( geom ):
            found = True
            if first:
              outFeat.setGeometry( QgsGeometry( tmpGeom ) )
              first = False
            else:
              try:
                cur_geom = QgsGeometry( outFeat.geometry() )
                new_geom = QgsGeometry( cur_geom.combine( tmpGeom ) )
                outFeat.setGeometry( QgsGeometry( new_geom ) )
              except:
                GEOS_EXCEPT = False
                break
        if found:
          try:
            cur_geom = QgsGeometry( outFeat.geometry() )
            new_geom = QgsGeometry( geom.intersection( cur_geom ) )
            if new_geom.wkbType() == 0:
              int_com = QgsGeometry( geom.combine( cur_geom ) )
              int_sym = QgsGeometry( geom.symDifference( cur_geom ) )
              new_geom = QgsGeometry( int_com.difference( int_sym ) )
            try:
              outFeat.setGeometry( new_geom )
              outFeat.setAttributes( atMap )
              writer.addFeature( outFeat )
            except:
              FEAT_EXCEPT = False
              continue
          except:
            GEOS_EXCEPT = False
            continue
    del writer

    resultLayer = QgsVectorLayer(filename, sourceLayer.name() + " - Ortho: Lat " + str(lat) + ", Lon " + str(lon), "ogr")
    QgsMapLayerRegistry.instance().addMapLayer(resultLayer)

Sembra molto promettente: lo proverò sicuramente e sarò felice di fornire un feedback. Sono un po 'appassionato di programmazione arcpy ma non ho iniziato con la programmazione qgis - ma cercherò di capire cosa stai facendo ;-) Un plugin (forse un batch funzionante per diversi livelli) sarebbe così utile!
— user1523709

1
Cordiali saluti, questo script non funziona più in QGIS 2.16, a causa della rimozione del pacchetto "fTools".
— Spike Williams,

2
@SpikeWilliams: ho aggiornato lo script per rimuovere la dipendenza da fTools.
— Jake,

5

Devi ritagliare i tuoi dati poligonali nella metà visibile del globo, perché QGIS non lo fa da solo.

Ho scritto un tutorial qui:

Dove sono finiti i poligoni dopo aver proiettato una mappa in QGIS?


MODIFICARE

L'immagine che mostri non è in realtà una orto proiezione, in quanto mostra il mondo intero e non solo la metà visibile vista dallo spazio. Per le mappe del mondo, il taglio è un po 'più semplice, come descritto qui:

QGIS mostra file di forma di paese del mondo centrati sull'Oceano Pacifico usando Robinson, Miller Cylindrical o altre proiezioni


Grazie Andre, è stato molto utile per capire il problema, ma dal momento che devo creare tali globi quasi quotidianamente e con il cambiamento delle prospettive, richiede un sacco di lavoro manuale. Conosci qualche plugin, ecc. automatizzare la tua soluzione?
— user1523709

Dopo aver creato un cerchio di ritaglio, il resto può essere fatto utilizzando GDAL a livello di riga di comando utilizzando uno script batch.
— AndreJ,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.