Criando imagem com posições específicas de latitude / longitude usando GDAL?

9

Eu tenho um arquivo ASCII com latitude, longitude e data_val no seguinte formato.

35-13.643782N, 080-57.190157W, 118.6
...

Eu tenho um arquivo de imagem GeoTiff e posso visualizá-lo facilmente.

Quero colocar um "alfinete" (pode ser um ponto / bandeira / estrela ou o que for mais fácil) na imagem na posição específica de latitude / longitude encontrada no arquivo ASCII.

Aqui está o que eu consegui fazer até agora:

Minha imagem de origem é assim:

Driver: GTiff/GeoTIFF
Files: /tmp/Charlotte SEC 100.tif
Size is 16867, 12358
Coordinate System is:
PROJCS["Lambert Conformal Conic",
    GEOGCS["NAD83",
        DATUM["North_American_Datum_1983",
            SPHEROID["GRS 1980",6378137,298.2572221010042,
                AUTHORITY["EPSG","7019"]],
            AUTHORITY["EPSG","6269"]],
        PRIMEM["Greenwich",0],
        UNIT["degree",0.0174532925199433],
        AUTHORITY["EPSG","4269"]],
    PROJECTION["Lambert_Conformal_Conic_2SP"],
    PARAMETER["standard_parallel_1",38.66666666666666],
    PARAMETER["standard_parallel_2",33.33333333333334],
    PARAMETER["latitude_of_origin",34.11666666666667],
    PARAMETER["central_meridian",-78.75],
    PARAMETER["false_easting",0],
    PARAMETER["false_northing",0],
    UNIT["metre",1,
        AUTHORITY["EPSG","9001"]]]
Origin = (-365041.822331817995291,240536.419747152860509)
Pixel Size = (42.334586069440391,-42.334898968590878)
Metadata:
  AREA_OR_POINT=Area
  TIFFTAG_DATETIME=2016:06:24 12:46:45
  TIFFTAG_RESOLUTIONUNIT=2 (pixels/inch)
  TIFFTAG_SOFTWARE=Adobe Photoshop CS5 Windows
  TIFFTAG_XRESOLUTION=300
  TIFFTAG_YRESOLUTION=300
Image Structure Metadata:
  COMPRESSION=LZW
  INTERLEAVE=BAND
