Ortho Projection produz artefatos

14

Estou tentando criar uma visão semelhante a uma esfera usando qgis e a projeção "mundo do espaço" http://spatialreference.org/ref/sr-org/6980/ (essencialmente uma orto-projeção). O ArcGIS quebra as formas corretamente, mas o QGIS (2.01) produz artefatos desagradáveis.

insira a descrição da imagem aqui

Eu tenho que produzir os globos regularmente com ângulos diferentes. Alguém aí tem uma idéia de como resolver esse problema?

user1523709
fonte
1
relatório de bug relacionado ao QGIS: hub.qgis.org/issues/2703
naught101
É um problema técnico muito grande ter uma projeção ortográfica pré-carregada, que possa ser centralizada novamente em qualquer visualização?
Isso não responde à pergunta. Faça o tour para saber como fazer uma pergunta focada.
19416 John Powell

Respostas:

23

Como Andre disse, para que isso funcione, você precisará cortar sua camada antes de projetá-la. André descreve um método manual , que funciona bem em muitos casos: projete seu arquivo de forma em uma projeção equidistante azimutal com os mesmos parâmetros que a projeção ortográfica, crie um círculo de recorte que cubra o hemisfério que será visível na projeção ortográfica e prenda o shapefile com isso. No entanto, esse método requer bastante esforço manual e não funciona para todos os parâmetros de projeção, pois projetar em uma projeção equidistante azimutal pode levar a problemas semelhantes aos da projeção ortográfica.

Aqui está um script (agora também disponível como o plugin Clip to Hemisphere QGIS ) que tem uma abordagem um pouco diferente: Uma camada de recorte é criada no sistema de referência de coordenadas do shapefile original projetando um círculo do CRS ortográfico para o CRS de origem, mas adicionalmente certificando-se de cobrir todo o hemisfério visível, incluindo o pólo visível.

É assim que a camada de recorte se parece com uma projeção ortográfica centrada em 30 ° N, 110 ° E:

O script corta a camada selecionada no momento com a camada de recorte e adiciona a camada resultante ao projeto. Essa camada pode ser projetada para a projeção ortográfica, em tempo real ou salvando-a no CRS ortográfico:

Aqui está o script. Salve-o no seu caminho Python, por exemplo, como 'cliportho.py'. Em seguida, você pode importá-lo no console do QGIS Python usando import cliportho. Para recortar uma camada, ligue 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)
Jake
fonte
Parece muito promissor - eu definitivamente tentarei isso e ficarei feliz em fornecer feedback. Eu gosto muito de programação arcpy, mas ainda não comecei com a programação qgis - mas tentarei entender o que você está fazendo ;-) Um plugin (talvez trabalhando em lotes para várias camadas) seria muito útil!
user1523709
1
Para sua informação, este script não funciona mais no QGIS 2.16, devido à remoção do pacote "fTools".
Spike Williams
2
@ SpikeWilliams: Atualizei o script para remover a dependência do fTools.
Jake
5

Você precisa cortar seus dados de polígono na metade visível do globo, porque o QGIS não faz isso por si só.

Eu escrevi um tutorial aqui:

Para onde foram os polígonos depois de projetar um mapa no QGIS?


EDITAR

A imagem que você mostra não é realmente uma projeção orto, pois mostra o mundo inteiro, e não apenas a metade visível como vista do espaço sideral. Para mapas mundiais, o corte é um pouco mais fácil, conforme descrito aqui:

O QGIS exibe arquivos de formatos de países do mundo centrados no oceano Pacífico usando Robinson, Miller Cilíndrico ou outra projeção

AndreJ
fonte
Obrigado, Andre, isso foi bastante útil para entender o problema - mas como tenho que criar esses globos quase diariamente e com novas perspectivas, é preciso muito trabalho manual. Você conhece algum plug-in-etc. automatizar sua solução?
user1523709
Depois de criar um círculo de recorte, o restante pode ser feito usando GDAL no nível da linha de comando, usando um script em lote.
Andrej