직교 투영으로 인공물 생성


14

qgis와 "world from space"-projection http://spatialreference.org/ref/sr-org/6980/ (본질적으로 정사영) 을 사용하여 구와 같은 뷰를 만들려고합니다 . ArcGIS는 모양을 올바르게 래핑하지만 QGIS (2.01)는 불규칙한 아티팩트를 생성합니다.

여기에 이미지 설명을 입력하십시오

다른 각도로 지구본을 정기적으로 만들어야하므로이 문제를 해결하는 방법을 아는 사람이 있습니까?


1
관련 QGIS 버그 보고서 : hub.qgis.org/issues/2703
naught101

직교 투영을 사전로드하여보기에 다시 집중시킬 수없는 것은 너무 큰 기술적 문제입니까?

이것은 질문에 대답하지 않습니다. 초점을 맞춘 질문을하는 방법을 알아 보려면 둘러보기방문 하십시오 .
존 파월

답변:


23

Andre가 말했듯이 이것이 작동하려면 레이어를 투영하기 전에 자르기해야합니다. Andre 는 많은 경우에 잘 작동 하는 수동 방법에 대해 설명합니다 . 직교 투영과 동일한 매개 변수를 사용하여 형상 파일을 방위각 등거리 투영으로 투영하고, 직교 투영에서 볼 수있는 반구를 덮는 클리핑 원을 생성합니다. 그것으로 shapefile을 자르십시오. 그러나 방위각 등거리 투영으로 투영 할 때 직교 투영법으로 투영 할 때와 유사한 문제가 발생할 수 있기 때문에이 방법에는 약간의 수동 노력이 필요하며 모든 투영 매개 변수에서 작동하지 않습니다.

다음 은 약간 다른 접근법을 취하는 스크립트 (현재 Clip to Hemisphere QGIS 플러그인 으로도 사용 가능 )입니다. 직교에서 소스 CRS로 원을 투영하여 원본 셰이프 파일의 좌표 참조 시스템에 클리핑 레이어가 생성됩니다. 보이는 극을 포함하여 전체 보이는 반구를 덮어야합니다.

30 ° N, 110 ° E를 중심으로하는 직교 투영의 경우 클리핑 레이어는 다음과 같습니다.

그런 다음 스크립트는 현재 선택된 레이어를 클리핑 레이어로 자르고 결과 레이어를 프로젝트에 추가합니다. 그런 다음 해당 레이어를 직교 투영 방식으로 투영하거나 직교 CRS에 저장하여 투영 할 수 있습니다.

여기 스크립트가 있습니다. 파이썬 경로에 저장하십시오 (예 : '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)

매우 유망한 것으로 보입니다. 나는 이것을 시도하고 피드백을 기꺼이 제공 할 것입니다. 나는 아크 피 프로그래밍에 거의 익숙하지 않지만 qgis 프로그래밍으로 시작하지는 않았다. 그러나 나는 당신이 무엇을하고 있는지 이해하려고 노력할 것이다.
user1523709

1
참고로,이 스크립트는 "fTools"패키지 제거로 인해 QGIS 2.16에서 더 이상 작동하지 않습니다.
스파이크 윌리엄스

2
@SpikeWilliams : fTools에 대한 의존성을 제거하기 위해 스크립트를 업데이트했습니다.
Jake

5

QGIS는 자체적으로 그렇게하지 않기 때문에 다각형 데이터를 지구의 보이는 절반으로 잘라야합니다.

나는 여기에 튜토리얼을 썼다 :

QGIS에서 맵을 투영 한 후 다각형은 어디로 갔습니까?


편집하다

당신이 보여준 그림은 실제로는 전체 공간을 보여주기 때문에 직교 투영이 아닙니다. 세계지도의 경우 다음과 같이 절단이 조금 더 쉽습니다.

QGIS는 Robinson, Miller Cylindrical 또는 기타 투영법을 사용하여 태평양을 중심으로 세계 국가 모양 파일을 표시합니다.


감사합니다 Andre는 문제를 이해하는 데 큰 도움이되었지만 거의 매일 지구본을 만들어야하고 관점이 바뀌면 많은 수작업이 필요합니다. 플러그인 등을 알고 있습니까? 솔루션을 자동화 하시겠습니까?
user1523709

클리핑 원을 만든 후에는 배치 스크립트를 사용하여 명령 줄 수준에서 GDAL을 사용하여 나머지를 수행 할 수 있습니다.
AndreJ
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.