Ortho Projection створює артефакти


14

Я намагаюся створити кульоподібний вигляд за допомогою qgis та "світу з космосу" -проекції http://spatialreference.org/ref/sr-org/6980/ (суттєво орто-проекція). ArcGIS розгортає фігури правильно, але QGIS (2.01) створює неприємні артефакти.

введіть тут опис зображення

Мені доводиться регулярно виробляти глобуси з різними кутами, щоб хтось там мав ідею, як виправити цю проблему?


1
пов'язаний звіт про помилки QGIS: hub.qgis.org/isissue/2703
naught101

Чи занадто велика технічна проблема, щоб ортографічна проекція була попередньо завантажена, і вона може бути зосереджена на будь-якій зорі?

Це не дає відповіді на запитання. Відвідайте тур, щоб дізнатися, як задати сфокусоване питання.
Джон Пауелл

Відповіді:


23

Як сказав Андре, для цього вам потрібно обрізати шар, перш ніж проектувати його. Андре описує ручний метод , який добре працює у багатьох випадках: спроектуйте свій файл форми на азимутальну рівновіддалену проекцію з тими ж параметрами, що й орфографічну проекцію, створіть відсічний коло, яке охоплює півсферу, яке буде видно в ортографічній проекції, і зафіксуйте формуляр з цим. Однак цей метод вимагає неабияких ручних зусиль і працює не для всіх параметрів проекції, оскільки проектування на азимутальну рівновіддалену проекцію може призвести до подібних проблем, як проеціювання до ортографічної проекції.

Ось сценарій (тепер також доступний як плагін QGIS Clip to Hemisphere ), який використовує дещо інший підхід: Відсічний шар створюється в системі координат оригінальної форми файлу шляхом проектування кола від ортографічного до вихідного CRS, але додатково обов'язково охоплюючи всю видиму півкулю, включаючи видимий полюс.

Ось так виглядає відсічний шар для ортографічної проекції, орієнтованої на 30 ° с.ш., 110 ° с.

Потім сценарій відсікає поточний вибраний шар із відсічним шаром і додає отриманий шар до проекту. Потім цей шар може бути спроектований на ортографічну проекцію як на льоту, так і за допомогою збереження в ортографічній 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 - але я спробую зрозуміти, що ви робите ;-) Плагін (можливо, робоча партія на кілька шарів) був би таким корисним!
користувач1523709

1
FYI, Цей сценарій більше не працює в QGIS 2.16, через вилучення пакету "fTools".
Спайк Вільямс

2
@SpikeWilliams: Я оновив сценарій, щоб усунути залежність від fTools.
Джейк

5

Ви повинні обрізати дані свого полігону на видиму половину земної кулі, оскільки QGIS не робить цього сам по собі.

Я написав тут підручник:

Куди пішли багатокутники після проектування карти в QGIS?


EDIT

Малюнок, який ви показуєте, насправді не є орто-проекцією, як показує весь світ, а не лише видиму половину, як видно з космосу. Для світових карт вирізання трохи легше, як описано тут:

QGIS відображає файли форм світу, орієнтовані на тихий океан, використовуючи циліндричну або іншу проекцію Робінсона, Міллера або іншу


Дякую Андре, це було дуже корисно, щоб зрозуміти проблему, але оскільки мені доводиться створювати такі глобуси щоденно і, змінюючи перспективи, потрібно багато ручної роботи. Чи знаєте ви про будь-які плагіни тощо. автоматизувати своє рішення?
користувач1523709

Після створення кола відсікання решту можна зробити за допомогою GDAL на рівні командного рядка, використовуючи пакетний сценарій.
AndreJ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.