การดาวน์โหลดข้อมูลแรสเตอร์สู่ python จาก postgis โดยใช้ psycopg2


13

ฉันมีข้อมูลแบบแรสเตอร์ในตาราง postgres ที่ฉันต้องการใช้เป็น python ในรูปแบบ numpy ฉันใช้ psycopg2 เพื่อเชื่อมต่อกับฐานข้อมูล ฉันสามารถดาวน์โหลดข้อมูล แต่กลับมาเป็นสตริง (อาจเป็นเลขฐานสองต่อเนื่อง)

ไม่มีใครรู้วิธีการใช้สายนี้และแปลงเป็นอาร์เรย์ numpy?

ฉันสำรวจตัวเลือกอื่น ๆ เพื่อดาวน์โหลดแรสเตอร์เช่นใช้ st_astiff และเข้ารหัสเพื่อดาวน์โหลดไฟล์ hex และใช้ xxd แต่มันไม่ทำงาน ฉันยังคงได้รับข้อผิดพลาด 'rt_raster_to_gdal: ไม่สามารถโหลดไดรเวอร์ GDAL เอาต์พุต' และฉันไม่ได้รับอนุญาตให้ตั้งค่าตัวแปรสภาพแวดล้อมเพื่อให้สามารถเปิดไดรเวอร์ได้

TL, DR: ต้องการนำเข้าข้อมูลแรสเตอร์เข้าสู่อาร์เรย์ numpy (โดยใช้ python)

คำตอบ:


14

rt_raster_to_gdal: ไม่สามารถโหลดไดรเวอร์ GDAL เอาต์พุต

สำหรับข้อผิดพลาดแรกกับST_AsTIFFคุณต้องเปิดใช้งานไดรเวอร์ GDAL ของคุณซึ่งโดยค่าเริ่มต้นจะไม่เปิดใช้งานสำหรับ PostGIS 2.1 ดูคู่มือวิธีการทำเช่นนี้ ตัวอย่างเช่นฉันมีการตั้งค่าตัวแปรสภาพแวดล้อมบนคอมพิวเตอร์ Windows ด้วย:

POSTGIS_GDAL_ENABLED_DRIVERS=GTiff PNG JPEG GIF XYZ DTED USGSDEM AAIGrid

ซึ่งสามารถยืนยันกับ PostGIS ด้วย:

SELECT short_name, long_name
FROM ST_GDALDrivers();

PostGIS ไปยัง Numpy

คุณสามารถส่งออกผลลัพธ์ไปยังไฟล์ GeoTIFF หน่วยความจำเสมือนสำหรับ GDAL เพื่ออ่านไปยังอาร์เรย์ Numpy สำหรับคำแนะนำเกี่ยวกับไฟล์เสมือนที่ใช้ใน GDAL ดูโพสต์บล็อกนี้

import os
import psycopg2
from osgeo import gdal

# Adjust this to connect to a PostGIS database
conn = psycopg2.connect(...)
curs = conn.cursor()

# Make a dummy table with raster data
curs.execute("""\
    SELECT ST_AsRaster(ST_Buffer(ST_Point(1, 5), 10), 10, 10, '8BUI', 1) AS rast
    INTO TEMP mytable;
""")

# Use a virtual memory file, which is named like this
vsipath = '/vsimem/from_postgis'

# Download raster data into Python as GeoTIFF, and make a virtual file for GDAL
curs.execute("SELECT ST_AsGDALRaster(rast, 'GTiff') FROM mytable;")
gdal.FileFromMemBuffer(vsipath, bytes(curs.fetchone()[0]))

# Read first band of raster with GDAL
ds = gdal.Open(vsipath)
band = ds.GetRasterBand(1)
arr = band.ReadAsArray()

# Close and clean up virtual memory file
ds = band = None
gdal.Unlink(vsipath)

print(arr)  # this is a 2D numpy array

แสดงจุดบัฟเฟอร์ที่แรสเตอร์แล้ว

[[0 0 0 1 1 1 1 0 0 0]
 [0 1 1 1 1 1 1 1 1 0]
 [0 1 1 1 1 1 1 1 1 0]
 [1 1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 1 1]
 [0 1 1 1 1 1 1 1 1 0]
 [0 1 1 1 1 1 1 1 1 0]
 [0 0 0 1 1 1 1 0 0 0]]

โปรดทราบว่าฉันใช้รูปแบบ 'GTiff' ในตัวอย่าง แต่รูปแบบอื่นอาจเหมาะสมกว่า ตัวอย่างเช่นหากคุณมีแรสเตอร์ขนาดใหญ่ที่ต้องถ่ายโอนผ่านการเชื่อมต่ออินเทอร์เน็ตที่ช้าให้ลองใช้ 'PNG' เพื่อบีบอัดข้อมูล


