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.