Corner Coordinates:
Upper Left  ( -365041.822,  240536.420) ( 82d48'55.43"W, 36d13' 4.92"N)
Lower Left  ( -365041.822, -282638.262) ( 82d35'10.11"W, 31d30'17.00"N)
Upper Right (  349015.641,  240536.420) ( 74d51'46.40"W, 36d13'26.16"N)
Lower Right (  349015.641, -282638.262) ( 75d 4'55.60"W, 31d30'36.99"N)
Center      (   -8013.091,  -21050.921) ( 78d50'12.11"W, 33d55'36.35"N)
Band 1 Block=16867x1 Type=Byte, ColorInterp=Palette
  Color Table (RGB with 256 entries)
    0: 255,255,255,255
...

Aqui está o que eu consegui juntar em Python:

from osgeo import gdal, osr

src_filename = '/tmp/Charlotte SEC 100.tif'
dst_filename = '/tmp/foo.tiff'

# Opens source dataset
src_ds = gdal.Open(src_filename)
format = "GTiff"
driver = gdal.GetDriverByName(format)

# Open destination dataset
dst_ds = driver.CreateCopy(dst_filename, src_ds, 0)

# Specify raster location through geotransform array
# (upperleftx, scalex, skewx, upperlefty, skewy, scaley)
# Scale = size of one pixel in units of raster projection
# this example below assumes 100x100
gt = [-365041.822, 100, 0, 240536.420, 0, -100]

# Set location
dst_ds.SetGeoTransform(gt)

# Get raster projection
epsg = 4269            # http://spatialreference.org/ref/sr-org/lambert_conformal_conic_2sp/
srs = osr.SpatialReference()
srs.ImportFromEPSG(epsg)
dest_wkt = srs.ExportToWkt()

# Set projection
dst_ds.SetProjection(dest_wkt)

# Close files
dst_ds = None
src_ds = None

Mas não consigo descobrir como colocar um "ponto vermelho" em 35-13.643782N, 080-57.190157W

Estou tendo que aprender alguns novos detalhes aqui (nomenclatura sobre SIG).

Brad Walker
fonte
O tópico que você pode precisar investigar é Georreferenciamento.
PolyGeo
Obrigado. Fiz algumas pesquisas no Google usando o termo Georreferenciamento. Isso foi útil. Metade da batalha é saber que termos técnicos usar .. #
Brad Walker Brad
Tenho certeza de que estou perdendo alguma coisa, mas você já pensou em converter os dados em KML ou algo assim?
Barrycarter
11
Pode ser necessário converter suas coordenadas DD-MM.mmmmH em graus decimais. Você precisará analisar as informações do Hemisfério W ou S significa um valor negativo (faça isso como a última etapa). Os minutos precisam ser divididos por 60 e adicionados ou concatenados com a parte de graus.
mkennedy

Respostas:

7

Sua gdalinfosaída mostra que você tem uma única banda GeoTIFF com uma tabela de cores (paleta AKA). Como não vejo os valores nessa tabela de cores, os comandos abaixo convertem a tabela de banda única + cor em um GeoTIFF RGB de três bandas. Os comandos também assumem que seu arquivo ASCII possui uma linha de cabeçalho e tem coordenadas em graus decimais; talvez seja necessário modificar o arquivo, se não houver.

Entradas:

$ cat testlonlat.csv
LON,LAT
143.798425,-15.551485
143.827437,-15.535119
143.84561,-15.530017
143.859107,-15.54819
143.812347,-15.523641
143.853581,-15.534694
143.883337,-15.537669
143.885356,-15.561687
143.887694,-15.588468

$ gdalinfo testutm.tif
Driver: GTiff/GeoTIFF
Files: testutm.tif
Size is 1102, 959
Coordinate System is:
PROJCS["WGS 84 / UTM zone 54S",
    GEOGCS["WGS 84",
        DATUM["WGS_1984",
            SPHEROID["WGS 84",6378137,298.257223563,
                AUTHORITY["EPSG","7030"]],
            AUTHORITY["EPSG","6326"]],
        PRIMEM["Greenwich",0,
            AUTHORITY["EPSG","8901"]],
        UNIT["degree",0.0174532925199433,
            AUTHORITY["EPSG","9122"]],
        AUTHORITY["EPSG","4326"]],
    PROJECTION["Transverse_Mercator"],
    PARAMETER["latitude_of_origin",0],
    PARAMETER["central_meridian",141],
    PARAMETER["scale_factor",0.9996],
    PARAMETER["false_easting",500000],
    PARAMETER["false_northing",10000000],
    UNIT["metre",1,
        AUTHORITY["EPSG","9001"]],
    AXIS["Easting",EAST],
    AXIS["Northing",NORTH],
    AUTHORITY["EPSG","32754"]]
Origin = (798741.168775000027381,8282084.855279999785125)
Pixel Size = (10.000000000000000,-10.000000000000000)
Metadata:
  AREA_OR_POINT=Area
Image Structure Metadata:
  INTERLEAVE=BAND
Corner Coordinates:
Upper Left  (  798741.169, 8282084.855) (143d47' 4.96"E, 15d31'16.22"S)
Lower Left  (  798741.169, 8272494.855) (143d47' 9.15"E, 15d36'27.98"S)
Upper Right (  809761.169, 8282084.855) (143d53'14.43"E, 15d31'11.47"S)
Lower Right (  809761.169, 8272494.855) (143d53'18.78"E, 15d36'23.20"S)
Center      (  804251.169, 8277289.855) (143d50'11.83"E, 15d33'49.74"S)
Band 1 Block=1102x7 Type=Byte, ColorInterp=Palette
  Color Table (RGB with 256 entries)
    0: 120,112,136,255
    1: 96,104,88,255
    ...
    254: 76,124,140,255
    255: 232,228,236,255

Processo:

$ gdal_translate -expand rgb testutm.tif testutm_rgb.tif

$ ogr2ogr -f "GeoJSON" -dialect sqlite                      \
  -sql "select ST_buffer(Geometry,0.001) from testlonlat"   \
  -s_srs EPSG:4326 -t_srs EPSG:32754                        \
  /vsistdout/ CSV:testlonlat.csv -oo X_POSSIBLE_NAMES=Lon   \
  -oo Y_POSSIBLE_NAMES=Lat |  gdal_rasterize -b 1 -b 2 -b 3 \
  -burn 255 -burn 0 -burn 0 /vsistdin/ testutm_rgb.tif

O último comando faz o seguinte:

  • coloca o ponto Lon / Lat em um polígono maior para que ele apareça melhor (você pode pular isso se quiser apenas um pixel colorido de vermelho)
  • converte de WGS84 Lat / Lon (EPSG: 4326) para o mesmo sistema de coordenadas da varredura (EPSG: 32754 que é WGS 84 UTM Zone 54S, seu CRS será diferente)
  • grava o polígono de saída como GeoJSON para STDOUT e o canaliza para gdal_rasterize
  • grava RGB 255,0,0 nas bandas de varredura RGB 1, 2 e 3

Resultado:

insira a descrição da imagem aqui

user2856
fonte
3

Você começou bem. gdal.CreateCopycuidará da georreferenciação, para que você não precise defini-la uma segunda vez usando a geotransformação e a projeção.

O processo completo transformará as cordas lon / lat nas coordenadas XY da referência espacial raster. Em seguida, essas cordas XY serão transformadas na linha, índices de colunas da varredura usando a geotransformação inversa. Algum valor de pixel será gravado nessa posição.

from osgeo import gdal, osr
import numpy as np

src_filename = '/tmp/Charlotte SEC 100.tif'
dst_filename = '/tmp/foo.tiff'

# Opens source dataset
src_ds = gdal.Open(src_filename)
format = "GTiff"
driver = gdal.GetDriverByName(format)

# Open destination dataset
dst_ds = driver.CreateCopy(dst_filename, src_ds, 0)

# Get raster projection
epsg = 4269         # http://spatialreference.org/ref/sr-org/lambert_conformal_conic_2sp/
srs = osr.SpatialReference()
srs.ImportFromEPSG(epsg)

# Make WGS84 lon lat coordinate system
world_sr = osr.SpatialReference()
world_sr.SetWellKnownGeogCS('WGS84')

# Transform lon lats into XY
lonlat = [[0.,30.], [20., 30.], [25., 30.]]
coord_transform = osr.CoordinateTransformation(world_sr, srs)
newpoints = coord_transform.TransformPoints(lonlat) # list of XYZ tuples

# Make Inverse Geotransform  (try:except due to gdal version differences)
try:
    success, inverse_gt = gdal.InvGeoTransform(gt)
except:
    inverse_gt = gdal.InvGeoTransform(gt)

# [Note 1] Set pixel values
marker_array_r = np.array([[255]], dtype=np.uint8)
marker_array_g = np.array([[0]], dtype=np.uint8)
marker_array_b = np.array([[0]], dtype=np.uint8)
for x,y,z in newpoints:
    pix_x = int(inverse_gt[0] + inverse_gt[1] * x + inverse_gt[2] * y)
    pix_y = int(inverse_gt[3] + inverse_gt[4] * x + inverse_gt[5] * y)
    dst_ds.GetRasterBand(1).WriteArray(marker_array_r, pix_x, pix_y)
    dst_ds.GetRasterBand(2).WriteArray(marker_array_g, pix_x, pix_y)
    dst_ds.GetRasterBand(3).WriteArray(marker_array_b, pix_x, pix_y)

# Close files
dst_ds = None
src_ds = None

Nota 1:

O comando gdal.RasterBand.WriteArray(array, xoff, yoff)opera no canto superior esquerdo. Neste exemplo, forneço uma matriz 1x1 com o valor 255, assim xoffe yoffsão a linha real, índices de col para a posição lon / lat. Se você deseja escrever um quadrado 3x3, precisa ajustar xoffe yoffsubtraindo 1. Você também deve garantir que o tipo de dados da matriz corresponda ao da varredura. Como você disse que quer um "ponto vermelho", estou assumindo que existem três faixas de uint8.

Logan Byers
fonte