Browse Source

new version

master
Andreas Demmelbauer 3 years ago
parent
commit
b1ae14879c
4 changed files with 532 additions and 364 deletions
  1. +2
    -5
      canvas.svg
  2. +378
    -0
      convert-svg-to-gistiff.py
  3. +0
    -263
      export-gis-jpg.py
  4. +152
    -96
      gis-svg-stitcher.py

+ 2
- 5
canvas.svg View File

@@ -19,9 +19,6 @@
xmlns:cc="http://creativecommons.org/ns#" xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"> xmlns:dc="http://purl.org/dc/elements/1.1/">
<style> <style>
.thermal_image {
opacity: .5 ;
}
g.tile[data-direction='down'] { g.tile[data-direction='down'] {
opacity: 1; opacity: 1;
} }
@@ -30,7 +27,7 @@ g.tile[data-direction='down'] {
} --> } -->
</style> </style>
<defs id="defs2"> <defs id="defs2">
<!-- <filter
<filter
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
style="color-interpolation-filters:sRGB;" style="color-interpolation-filters:sRGB;"
inkscape:label="Blur" inkscape:label="Blur"
@@ -56,7 +53,7 @@ g.tile[data-direction='down'] {
y="-25" y="-25"
style="fill:#ffffff;filter:url(#filter156128)" style="fill:#ffffff;filter:url(#filter156128)"
transform="matrix(0.77703373,0,0,0.74018882,207.24036,100.70559)" /> transform="matrix(0.77703373,0,0,0.74018882,207.24036,100.70559)" />
</mask> -->
</mask>


</defs> </defs>
<sodipodi:namedview <sodipodi:namedview


+ 378
- 0
convert-svg-to-gistiff.py View File

@@ -0,0 +1,378 @@
import os
import argparse
import lxml.etree as ET
import subprocess
import flirimageextractor
import cv2
import numpy as np
from pathlib import Path
from selenium import webdriver
import rasterio
# import shapefile

arg_parser = argparse.ArgumentParser(description='Export SVG composition of FLIR images as TIFF with thermo layer')

arg_parser.add_argument('Input',
metavar='input_svg',
type=str,
help='Path to the input SVG file cotaining xlinks to FLIR images')

arg_parser.add_argument('Output',
metavar='output_tiff',
type=str,
help='Output filename')


args = arg_parser.parse_args()
dirname = os.path.dirname(__file__)
INPUT_PATH = os.path.join(dirname, args.Input)
INPUT_DIR = os.path.split(INPUT_PATH)[0]

TEMP_MAP_THERMALPNG_SVG_PATH = os.path.join(INPUT_DIR, 'map_thermalpng.svg')
TEMP_MAP_THERMALPNG_PATH = os.path.join(INPUT_DIR, 'map_thermalpng.png')
TEMP_MAP_PREVIEW_PATH = os.path.join(INPUT_DIR, 'map_preview.png')
TEMP_MAP_UNGROUPED_SVG_PATH = os.path.join(INPUT_DIR, 'map_ungrouped.svg')

TEMP_MAP_ALIGNMENTPROOF_PATH = os.path.join(INPUT_DIR, 'map_thermalpng_proof.png')


THERMALPNG_DIR = 'thermalpngs'

# VECTOR_SHAPEFILE_PATH = os.path.join(INPUT_DIR, 'shapefile')


OUTPUT_PATH = os.path.join(dirname, args.Output)



def make_thermalpng_tiles():
"""
Extract thermal infomration as greyscale PNG-16
(temp * 1000 to retain some decimals)
and save the png tiles.
Assuming, that the data will always have a positive value.
"""
print('Building PNG tiles representing the thermal data ...')
Path(os.path.join(INPUT_DIR, THERMALPNG_DIR)).mkdir(parents=True, exist_ok=True)
png_output_dir = os.path.join(INPUT_DIR, THERMALPNG_DIR)

for root_path, directories, file in os.walk(os.path.join(dirname, INPUT_DIR)):
for file in file:
if(file.endswith(".jpg")):
print(' Processing ' + file)
full_filepath = os.path.join(root_path, file)
flir = flirimageextractor.FlirImageExtractor()
flir.process_image(full_filepath)
thermal_img_np = flir.thermal_image_np
multiplied_image = cv2.multiply(thermal_img_np, 1000)
output_file_path = os.path.join(png_output_dir, file + '.thermal.png')
cv2.imwrite(output_file_path, multiplied_image.astype(np.uint16))




def make_thermalpng_svg():
"""
replaces the image paths with the thermal pngs
and creates new SVG file
"""
print('Replacing the images inside the SVG with the PNG tiles ...')
# print("svg_file")
# print(dir(svg_file))
tree = ET.parse(INPUT_PATH)
root = tree.getroot()
# print(ET.tostring(root))
# tile_rows = root.xpath('//image', namespaces={'n': "http://www.w3.org/2000/svg"})
# print(dir(root))
tile_elements = root.xpath('//*[@class="thermal_image"]')
linkattrib ='{http://www.w3.org/1999/xlink}href'
for tile in tile_elements:
tile.attrib[linkattrib] = os.path.join(THERMALPNG_DIR, tile.attrib[linkattrib] + '.thermal.png')
# Post Production
tile.attrib["mask"] = 'url(#tilefademask)'
tile.attrib["style"] = 'opacity:.7'


# newxml = ET.tostring(tree, encoding="unicode")
# print(newxml)
# return newxml




with open(TEMP_MAP_THERMALPNG_SVG_PATH, 'wb') as f:
tree.write(f, encoding='utf-8')

return tree



def make_thermalpng():
"""
exports the SVG canvas as Gray_16 PNG
"""
print('Creating a big 16-bit grayscale PNG image representing the thermal data out of the SVG file ...')
command = [
'/snap/bin/inkscape',
'--pipe',
'--export-type=png',
'--export-png-color-mode=Gray_16'
],
input_file = open(TEMP_MAP_THERMALPNG_SVG_PATH, "rb")
output_file = open(TEMP_MAP_THERMALPNG_PATH, "wb")
completed = subprocess.run(
*command,
cwd=INPUT_DIR, # needed for reative image links
stdin=input_file,
stdout=output_file
)
return completed

# def make_thermalpreview():
# """
# exports the preview image
# """
# command = [
# '/snap/bin/inkscape',
# '--pipe',
# '--export-type=png',
# '--export-png-color-mode=Gray_8'
# ],
# input_file = open(TEMP_MAP_THERMALPNG_SVG_PATH, "rb")
# output_file = open(TEMP_MAP_PREVIEW_PATH, "wb")
# completed = subprocess.run(
# *command,
# cwd=INPUT_DIR, # needed for reative image links
# stdin=input_file,
# stdout=output_file
# )
# return completed



def get_thermal_numpy_array():
print('Converting the PNG into NumPy Array and normalize temperature values ...')
image = cv2.imread(TEMP_MAP_THERMALPNG_PATH, cv2.IMREAD_ANYDEPTH)
image_float = image.astype(np.float32)
image_float_normalized = cv2.divide(image_float, 1000)
# print(image_float_normalized[1000][905]) # looking what's the value of some pixel
return image_float_normalized

def get_used_tiles_relpaths():
"""
outputs an array of all used tile filenames in the input SVG
(relative filepaths like they appear in the svg.)
"""
images = []
tree = ET.parse(INPUT_PATH)
root = tree.getroot()
tile_elements = root.xpath('//*[@class="thermal_image"]')
linkattrib ='{http://www.w3.org/1999/xlink}href'
for tile in tile_elements:
images.append(tile.attrib[linkattrib])
return images



# def deg_coordinates_to_decimal(coordStr):
# coordArr = coordStr.split(', ')
# calculatedCoordArray = []
# for calculation in coordArr:
# calculationArr = calculation.split('/')
# calculatedCoordArray.append(int(calculationArr[0]) / int(calculationArr[1]))
# degrees = calculatedCoordArray[0]
# minutes = calculatedCoordArray[1]
# seconds = calculatedCoordArray[2]
# decimal = (degrees + (minutes * 1/60) + (seconds * 1/60 * 1/60))
# # print(decimal)
# return decimal


# def read_coordinates_from_tile(filename):
# full_filepath = os.path.join(INPUT_DIR, filename)
# with Image(filename=full_filepath) as image:
# for key, value in image.metadata.items():
# if key == 'exif:GPSLatitude':
# # print('latstr', value)
# lat = deg_coordinates_to_decimal(value) # lat -> Y vertical
# if key == 'exif:GPSLongitude':
# # print('lonstr', value)
# lon = deg_coordinates_to_decimal(value) # lon -> X horizontal

# return [lat, lon]


# def get_coordinate_boundaries():
# image_names = get_used_tiles_relpaths()
# coordinates = {
# 'lat': [],
# 'lon': []
# }
# for filename in image_names:
# tile_coordinates = read_coordinates_from_tile(filename)
# coordinates['lat'].append(tile_coordinates[0])
# coordinates['lon'].append(tile_coordinates[1])

# boundaries = {
# 'xmin': min(coordinates['lon']),
# 'xmax': max(coordinates['lon']),
# 'ymin': min(coordinates['lat']),
# 'ymax': max(coordinates['lat']),
# }
# return boundaries


# def create_ungrouped_svg():
# """
# exports the SVG without any grouped elements
# (the quick and dirty way)
# """
# print('Create an SVG without groups ...')
# inkscape_actions = "select-all:groups; SelectionUnGroup; select-all:groups; SelectionUnGroup; select-all:groups; SelectionUnGroup; select-all:groups; SelectionUnGroup; select-all:groups; SelectionUnGroup; select-all:groups; SelectionUnGroup; select-all:groups; SelectionUnGroup; export-filename: {}; export-plain-svg; export-do;".format(TEMP_MAP_UNGROUPED_SVG_PATH)
# command = [
# '/snap/bin/inkscape',
# '--pipe',
# '--actions={}'.format(inkscape_actions),
# ],
# input_file = open(INPUT_PATH, "rb")
# completed = subprocess.run(
# *command,
# cwd=INPUT_DIR, # needed for reative image links
# stdin=input_file,
# )
# print('completed', completed)
# return completed


def get_ground_control_points():
"""
Using selenium with firefox for rendering the SVG and
getting the positional matching between GPS
and x/y coords of SVG-image.
image tags need to have the gps data attached as data attributes.
"""
print('Getting ground control points ...')
options = webdriver.firefox.options.Options()
options.headless = True
driver = webdriver.Firefox(options=options)
driver.get("file://{}".format(INPUT_PATH))
images = driver.find_elements_by_class_name("thermal_image")

gcps = []
for image in images:
location = image.location
size = image.size
raster_y = float(location['y'] + size['height']/2)
raster_x = float(location['x'] + size['width']/2)
reference_lon = float(image.get_attribute('data-lon'))
reference_lat = float(image.get_attribute('data-lat'))
imageMapping = rasterio.control.GroundControlPoint(row=raster_y, col=raster_x, x=reference_lon, y=reference_lat)
gcps.append(imageMapping)

driver.quit()
return gcps


# def make_vector_shapefile():
# w = shapefile.Writer(VECTOR_SHAPEFILE_PATH)
# w.field('name', 'C')

# tree = ET.parse(INPUT_PATH)
# root = tree.getroot()
# tiles = root.xpath('//*[@class="thermal_image"]')
# for index, tile in enumerate(tiles):
# w.point(float(tile.attrib['data-lon']), float(tile.attrib['data-lat']))
# w.record('point{}'.format(index))
# w.close()
# return True

def verify_coordinate_matching():
"""
During development, i want to proof
that the point/coordinate matching is right.
Producing a png file with red dots for visual proof.
"""
img = cv2.imread(TEMP_MAP_THERMALPNG_PATH, cv2.IMREAD_GRAYSCALE)
img = cv2.cvtColor(img,cv2.COLOR_GRAY2RGB)

options = webdriver.firefox.options.Options()
# options.headless = True
driver = webdriver.Firefox(options=options)
driver.get("file://{}".format(INPUT_PATH))
images = driver.find_elements_by_class_name("thermal_image")

gcps = []
for image in images:
location = image.location
size = image.size
raster_y = int(location['y'] + size['height']/2)
raster_x = int(location['x'] + size['width']/2)
reference_lon = float(image.get_attribute('data-lon'))
reference_lat = float(image.get_attribute('data-lat'))
print(raster_x, raster_y)
img = cv2.circle(img, (raster_x, raster_y), radius=10, color=(0, 0, 255), thickness=-1)
driver.quit()
cv2.imwrite(TEMP_MAP_ALIGNMENTPROOF_PATH, img)




def make_geotiff_image():

thermal_numpy_array = get_thermal_numpy_array()
thermal_numpy_array[thermal_numpy_array == 0] = np.NaN # zeros to NaN

# # coordinates of all tiles
# geo_bound = get_coordinate_boundaries()
# print('boundaries', geo_bound)

np_shape = thermal_numpy_array.shape
image_size = (np_shape[0], np_shape[1])

gcps = get_ground_control_points()
print('Applying affine transform ...')
gcp_transform = rasterio.transform.from_gcps(gcps)
print(gcp_transform)

print('Generating the GeoTiff ...')

raster_io_dataset = rasterio.open(
OUTPUT_PATH,
'w',
driver='GTiff',
height=thermal_numpy_array.shape[0],
width=thermal_numpy_array.shape[1],
count=1,
dtype=thermal_numpy_array.dtype,
transform=gcp_transform,
crs='+proj=latlong',
)
raster_io_dataset.write(thermal_numpy_array, 1)

# # try to get rid of the black frame
# src = rasterio.open(OUTPUT_PATH)
# src[src == 0] = np.NaN # zeros to NaN
# raster_io_dataset2 = rasterio.open(
# OUTPUT_PATH,
# 'w',
# driver='GTiff',
# height=src.shape[0],
# width=src.shape[1],
# count=1,
# dtype=src.dtype,
# crs='+proj=latlong',
# )
# raster_io_dataset2.write(src, 1)

print('Saved to ', OUTPUT_PATH)



# make_thermalpng_tiles()
make_thermalpng_svg()
make_thermalpng()
make_geotiff_image()

# # Helpers for debugging
# verify_coordinate_matching()
# make_vector_shapefile()

+ 0
- 263
export-gis-jpg.py View File

@@ -1,263 +0,0 @@
import os
import argparse
import lxml.etree as ET
import subprocess
import flirimageextractor
import cv2
import numpy as np
from pathlib import Path
from wand.image import Image
from osgeo import gdal
from osgeo import osr

arg_parser = argparse.ArgumentParser(description='Export SVG composition of FLIR images as TIFF with thermo layer')

arg_parser.add_argument('Input',
metavar='input_svg',
type=str,
help='Path to the input SVG file cotaining xlinks to FLIR images')

arg_parser.add_argument('Output',
metavar='output_tiff',
type=str,
help='Output filename')


args = arg_parser.parse_args()
dirname = os.path.dirname(__file__)
INPUT_PATH = os.path.join(dirname, args.Input)
INPUT_DIR = os.path.split(INPUT_PATH)[0]

TEMP_MAP_THERMALPNG_SVG_PATH = os.path.join(INPUT_DIR, 'map_thermalpng.svg')
TEMP_MAP_THERMALPNG_PATH = os.path.join(INPUT_DIR, 'map_thermalpng.png')
TEMP_MAP_PREVIEW_PATH = os.path.join(INPUT_DIR, 'map_preview.png')
THERMALPNG_DIR = 'thermalpngs'

OUTPUT_PATH = os.path.join(dirname, args.Output)



def make_thermalpng_tiles():
"""
Extract thermal infomration as greyscale PNG-16 (temp * 1000 to retain some decimals)
and save the png tiles
"""
Path(os.path.join(INPUT_DIR, THERMALPNG_DIR)).mkdir(parents=True, exist_ok=True)
png_output_dir = os.path.join(INPUT_DIR, THERMALPNG_DIR)

for root_path, directories, file in os.walk(os.path.join(dirname, INPUT_DIR)):
for file in file:
if(file.endswith(".jpg")):
print('Extracting thermal info from ' + file)
full_filepath = os.path.join(root_path, file)
flir = flirimageextractor.FlirImageExtractor()
flir.process_image(full_filepath)
thermal_img_np = flir.thermal_image_np
multiplied_image = cv2.multiply(thermal_img_np, 1000)
output_file_path = os.path.join(png_output_dir, file + '.thermal.png')
print(output_file_path)
cv2.imwrite(output_file_path, multiplied_image.astype(np.uint16))




def make_thermalpng_svg():
"""
replaces the image paths with the thermal pngs
and creates new SVG file
"""
# print("svg_file")
# print(dir(svg_file))
tree = ET.parse(INPUT_PATH)
root = tree.getroot()
# print(ET.tostring(root))
# tile_rows = root.xpath('//image', namespaces={'n': "http://www.w3.org/2000/svg"})
# print(dir(root))
tile_elements = root.xpath('//*[@class="thermal_image"]')
linkattrib ='{http://www.w3.org/1999/xlink}href'
for tile in tile_elements:
tile.attrib[linkattrib] = os.path.join(THERMALPNG_DIR, tile.attrib[linkattrib] + '.thermal.png')
# newxml = ET.tostring(tree, encoding="unicode")
# print(newxml)
# return newxml

with open(TEMP_MAP_THERMALPNG_SVG_PATH, 'wb') as f:
tree.write(f, encoding='utf-8')

return tree



def make_thermalpng():
"""
exports the SVG canvas as Gray_16 PNG
"""
command = [
'/snap/bin/inkscape',
'--pipe',
'--export-type=png',
'--export-png-color-mode=Gray_16'
],
input_file = open(TEMP_MAP_THERMALPNG_SVG_PATH, "rb")
output_file = open(TEMP_MAP_THERMALPNG_PATH, "wb")
completed = subprocess.run(
*command,
cwd=INPUT_DIR, # needed for reative image links
stdin=input_file,
stdout=output_file
)
return completed

def make_thermalpreview():
"""
exports the preview image
"""
command = [
'/snap/bin/inkscape',
'--pipe',
'--export-type=png',
'--export-png-color-mode=Gray_8'
],
input_file = open(TEMP_MAP_THERMALPNG_SVG_PATH, "rb")
output_file = open(TEMP_MAP_PREVIEW_PATH, "wb")
completed = subprocess.run(
*command,
cwd=INPUT_DIR, # needed for reative image links
stdin=input_file,
stdout=output_file
)
return completed

# def make_thermalpreview():
# """
# exports the preview image
# """
# command = [
# '/snap/bin/inkscape',
# '--pipe',
# '--export-type=png',
# '--export-png-color-mode=Gray_8'
# ]
# input_file = open(TEMP_MAP_THERMALPNG_SVG_PATH, "rb")
# output_file = open(TEMP_MAP_PREVIEW_PATH, "wb")
# completed = subprocess.run(
# *command,
# cwd=INPUT_DIR, # needed for reative image links
# stdin=input_file,
# stdout=output_file
# )
# return completed


def get_thermal_numpy_array():
# input_file = open(TEMP_MAP_THERMALPNG_PATH, "rb")
image = cv2.imread(TEMP_MAP_THERMALPNG_PATH, cv2.IMREAD_ANYDEPTH)
image_float = image.astype(np.float32)
image_float_normalized = cv2.divide(image_float, 1000)
print(image_float_normalized[1000][905])
# cv2.imshow("OpenCV Image Reading", image)
return image_float_normalized

def get_used_tiles_relpaths():
"""
outputs an array of all used tile filenames in the input SVG
(relative filepaths like they appear in the svg.)
"""
images = []
tree = ET.parse(INPUT_PATH)
root = tree.getroot()
tile_elements = root.xpath('//*[@class="thermal_image"]')
linkattrib ='{http://www.w3.org/1999/xlink}href'
for tile in tile_elements:
images.append(tile.attrib[linkattrib])
return images

def deg_coordinates_to_decimal(coordStr):
coordArr = coordStr.split(', ')
calculatedCoordArray = []
for calculation in coordArr:
calculationArr = calculation.split('/')
calculatedCoordArray.append(int(calculationArr[0]) / int(calculationArr[1]))
degrees = calculatedCoordArray[0]
minutes = calculatedCoordArray[1]
seconds = calculatedCoordArray[2]
decimal = (degrees + (minutes * 1/60) + (seconds * 1/60 * 1/60))
# print(decimal)
return decimal


def read_coordinates_from_tile(filename):
full_filepath = os.path.join(INPUT_DIR, filename)
with Image(filename=full_filepath) as image:
for key, value in image.metadata.items():
if key == 'exif:GPSLatitude':
# print('latstr', value)
lat = deg_coordinates_to_decimal(value) # lat -> Y vertical
if key == 'exif:GPSLongitude':
# print('lonstr', value)
lon = deg_coordinates_to_decimal(value) # lon -> X horizontal
if key == 'exif:GPSImgDirection':
direction = value.split('/')
print(int(direction[0])/int(direction[1])/2, ' ', (value))
return [lat, lon]

def get_coordinate_boundaries():
image_names = get_used_tiles_relpaths()
coordinates = {
'lat': [],
'lon': []
}
for filename in image_names:
tile_coordinates = read_coordinates_from_tile(filename)
coordinates['lat'].append(tile_coordinates[0])
coordinates['lon'].append(tile_coordinates[1])

boundaries = {
'xmin': min(coordinates['lon']),
'xmax': max(coordinates['lon']),
'ymin': min(coordinates['lat']),
'ymax': max(coordinates['lat']),
}
return boundaries


def make_geotiff_image():
thermal_numpy_array = get_thermal_numpy_array()

# coordinates of all tiles
geo_bound = get_coordinate_boundaries()
print('boundaries', geo_bound)

np_shape = thermal_numpy_array.shape
image_size = (np_shape[0], np_shape[1])

# set geotransform
nx = image_size[0]
ny = image_size[1]
xres = (geo_bound['xmax'] - geo_bound['xmin']) / float(nx)
yres = (geo_bound['ymax'] - geo_bound['ymin']) / float(ny)
geotransform = (geo_bound['xmin'], xres, 0, geo_bound['ymax'], 0, -yres)

# create the 3-band raster file
dst_ds = gdal.GetDriverByName('GTiff').Create(OUTPUT_PATH, ny, nx, 1, gdal.GDT_Float32)
dst_ds.SetGeoTransform(geotransform) # specify coords
srs = osr.SpatialReference() # establish encoding
res = srs.SetWellKnownGeogCS( "WGS84" ) # WGS84 lat/long
dst_ds.SetProjection(srs.ExportToWkt()) # export coords to file
dst_ds.GetRasterBand(1).WriteArray(thermal_numpy_array) # write thermal-band to the raster
dst_ds.FlushCache() # write to disk




# make_thermalpng_tiles()
# make_thermalpng_svg()
# make_thermalpreview()
# make_thermalpng()
make_geotiff_image()


# dataset = gdal.Open("working_result_example.tif", gdal.GA_ReadOnly)
# print(dir(dataset))
# print(dataset.GetMetadata_List())

+ 152
- 96
gis-svg-stitcher.py View File

@@ -1,23 +1,48 @@
from wand.image import Image
import PIL.Image
import io
import exiftool
import subprocess
import os import os
import argparse
from wand.image import Image
import lxml.etree as ET import lxml.etree as ET
import copy import copy
import math


from pyproj import CRS
from pyproj.aoi import AreaOfInterest
from pyproj.database import query_utm_crs_info
from pyproj import Transformer



arg_parser = argparse.ArgumentParser(description='Place drone FLIR-tiles into a SVG in order to edit them in Inkscape')


import cv2
import flirimageextractor
from matplotlib import cm
import numpy as np
import urllib.request


arg_parser.add_argument('Input',
metavar='input_directory',
type=str,
help='Path where the FLIR tiles are')




arg_parser.add_argument(
'--base_rotation', action='store', default=115,
help="Base orientation of drone in degrees (0-360) Defaults to 115",
type=int, dest='base_rotation'
)

arg_parser.add_argument(
'--scale', action='store', default=15,
help="Scaling (higher number leads to bigger canvas and less dense tiles) (defaults to 15)",
type=int, dest='scale'
)




args = arg_parser.parse_args()

dirname = os.path.dirname(__file__) dirname = os.path.dirname(__file__)


working_dir = 'source_images_full'
working_dir = args.Input
OUTPUT_PATH = os.path.join(working_dir,'map.svg')




filename = os.path.join(dirname, 'canvas.svg') filename = os.path.join(dirname, 'canvas.svg')
@@ -42,25 +67,10 @@ def deg_coordinates_to_decimal(coordStr):
seconds = calculatedCoordArray[2] seconds = calculatedCoordArray[2]
return (degrees + (minutes * 1/60) + (seconds * 1/60 * 1/60)) return (degrees + (minutes * 1/60) + (seconds * 1/60 * 1/60))


# # extracting TIF Data
# for root, directories, file in os.walk(os.path.join(dirname, working_dir)):
# for file in file:
# if(file.endswith(".jpg")):
# print(os.path.join(root, file))
# full_filepath = os.path.join(root, file)
# with exiftool.ExifTool() as et:
# cmd = ['exiftool', full_filepath, "-b", "-RawThermalImage"]
# tif_data = subprocess.check_output(cmd)
# tif_image = PIL.Image.open(io.BytesIO(tif_data))
# tif_filepath = os.path.join(dirname, working_dir, file.split('.')[0] + '_thermal.tif')
# tif_image.save(tif_filepath)
# print(tif_filepath)


# finding the boundaries of the whole canvas # finding the boundaries of the whole canvas
latsArr = [] latsArr = []
lonsArr = [] lonsArr = []

for root_path, directories, file in os.walk(os.path.join(dirname, working_dir)): for root_path, directories, file in os.walk(os.path.join(dirname, working_dir)):
for file in file: for file in file:
if(file.endswith(".jpg")): if(file.endswith(".jpg")):
@@ -86,24 +96,71 @@ minLat = min(latsArr)
minLon = min(lonsArr) minLon = min(lonsArr)
maxLat = max(latsArr) maxLat = max(latsArr)
maxLon = max(lonsArr) maxLon = max(lonsArr)
width = maxLon - minLon
height = maxLat- minLat
midLon = (minLon + maxLon) /2
midLat = (minLat + maxLat) /2



# find CRS system
utm_crs_list = query_utm_crs_info(
datum_name="WGS 84",
area_of_interest=AreaOfInterest(
west_lon_degree=minLon,
south_lat_degree=minLat,
east_lon_degree=maxLon,
north_lat_degree=maxLat,
),
)
utm_crs = CRS.from_epsg(utm_crs_list[0].code)
transformer = Transformer.from_crs("EPSG:4326", utm_crs, always_xy=True)

min_transformed_lon, min_transformed_lat = transformer.transform(minLon, minLat)
max_transformed_lon, max_transformed_lat = transformer.transform(maxLon, maxLat)


width = max_transformed_lon - min_transformed_lon
height = max_transformed_lat - min_transformed_lat



# def latlngToGlobalXY(lat, lng):
# earth_radius = 6371
# # Calculates x based on cos of average of the latitudes
# x = earth_radius * lng * math.cos((minLat + maxLat)/2)
# # Calculates y based on latitude
# y = earth_radius * lat
# return {x: x, y: y}

# def latlngToScreenXY(lat, lng):
# topLeft_corner = latlngToGlobalXY(minLat, minLon)
# bottomRight_corner = latlngToGlobalXY(maxLat, maxLon)
# # Calculate global X and Y for projection point
# pos = latlngToGlobalXY(lat, lng)
# # Calculate the percentage of Global X position in relation to total global width
# pos.perX = ((pos.x - topLeft_corner.x) / (bottomRight_corner.x - topLeft_corner.x))
# # Calculate the percentage of Global Y position in relation to total global height
# pos.perY = ((pos.y - topLeft_corner.y) / (bottomRight_corner.y - topLeft_corner.y))

# # Returns the screen position based on reference points
# return {
# x: p0.scrX + (p1.scrX - p0.scrX)*pos.perX,
# y: p0.scrY + (p1.scrY - p0.scrY)*pos.perY
# }










# placing the images into the svg # placing the images into the svg


rotation = 125


y_scale = -1800000 #-400000
x_scale = 655000 #-950000


# y_scale = 2600000 #-400000
# x_scale = 1200000 #-950000


image_rotation_up = rotation #32
image_rotation_down = rotation + 180 #192


# image_rotation_up = rotation #32
# image_rotation_down = rotation + 180 #192


for root_path, directories, file in os.walk(os.path.join(dirname, working_dir)): for root_path, directories, file in os.walk(os.path.join(dirname, working_dir)):
for file in file: for file in file:
@@ -111,52 +168,48 @@ for root_path, directories, file in os.walk(os.path.join(dirname, working_dir)):
print(os.path.join(root_path, file)) print(os.path.join(root_path, file))
full_filepath = os.path.join(root_path, file) full_filepath = os.path.join(root_path, file)
with Image(filename=full_filepath) as image: with Image(filename=full_filepath) as image:
print(image.width)
print(image.height)
# print(image.width)
# print(image.height)
for key, value in image.metadata.items(): for key, value in image.metadata.items():
# print("{}: {}".format(key, value)) # print("{}: {}".format(key, value))
if key == 'exif:GPSLatitude': if key == 'exif:GPSLatitude':
lat = deg_coordinates_to_decimal(value) - minLat
print('lat '+ str(lat))
lat = deg_coordinates_to_decimal(value)
lat_offset = lat - minLat
if key == 'exif:GPSLongitude': if key == 'exif:GPSLongitude':
lon = deg_coordinates_to_decimal(value) - minLon
print('lon '+ str(lon))
lon = deg_coordinates_to_decimal(value)
lon_offset = lon - minLon
if key == 'exif:GPSImgDirection': if key == 'exif:GPSImgDirection':
direction = value.split('/') direction = value.split('/')
rotation = int(direction[0])/int(direction[1])/2

rotation = ( int(direction[0]) / int(direction[1]) ) / 2 + args.base_rotation
print('rotation',rotation)
transformed_lon, transformed_lat = transformer.transform(lon, lat)
lon_offset = transformed_lon - min_transformed_lon
lat_offset = transformed_lat - min_transformed_lat
# print(transformed_lon, min_transformed_lon, transformed_lat, min_transformed_lat)
# print('lon_offset, lat_offset', lon_offset, lat_offset)
g_pos_el_attributes = { g_pos_el_attributes = {
# 'x': str(lat*scale),
# 'y': str(lon*scale),
'transform': "translate({}, {})".format(format(lon*x_scale, '.20f'), format(lat*y_scale, '.20f')),
'data-lat': format(lat, '.20f'),
'data-lon': format(lon, '.20f'),
'transform': "translate({}, {})".format(format(lon_offset*args.scale, '.20f'), format(lat_offset*args.scale*-1, '.20f')),
'data-lat-offset': format(lat_offset, '.20f'),
'data-lon-offset': format(lon_offset, '.20f'),
'class': 'tile', 'class': 'tile',
'id': 'tile_{}'.format(file.split('.')[0]), 'id': 'tile_{}'.format(file.split('.')[0]),
# 'style': 'opacity:.6',
} }
g_pos_el = ET.SubElement(main_layer, 'g', attrib=g_pos_el_attributes) g_pos_el = ET.SubElement(main_layer, 'g', attrib=g_pos_el_attributes)


g_offset_corr_el_attributes = { g_offset_corr_el_attributes = {
'transform': "translate(150, 0)",
'transform': "translate({}, {})".format(-image.width/2, -image.height/2),
'class': 'tile-offset-corr', 'class': 'tile-offset-corr',
} }
g_offset_corr_el = ET.SubElement(g_pos_el, 'g', attrib=g_offset_corr_el_attributes) g_offset_corr_el = ET.SubElement(g_pos_el, 'g', attrib=g_offset_corr_el_attributes)



g_center_el_attributes = {
'class': 'tile-center',
'transform': 'translate({}, {})'.format(str(image.width/2*-1), str(image.height/2*-1))
}
g_center_el = ET.SubElement(g_offset_corr_el, 'g', attrib=g_center_el_attributes)

g_rot_el_attributes = { g_rot_el_attributes = {
'class': 'tile-rotate', 'class': 'tile-rotate',
'data-image-rotation': str(image_rotation_up),
'data-image-dimensions': str(image.width/2) + ' ' + str(image.height/2),
'transform': 'rotate({} {} {})'.format(str(image_rotation_up), str(image.width/2), str(image.height/2))
'data-image-rotation': str(rotation),
'data-image-dimensions': str(image.width) + ' ' + str(image.height),
'transform': 'rotate({} {} {})'.format(str(rotation), str(image.width/2), str(image.height/2))
# 'transform': 'rotate({} {} {})'.format(str(rotation), 0,0)
} }
g_rot_el = ET.SubElement(g_center_el, 'g', attrib=g_rot_el_attributes)
g_rot_el = ET.SubElement(g_offset_corr_el, 'g', attrib=g_rot_el_attributes)


xlinkns ="http://www.w3.org/1999/xlink" xlinkns ="http://www.w3.org/1999/xlink"
image_el = ET.SubElement(g_rot_el, 'image', { image_el = ET.SubElement(g_rot_el, 'image', {
@@ -164,14 +217,10 @@ for root_path, directories, file in os.walk(os.path.join(dirname, working_dir)):
"{%s}href" % xlinkns: file, "{%s}href" % xlinkns: file,
"width": str(image.width), "width": str(image.width),
"height": str(image.height), "height": str(image.height),
"mask" : "url(#tilefademask)",
'data-lat': format(lat, '.20f'),
'data-lon': format(lon, '.20f'),
}) })


# transform_str = "translate(-{}, -{})".format(str(min(latsArr)*scale), str(min(lonsArr)*scale))
# print(transform_str)
# main_layer.attrib['transform'] = transform_str




# sort elements # sort elements
def getkey(elem): def getkey(elem):
@@ -184,55 +233,62 @@ def getkey(elem):
main_layer[:] = sorted(main_layer, key=getkey) main_layer[:] = sorted(main_layer, key=getkey)




# rotate image if previous element is under the current one
# find rows
# up/down is actually left/right or right/left
last_state = 'down' last_state = 'down'
for index, el in enumerate(main_layer): for index, el in enumerate(main_layer):
if(el.getprevious() is not None): if(el.getprevious() is not None):
if (el.getprevious().attrib['data-lon'] > el.attrib['data-lon'] or (el.getprevious().attrib['data-lon'] == el.attrib['data-lon'] and last_state == 'up')):
if (el.getprevious().attrib['data-lon-offset'] > el.attrib['data-lon-offset'] or (el.getprevious().attrib['data-lon-offset'] == el.attrib['data-lon-offset'] and last_state == 'up')):
print('up') print('up')
rot_el = el[0][0][0]
# print(rot_el.attrib['data-image-rotation'])
# print(rot_el.attrib['data-image-dimensions'])
rot_el = el[0][0]
el.attrib['data-direction'] = 'up' el.attrib['data-direction'] = 'up'

# print(el.attrib['data-lat'], el.getprevious().attrib['data-lat'])
else: else:
rot_el = el[0][0][0]
rot_el = el[0][0]
el.attrib['data-direction'] = 'down' el.attrib['data-direction'] = 'down'
# el.attrib['style'] = 'opacity:0'
new_rotation = image_rotation_down #float(rot_el.attrib['data-image-rotation']) + 180
rot_el.attrib['transform'] = "rotate({} {})".format(str(new_rotation), rot_el.attrib['data-image-dimensions'])
print('down') print('down')
# print(rot_el.attrib['data-image-rotation'])
# print(rot_el.attrib['data-image-dimensions'])


# NOT NEEDED SINCE THERE IS A ROTATION INFORMATION
# merge tiles into groups # merge tiles into groups
print(index)
print("el.attrib['data-direction'] " + el.attrib['data-direction'])
print("last_state " + last_state)
if index is 1 or last_state != el.attrib['data-direction']:
# print(index)
# print("el.attrib['data-direction'] " + el.attrib['data-direction'])
# print("last_state " + last_state)
if index is 1 or last_state is not el.attrib['data-direction']:
current_row = ET.SubElement(tile_rows, 'g', attrib={ 'class': 'tile-row' }) current_row = ET.SubElement(tile_rows, 'g', attrib={ 'class': 'tile-row' })
copyElem = copy.deepcopy(el) copyElem = copy.deepcopy(el)
current_row.insert(0, copyElem) current_row.insert(0, copyElem)
last_state = el.attrib['data-direction'] last_state = el.attrib['data-direction']


# remove temporary group
root.remove(main_layer) root.remove(main_layer)


# resize canvas to tiles and add some padding


with open(os.path.join(working_dir,'map.svg'), 'wb') as f:
tree.write(f, encoding='utf-8')


print(width, height, args.scale)
scaled_width = width * args.scale
scaled_height = height * args.scale

padding = 500

canvas_width = str(scaled_width + padding*2)
canvas_height = str(scaled_height + padding*2)


viewbox_x = str(padding * -1)
viewbox_y = str((scaled_height + padding) * -1)
viewbox_width = canvas_width
viewbox_height = canvas_height


# # get some base satellite map for reference
# apikey = "MYaMHCLtPz1fUfe0FzZqOMI35m893jIV80oeHG19Piw"
# lon_center =
# lat_center =
# zoom =
# map_width =
# request = "https://image.maps.ls.hereapi.com/mia/1.6/mapview?apiKey={}&c={},{}&sb=mk&t=1&z={}&w={}&nodot".format(apikey, lon_center, lat_center, zoom, map_width)
root.attrib['width'] = canvas_width
root.attrib['height'] = canvas_height
root.attrib['viewBox'] = "{} {} {} {}".format(viewbox_x, viewbox_y, viewbox_width, viewbox_height)



# Finally save the svg
with open(OUTPUT_PATH, 'wb') as f:
tree.write(f, encoding='utf-8')


# svg = ET.tostring(tree, encoding="unicode")
# print(svg)


print('Done!') print('Done!')



Loading…
Cancel
Save