Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/progress bar #37

Merged
merged 18 commits into from
Jun 18, 2024
61 changes: 6 additions & 55 deletions elevation_tile_tools/elevation_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,15 @@

import numpy as np

from PyQt5.QtWidgets import QMessageBox


class TileQuantityException(Exception):
def __init__(self, max_number_of_tiles, number_of_tiles):
self.number_of_tiles = number_of_tiles
self.max_number_of_tiles = max_number_of_tiles

def __str__(self):
return (
f"取得タイル数({self.number_of_tiles}枚)が多すぎます。\n"
f"上限の{self.max_number_of_tiles}枚を超えないように取得領域を狭くするか、ズームレベルを小さくしてください。"
)


class UserTerminationException(Exception):
pass


class ElevationArray:

def __init__(self, zoom_level, start_path, end_path):
self.max_number_of_tiles = 1000
self.large_number_of_tiles = 100
self.zoom_level = zoom_level
self.start_path = start_path
self.end_path = end_path
self.x_length = range(self.start_path[0], self.end_path[0] + 1)
self.y_length = range(self.start_path[1], self.end_path[1] + 1)

# タイル座標から標高タイルを読み込む
@staticmethod
Expand All @@ -41,41 +24,9 @@ def fetch_tile(z, x, y):
return array

# 範囲内の全ての標高タイルをマージ
def fetch_all_tiles(self):
x_length = range(self.start_path[0], self.end_path[0] + 1)
y_length = range(self.start_path[1], self.end_path[1] + 1)

number_of_tiles = len(x_length) * len(y_length)
def count_tiles(self) -> int:
number_of_tiles = len(self.x_length) * len(self.y_length)
print(f"number of tiles:{number_of_tiles}")

if number_of_tiles > self.max_number_of_tiles:
raise TileQuantityException(self.max_number_of_tiles, number_of_tiles)
elif number_of_tiles > self.large_number_of_tiles:
message = (
f"取得タイル数({number_of_tiles}枚)が多いため、処理に時間がかかる可能性があります。"
"ダウンロードを実行しますか?"
)
if QMessageBox.No == QMessageBox.question(
None,
"確認",
message,
QMessageBox.Yes,
QMessageBox.No,
):
raise UserTerminationException

all_array = np.concatenate(
[
np.concatenate(
[self.fetch_tile(self.zoom_level, x, y) for y in y_length], axis=0
)
for x in x_length
],
axis=1,
)

if (all_array == -9999).all():
raise Exception(
"The specified extent is out of range from the provided dem tiles"
)
return all_array
return number_of_tiles
bordoray marked this conversation as resolved.
Show resolved Hide resolved
121 changes: 82 additions & 39 deletions elevation_tile_tools/elevation_tile_converter.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
from math import atan, exp, pi
from pathlib import Path

import numpy as np

import pyproj

from PyQt5.QtCore import QThread, pyqtSignal

from .elevation_array import ElevationArray
from .geotiff import GeoTiff
from .tile_coordinate import TileCoordinate


class ElevationTileConverter:
class ElevationTileConverter(QThread):
# thread signals to progress dialog
# use : set maximum value in progress bar: self.setMaximum.emit(110)
setMaximum = pyqtSignal(int)
addProgress = pyqtSignal(int)
postMessage = pyqtSignal(str)
processFinished = pyqtSignal()
setAbortable = pyqtSignal(bool)
processFailed = pyqtSignal(str)
bordoray marked this conversation as resolved.
Show resolved Hide resolved

max_number_of_tiles = 1000
large_number_of_tiles = 100
bordoray marked this conversation as resolved.
Show resolved Hide resolved

