正交投影产生伪像


14

我正在尝试使用qgis和“来自太空的世界” 投影http://spatialreference.org/ref/sr-org/6980/(本质上是正交投影)来创建球形视图。ArcGIS正确包装了形状,但是QGIS(2.01)产生了讨厌的工件。

在此处输入图片说明

我必须定期制作具有不同角度的地球仪,所以外面的人是否有解决此问题的想法?


1
相关的QGIS错误报告:hub.qgis.org/issues/2703
naught101

预装正交投影可以重新集中到任何视图是否太大,这是一个技术问题吗?

这不能回答问题。请参观以了解如何提出重点问题。
约翰·鲍威尔

Answers:


23

正如Andre所说,要使其正常工作,您需要在投影图层之前对其进行裁剪。安德烈(Andre)描述了一种手动方法,该方法在许多情况下都适用:将您的shapefile投影到具有与正交投影相同参数的方位角等距投影,创建一个覆盖半球的剪裁圆,该半球将在正交投影中可见,并且剪切shapefile。但是,由于投影到方位等距投影可能会导致与投影到正射投影类似的问题,因此该方法需要相当多的人工,并且不适用于所有投影参数。

这是一个脚本(现在也可以作为Clip to Hemisphere QGIS插件使用),该脚本采用稍有不同的方法:通过从正投影到源CRS投影一个圆,在原始shapefile的坐标参考系统中创建了一个裁剪层,但是另外确保覆盖整个可见半球,包括可见极。

这就是以30°N,110°E为中心的正交投影的裁剪层的外观:

然后,脚本将使用剪切层剪切当前选定的层,并将结果层添加到项目中。然后可以动态地或通过将其保存在正交CRS中将该层投影为正交投影:

这是脚本。确保将其保存在您的Python路径中,例如,作为“ cliportho.py”。然后,您可以使用将其导入QGIS Python控制台中import cliportho。要剪辑图层,请调用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)

看起来很有希望-我一定会尝试一下,并乐意提供反馈。我不太了解arcpy编程,但还没有开始使用qgis编程-但我会尽力了解您在做什么;-)插件(也许可以批量工作几层)会很有帮助!
user1523709

1
仅供参考,由于删除了“ fTools”软件包,此脚本在QGIS 2.16中不再起作用。
秒杀威廉姆斯

2
@SpikeWilliams:我已经更新了脚本,以消除对fTools的依赖。
杰克

5

您必须将多边形数据裁剪到地球的可见部分,因为QGIS本身不会这样做。

我在这里写了一个教程:

在QGIS中投影地图后,多边形在哪里去了?


编辑

您显示的图片实际上不是正交投影,因为它显示的是整个世界,而不仅仅是从外太空看到的可见的一半。对于世界地图,切割要容易一些,如下所述:

QGIS使用Robinson,Miller Cylindrical或其他投影显示以太平洋为中心的世界国家形状文件


感谢安德烈(Andre),这对于理解该问题非常有帮助-但由于我不得不每天几乎都在创建这种地球仪,并且随着视角的变化,它需要大量的手工工作。您是否知道任何插件等。自动化您的解决方案?
user1523709

一旦创建了一个剪切圆,其余的操作就可以在批处理脚本的命令行级别使用GDAL来完成。
AndreJ
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.