Skip to content

Commit 567cb30

Browse files
authored
Merge pull request #893 from pierotofy/cogeogdal
Create COGEOs using GDAL
2 parents b56c355 + 9026ecb commit 567cb30

File tree

6 files changed

+533
-9
lines changed

6 files changed

+533
-9
lines changed

Dockerfile

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM python:3.6-stretch
1+
FROM debian:stretch
22
MAINTAINER Piero Toffanin <[email protected]>
33

44
ENV PYTHONUNBUFFERED 1
@@ -9,17 +9,20 @@ ENV PROJ_LIB=/usr/share/proj
99
RUN mkdir /webodm
1010
WORKDIR /webodm
1111

12-
RUN curl --silent --location https://deb.nodesource.com/setup_10.x | bash -
13-
RUN apt-get -qq install -y nodejs
12+
# Install Node.js
13+
RUN apt-get -qq update && apt-get -qq install -y --no-install-recommends wget
14+
RUN wget --no-check-certificate https://deb.nodesource.com/setup_12.x -O /tmp/node.sh && bash /tmp/node.sh
15+
RUN apt-get -qq update && apt-get -qq install -y nodejs
1416

1517
# Configure use of testing branch of Debian
1618
RUN printf "Package: *\nPin: release a=stable\nPin-Priority: 900\n" > /etc/apt/preferences.d/stable.pref
1719
RUN printf "Package: *\nPin: release a=testing\nPin-Priority: 750\n" > /etc/apt/preferences.d/testing.pref
1820
RUN printf "deb http://ftp.us.debian.org/debian/ stable main contrib non-free\ndeb-src http://ftp.us.debian.org/debian/ stable main contrib non-free" > /etc/apt/sources.list.d/stable.list
1921
RUN printf "deb http://ftp.us.debian.org/debian/ testing main contrib non-free\ndeb-src http://ftp.us.debian.org/debian/ testing main contrib non-free" > /etc/apt/sources.list.d/testing.list
2022

21-
# Install Node.js GDAL, nginx, letsencrypt, psql
22-
RUN apt-get -qq update && apt-get -qq install -t testing -y binutils libproj-dev gdal-bin nginx certbot grass-core && apt-get -qq install -y gettext-base cron postgresql-client-9.6
23+
# Install Python3, GDAL, nginx, letsencrypt, psql
24+
RUN apt-get -qq update && apt-get -qq install -t testing -y --no-install-recommends python3 python3-pip git g++ python3-dev libpq-dev binutils libproj-dev gdal-bin python3-gdal nginx certbot grass-core && apt-get -qq install -y --no-install-recommends gettext-base cron postgresql-client-9.6
25+
RUN update-alternatives --install /usr/bin/python python /usr/bin/python2.7 1 && update-alternatives --install /usr/bin/python python /usr/bin/python3.8 2
2326

2427
# Install pip reqs
2528
ADD requirements.txt /webodm/
@@ -40,6 +43,10 @@ RUN npm install --quiet -g webpack && npm install --quiet -g webpack-cli && npm
4043
RUN python manage.py collectstatic --noinput
4144
RUN bash app/scripts/plugin_cleanup.sh && echo "from app.plugins import build_plugins;build_plugins()" | python manage.py shell
4245

46+
# Cleanup
47+
RUN apt-get remove -y g++ python3-dev libpq-dev && apt-get autoremove -y
48+
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
49+
4350
RUN rm /webodm/webodm/secret_key.py
4451

4552
VOLUME /webodm/app/media