def __init__(
self,
output_path=Path(__file__).parent.parent / "GeoTiff",
output_crs_id="EPSG:3857",
zoom_level=10,
bbox=None
bbox=None,
):
super().__init__()
# x_min, y_min, x_max, y_max
if bbox is None:
bbox = [
Expand All @@ -31,6 +48,17 @@ def __init__(
self.tile_coordinate = TileCoordinate(self.zoom_level, self.bbox)
self.params_for_creating_geotiff = []

start_path, end_path, self.lower_left_tile_path, self.upper_right_tile_path = (
self.tile_coordinate.calc_tile_coordinates()
)

self.elevation_array = ElevationArray(self.zoom_level, start_path, end_path)
self.number_of_tiles = self.elevation_array.count_tiles()

def set_abort_flag(self, flag=True):
# used when abort signal is given
self.abort_flag = flag
bordoray marked this conversation as resolved.
Show resolved Hide resolved

# 緯度経度をWebメルカトルのXY座標に変換
@staticmethod
def transform_latlon_to_xy(latlon):
Expand All @@ -53,22 +81,20 @@ def transform_latlon_to_xy(latlon):

# タイル座標から、そのタイルの左下・右上の緯度経度を算出
@staticmethod
def tile_to_pixel_coordinate_of_corner(
z, x_tile_coordinate, y_tile_coordinate):
def tile_to_pixel_coordinate_of_corner(z, x_tile_coordinate, y_tile_coordinate):
# 楕円体の長半径
ellipsoid_radius = 6378137
# タイル原点(0,0)のXY座標
org_x = -1 * (2 * ellipsoid_radius * pi / 2)
org_y = 2 * ellipsoid_radius * pi / 2
# 楕円体の円周をタイルの枚数で割ってタイル1枚の距離を算出
unit = 2 * ellipsoid_radius * pi / (2 ** z)
unit = 2 * ellipsoid_radius * pi / (2**z)
# タイルの原点からタイル座標までの距離を算出
x = org_x + x_tile_coordinate * unit
y = org_y - y_tile_coordinate * unit

# 左下・右上の緯度経度を算出
lower_left_lat = atan(
exp((y - unit) * pi / 20037508.34)) * 360 / pi - 90
lower_left_lat = atan(exp((y - unit) * pi / 20037508.34)) * 360 / pi - 90
lower_left_lon = x * 180 / 20037508.34
lower_left_latlon = [lower_left_lat, lower_left_lon]

Expand All @@ -79,20 +105,46 @@ def tile_to_pixel_coordinate_of_corner(
return lower_left_latlon, upper_right_latlon

# 一括処理を行うメソッド
def calc(self):
start_path, end_path, lower_left_tile_path, upper_light_tile_path = self.tile_coordinate.calc_tile_coordinates()

self.elevation_array = ElevationArray(
self.zoom_level, start_path, end_path)
np_array = self.elevation_array.fetch_all_tiles()
x_length = np_array.shape[1]
y_length = np_array.shape[0]
def run(self):
try:
self.setMaximum.emit(self.number_of_tiles)
self.postMessage.emit("集計中")
self.setAbortable.emit(True)

# get elevation tile arrays
tiles = []
for x in self.elevation_array.x_length:
row_tiles = []
for y in self.elevation_array.y_length:
tile = self.elevation_array.fetch_tile(self.zoom_level, x, y)
row_tiles.append(tile)
self.addProgress.emit(1)
tiles.append(np.concatenate(row_tiles, axis=0))

self.np_array = np.concatenate(tiles, axis=1)

if (self.np_array == -9999).all():
error_message = (
"The specified extent is out of range from the provided dem tiles"
)
self.processFailed.emit(error_message)
except Exception as e:
self.processFailed.emit(e)

self.postMessage.emit("終了処理中")
self.processFinished.emit()

bordoray marked this conversation as resolved.
Show resolved Hide resolved
def create_geotiff(self):
x_length = self.np_array.shape[1]
y_length = self.np_array.shape[0]

lower_left_latlon = self.tile_to_pixel_coordinate_of_corner(
self.zoom_level, lower_left_tile_path[0], lower_left_tile_path[1]
self.zoom_level, self.lower_left_tile_path[0], self.lower_left_tile_path[1]
)[0]
upper_right_latlon = self.tile_to_pixel_coordinate_of_corner(
self.zoom_level, upper_light_tile_path[0], upper_light_tile_path[1]
self.zoom_level,
self.upper_right_tile_path[0],
self.upper_right_tile_path[1],
)[1]

lower_left_XY = self.transform_latlon_to_xy(lower_left_latlon)
Expand All @@ -102,30 +154,21 @@ def calc(self):
# どっちもプラス、マイナスなら、絶対値の大きい方から小さい方を引く
pixel_size_x = None
pixel_size_y = None
if upper_right_XY[0] >= 0 and lower_left_XY[0] >= 0:
pixel_size_x = (
abs(upper_right_XY[0]) - abs(lower_left_XY[0])) / x_length
pixel_size_y = - \
(abs(upper_right_XY[1]) - abs(lower_left_XY[1])) / y_length
elif upper_right_XY[0] <= 0 and lower_left_XY[0] <= 0:
pixel_size_x = (
abs(upper_right_XY[0]) - abs(lower_left_XY[0])) / x_length
pixel_size_y = - \
(abs(upper_right_XY[1]) - abs(lower_left_XY[1])) / y_length

if (upper_right_XY[0] >= 0 and lower_left_XY[0] >= 0) or (
upper_right_XY[0] <= 0 and lower_left_XY[0] <= 0
):
pixel_size_x = (abs(upper_right_XY[0]) - abs(lower_left_XY[0])) / x_length
pixel_size_y = -(abs(upper_right_XY[1]) - abs(lower_left_XY[1])) / y_length
# 片方がプラスなら絶対値を足す
elif upper_right_XY[0] <= 0 <= lower_left_XY[0]:
pixel_size_x = (
abs(upper_right_XY[0]) + abs(lower_left_XY[0])) / x_length
pixel_size_y = - \
(abs(upper_right_XY[1]) + abs(lower_left_XY[1])) / y_length
elif upper_right_XY[0] >= 0 >= lower_left_XY[0]:
pixel_size_x = (
abs(upper_right_XY[0]) + abs(lower_left_XY[0])) / x_length
pixel_size_y = - \
(abs(upper_right_XY[1]) + abs(lower_left_XY[1])) / y_length
elif (upper_right_XY[0] <= 0 <= lower_left_XY[0]) or (
upper_right_XY[0] >= 0 >= lower_left_XY[0]
):
pixel_size_x = (abs(upper_right_XY[0]) + abs(lower_left_XY[0])) / x_length
pixel_size_y = -(abs(upper_right_XY[1]) + abs(lower_left_XY[1])) / y_length

self.params_for_creating_geotiff = [
np_array,
self.np_array,
lower_left_XY[0],
upper_right_XY[1],
pixel_size_x,
Expand All @@ -136,7 +179,7 @@ def calc(self):

geotiff = GeoTiff(self.output_path)
geotiff.write_geotiff(
np_array,
self.np_array,
lower_left_XY[0],
upper_right_XY[1],
pixel_size_x,
Expand Down
Loading