นั่นมีประโยชน์มาก
John Powell

มีประโยชน์มาก ขอบคุณ! ฉันยังคงพบปัญหานี้อยู่: ข้อผิดพลาด: rt_raster_to_gdal: ไม่สามารถโหลดไดรเวอร์ GDAL เอาต์พุต แต่ฉันคิดว่าฉันมีวิธีแก้ปัญหาสำหรับเรื่องนี้ ขอบคุณอีกครั้ง!
Mayank Agarwal

@MayankAgarwal คำตอบที่อัปเดตสำหรับข้อผิดพลาด rt_raster_to_gdal
Mike T

6

ฉันคิดว่าคำถามคือว่าคุณสามารถอ่านได้จากตารางแรสเตอร์ postgis โดยไม่ต้องใช้ไดรเวอร์ gdal คุณสามารถทำได้!

ตรวจสอบให้แน่ใจว่าคุณเลือกผลแรสเตอร์ของคุณเป็น WKBinary:

เลือก St_AsBinary (rast) ...

ใช้สคริปต์ด้านล่างเพื่อแยก WKBinary ลงในรูปแบบภาพหลาม ฉันชอบ opencv เพราะมันจัดการกับจำนวนของแถบภาพโดยพลการ แต่ก็สามารถใช้ PIL / low ได้ถ้าหากมี 1 หรือ 3 แบนด์มากกว่าปกติ

ตอนนี้ฉันจัดการกับภาพไบต์เท่านั้น แต่มันค่อนข้างเล็กน้อยที่จะขยายไปยังประเภทข้อมูลอื่น

หวังว่านี่จะเป็นประโยชน์

นำเข้า struct
นำเข้าจำนวนมากเป็น np
นำเข้า cv2

# ฟังก์ชั่นที่จะแยกส่วนหัว WKB
def wkbHeader (ดิบ):
    # ดู http://trac.osgeo.org/postgis/browser/trunk/raster/doc/RFC2-WellKnownBinaryFormat

    header = {}

    header ['endianess'] = struct.unpack ('B', ดิบ [0]) [0]
    header ['version'] = struct.unpack ('H', raw [1: 3]) [0]
    header ['nbands'] = struct.unpack ('H', raw [3: 5]) [0]
    header ['scaleX'] = struct.unpack ('d', ดิบ [5:13]) [0]
    header ['scaleY'] = struct.unpack ('d', ดิบ [13:21]) [0]
    header ['ipX'] = struct.unpack ('d', ดิบ [21:29]) [0]
    header ['ipY'] = struct.unpack ('d', ดิบ [29:37]) [0]
    header ['skewX'] = struct.unpack ('d', ดิบ [37:45]) [0]
    header ['skewY'] = struct.unpack ('d', ดิบ [45:53]) [0]
    header ['srid'] = struct.unpack ('i', raw [53:57]) [0]
    header ['width'] = struct.unpack ('H', raw [57:59]) [0]
    header ['height'] = struct.unpack ('H', raw [59:61]) [0]

    กลับส่วนหัว

# ฟังก์ชั่นที่จะถอดรหัสข้อมูลแรสเตอร์ WKB 
def wkbImage (ดิบ):
    h = wkbHeader (ดิบ)
    img = [] # array เพื่อเก็บแถบรูปภาพ
    offset = 61 # ความยาวดิบส่วนหัวเป็นไบต์
    สำหรับฉันอยู่ในช่วง (h ['nbands']):
        # กำหนด pixtype สำหรับวงนี้
        pixtype = struct.unpack ('B', raw [offset]) [0] >> 4
        # สำหรับตอนนี้เราจัดการกับไบต์ที่ไม่ได้ลงชื่อเท่านั้น
        ถ้า pixtype == 4:
            band = np.frombuffer (raw, dtype = 'uint8', count = h ['width'] * h ['height'], offset = offset + 1)
            img.append ((np.reshape (วงดนตรี ((h ['สูง'], h ['ความกว้าง']))))
            offset = offset + 2 + h ['width'] * h ['height']
        # to do: จัดการกับชนิดข้อมูลอื่น 

    ส่งคืน cv2.merge (tuple (img))


นั่นมีประโยชน์มาก ฉันมีปัญหามากมายเกี่ยวกับ gdal ในสภาพแวดล้อมของคอนโด แต่วิธีการนี้ใช้งานได้เป็นครั้งแรกและมันก็ดีที่จะสามารถเจาะลึกโครงสร้างได้เช่นกัน
John Powell
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.