app/cogeo.py

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
import tempfile
44
import shutil
55
import rasterio
6+
import re
7+
import subprocess
8+
from pipes import quote
69
from rio_cogeo.cogeo import cog_validate, cog_translate
710
from rio_tiler.utils import has_alpha_band
811
from webodm import settings
@@ -15,7 +18,14 @@ def valid_cogeo(src_path):
1518
:param src_path: path to GeoTIFF
1619
:return: true if the GeoTIFF is a cogeo, false otherwise
1720
"""
18-
return cog_validate(src_path, strict=True)
21+
try:
22+
from app.vendor.validate_cloud_optimized_geotiff import validate
23+
warnings, errors, details = validate(src_path, full_check=True)
24+
return not errors and not warnings
25+
except ModuleNotFoundError:
26+
logger.warning("Using legacy cog_validate (osgeo.gdal package not found)")
27+
# Legacy
28+
return cog_validate(src_path, strict=True)
1929

2030

2131
def assure_cogeo(src_path):
@@ -36,6 +46,90 @@ def assure_cogeo(src_path):
3646

3747
# Not a cogeo
3848
logger.info("Optimizing %s as Cloud Optimized GeoTIFF" % src_path)
49+
50+
# Check if we have GDAL >= 3.1
51+
use_legacy = False
52+
gdal_version = get_gdal_version()
53+
if gdal_version:
54+
major, minor, build = gdal_version
55+
56+
# GDAL 2 and lower
57+
if major <= 2:
58+
use_legacy = True
59+
60+
# GDAL 3.0 and lower
61+
if major == 3 and minor < 1:
62+
use_legacy = True
63+
else:
64+
# This shouldn't happen
65+
use_legacy = True
66+
67+
if use_legacy:
68+
logger.warning("Using legacy implementation (GDAL >= 3.1 not found)")
69+
return make_cogeo_legacy(src_path)
70+
else:
71+
return make_cogeo_gdal(src_path)
72+
73+
def get_gdal_version():
74+
# Bit of a hack without installing
75+
# python bindings
76+
gdal_translate = shutil.which('gdal_translate')
77+
if not gdal_translate:
78+
return None
79+
80+
# Get version
81+
version_output = subprocess.check_output([gdal_translate, "--version"]).decode('utf-8')
82+
83+
m = re.match(r"GDAL\s+([\d+])\.([\d+])\.([\d+]),\s+released", version_output)
84+
if not m:
85+
return None
86+
87+
return tuple(map(int, m.groups()))
88+
89+
90+
def make_cogeo_gdal(src_path):
91+
"""
92+
Make src_path a Cloud Optimized GeoTIFF.
93+
Requires GDAL >= 3.1
94+
"""
95+
96+
tmpfile = tempfile.mktemp('_cogeo.tif', dir=settings.MEDIA_TMP)
97+
swapfile = tempfile.mktemp('_cogeo_swap.tif', dir=settings.MEDIA_TMP)
98+
99+
try:
100+
subprocess.run(["gdal_translate", "-of", "COG",
101+
"-co", "BLOCKSIZE=256",
102+
"-co", "COMPRESS=deflate",
103+
"-co", "NUM_THREADS=ALL_CPUS",
104+
"-co", "BIGTIFF=IF_SAFER",
105+
"--config", "GDAL_NUM_THREADS", "ALL_CPUS",
106+
quote(src_path), quote(tmpfile)])
107+
except Exception as e:
108+
logger.warning("Cannot create Cloud Optimized GeoTIFF: %s" % str(e))
109+
110+
if os.path.isfile(tmpfile):
111+
shutil.move(src_path, swapfile) # Move to swap location
112+
113+
try:
114+
shutil.move(tmpfile, src_path)
115+
except IOError as e:
116+
logger.warning("Cannot move %s to %s: %s" % (tmpfile, src_path, str(e)))
117+
shutil.move(swapfile, src_path) # Attempt to restore
118+
raise e
119+
120+
if os.path.isfile(swapfile):
121+
os.remove(swapfile)
122+
123+
return True
124+
else:
125+
return False
126+
127+
def make_cogeo_legacy(src_path):
128+
"""
129+
Make src_path a Cloud Optimized GeoTIFF
130+
This implementation does not require GDAL >= 3.1
131+
but sometimes (rarely) hangs for unknown reasons
132+
"""
39133
tmpfile = tempfile.mktemp('_cogeo.tif', dir=settings.MEDIA_TMP)
40134
swapfile = tempfile.mktemp('_cogeo_swap.tif', dir=settings.MEDIA_TMP)
41135

@@ -77,4 +171,8 @@ def assure_cogeo(src_path):
77171
raise e
78172

79173
if os.path.isfile(swapfile):
80-
os.remove(swapfile)
174+
os.remove(swapfile)
175+
176+
return True
177+
else:
178+
return False

app/static/app/js/components/TaskListItem.jsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,9 +285,21 @@ class TaskListItem extends React.Component {
285285
rfMap[rfParam].onClick = this.genRestartAction(rfParam);
286286
}
287287

288-
return task.can_rerun_from
288+
let items = task.can_rerun_from
289289
.map(rf => rfMap[rf])
290290
.filter(rf => rf !== undefined);
291+
292+
if (items.length > 0 && [statusCodes.CANCELED, statusCodes.FAILED].indexOf(task.status) !== -1){
293+
// Add resume "pseudo button" to help users understand
294+
// how to resume a task that failed for memory/disk issues.
295+
items.unshift({
296+
label: "Resume Processing",
297+
icon: "fa fa-bolt",
298+
onClick: this.genRestartAction(task.can_rerun_from[task.can_rerun_from.length - 1])
299+
});
300+
}
301+
302+
return items;
291303
}
292304

293305
genRestartAction(rerunFrom = null){

app/vendor/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)