Skip to content

Commit 4b9eaa3

Browse files
committed
Reshape dataset when landcover resolution != dem resolution and smooth medialaxis
1 parent 6db608d commit 4b9eaa3

8 files changed

Lines changed: 90 additions & 51 deletions

File tree

fct/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@
1111
***************************************************************************
1212
"""
1313

14-
__version__ = '1.1.0'
14+
__version__ = '1.2.0'

fct/cli/Tiles.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@
1818
import click
1919

2020
import rasterio as rio
21+
from rasterio.windows import from_bounds
2122
import numpy as np
2223
import fiona
2324
import fiona.crs
2425

25-
from ..config import config
26-
from ..tileio import as_window
27-
from ..cli import starcall
26+
from fct.config import config
27+
from fct.config.descriptors import DatasetResolver
28+
from fct.tileio import as_window
29+
from fct.cli import starcall
2830

2931
def ExtractTile(datasource, dataset, tile, tileset, overwrite=False):
3032

@@ -35,10 +37,13 @@ def ExtractTile(datasource, dataset, tile, tileset, overwrite=False):
3537
return
3638

3739
with rio.open(raster) as ds:
40+
#window = as_window(tile.bounds, ds.transform)
41+
window = from_bounds(*tile.bounds, ds.transform)
3842

39-
window = as_window(tile.bounds, ds.transform)
4043
data = ds.read(1, window=window, boundless=True, fill_value=ds.nodata)
41-
transform = ds.transform * ds.transform.translation(window.col_off, window.row_off)
44+
45+
# transform = ds.transform * ds.transform.translation(window.col_off, window.row_off)
46+
transform = ds.window_transform(window)
4247

4348
profile = ds.profile.copy()
4449
profile.update(

fct/corridor/ContinuityAnalysisMax.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,24 @@
1919
import logging
2020
from multiprocessing import Pool
2121
import numpy as np
22+
import cv2
2223

2324
import click
2425
import rasterio as rio
2526
from rasterio.windows import Window
2627

27-
from .. import speedup
28-
from ..cli import starcall
29-
from ..config import (
28+
from fct import speedup
29+
from fct.cli import starcall
30+
from fct.config import (
3031
config,
3132
LiteralParameter,
3233
DatasetParameter
3334
)
34-
from .. import transform as fct
35-
from ..tileio import (
35+
from fct import transform as fct
36+
from fct.tileio import (
3637
PadRaster,
3738
border
3839
)
39-
from .ValleyBottomFeatures import MASK_EXTERIOR
4040

4141
class Parameters:
4242
"""
@@ -154,10 +154,16 @@ def BoundlessRaster(row, col, dataset):
154154

155155
else:
156156

157+
if nearest_distance.shape != landcover.shape:
158+
nearest_distance = cv2.resize(nearest_distance, landcover.shape, interpolation=cv2.INTER_NEAREST)
159+
157160
out = np.full_like(landcover, nodata)
158161
distance = np.zeros_like(nearest_distance, dtype='float32')
159162
state = np.uint8(np.abs(nearest_distance) < 1)
160163

164+
if state.shape != landcover.shape:
165+
click.secho(f'Unable to fit tile {row}_{col}', fg='red')
166+
161167
state[landcover == nodata] = 255
162168

163169
if seeds:

fct/corridor/MedialAxis2.py

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919

2020
from shapely.geometry import (
2121
LineString,
22-
MultiLineString,
2322
shape
2423
)
2524
from shapely.ops import linemerge
25+
from shapelysmooth import chaikin_smooth
2626

2727
from fct.config import (
2828
config,
@@ -289,6 +289,24 @@ def FixOrientation(medialaxis, params):
289289

290290
return medialaxis
291291

292+
293+
def OrderAndMergeLines(geoms, params):
294+
"""
295+
Order list of LineString from upstream to downstream
296+
"""
297+
measure_raster = params.measure.filename()
298+
299+
coords = [list(l.coords) for l in geoms]
300+
301+
with rio.open(measure_raster) as ds:
302+
heights = [(np.median(list(ds.sample(c))).item(), c) for c in coords]
303+
heights = sorted(heights, key=lambda x: x[0], reverse=True)
304+
305+
coords = [l[1] for l in heights if l[0] != ds.nodata and l[0] > 0]
306+
medialaxis = LineString([i for sublist in coords for i in sublist])
307+
308+
return medialaxis
309+
292310
def MedialAxis(params):
293311
"""
294312
Calculate corridor medial axis
@@ -380,7 +398,13 @@ def open_medialaxis_sink(output, crs, driver='ESRI Shapefile'):
380398
continue
381399

382400
lines = sorted(lines, key=lambda line: -line.length)
383-
medialaxis = lines[0]
401+
# medialaxis = lines[0]
402+
filtered = [l for l in lines if l.length > 20*params.swath_length]
403+
404+
if not filtered:
405+
medialaxis = lines[0]
406+
else:
407+
medialaxis = linemerge(filtered)
384408

385409
else:
386410

@@ -399,7 +423,8 @@ def open_medialaxis_sink(output, crs, driver='ESRI Shapefile'):
399423

400424
geoms.append(FixOrientation(geom, params))
401425

402-
medialaxis = MultiLineString(geoms)
426+
medialaxis = OrderAndMergeLines(geoms, params)
427+
# medialaxis = MultiLineString(geoms)
403428

404429
else:
405430

@@ -551,21 +576,15 @@ def open_output_sink(filename, options):
551576

552577
widths = np.interp(measures, smoothed.measure, smoothed.values)
553578

554-
# simplified = LineString(
555-
# smooth_chaikin(
556-
# np.array([
557-
# c for c, weight, width
558-
# in zip(coords, weights, widths)
559-
# if filtr(weight, width)
560-
# ])))
561-
562579
simplified = LineString([
563580
coord for coord, weight, width
564581
in zip(coords, weights, widths)
565582
if filtr(weight, width)
566583
])
567584

585+
smoothed = chaikin_smooth(simplified, iters=2)
586+
568587
# print(len(simplified.coords))
569-
sink.send((axis, simplified))
588+
sink.send((axis, smoothed))
570589

571590
sink.close()

fct/profiles/SwathProfile.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def SwathProfileUnit(
152152
nearest = ds.read(1, window=window, boundless=True, fill_value=ds.nodata)
153153

154154
if nearest.shape != values.shape:
155-
nearest = cv2.resize(nearest.astype('float32'), dsize=(values.shape[1],values.shape[0]), interpolation=cv2.INTER_NEAREST).astype('uint32')
155+
nearest = cv2.resize(nearest.astype('float32'), dsize=(values.shape[1],values.shape[0]), interpolation=cv2.INTER_NEAREST)
156156

157157
# nearest_nodata = ds.nodata
158158

@@ -183,7 +183,7 @@ def SwathProfileUnit(
183183
swaths = measure_to_swath_identifier(swaths, params.swath_length)
184184

185185
if swaths.shape != values.shape:
186-
swaths = cv2.resize(swaths.astype('float32'), dsize=(values.shape[1],values.shape[0]), interpolation=cv2.INTER_NEAREST).astype('uint32')
186+
swaths = cv2.resize(swaths.astype('float32'), dsize=(values.shape[1],values.shape[0]), interpolation=cv2.INTER_NEAREST)
187187

188188
try:
189189

@@ -197,7 +197,7 @@ def SwathProfileUnit(
197197
logger.error('Window error on swath (%d, %f)', axis, measure)
198198
return None
199199

200-
mask = (nearest == axis) & (swaths == swath) & (values != nodata)
200+
mask = (nearest == float(axis)) & (swaths == float(swath)) & (values != nodata)
201201

202202
if np.sum(mask) == 0:
203203
return None

fct/profiles/ValleyBottomHeight.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,17 @@
44

55
from multiprocessing import Pool
66
import numpy as np
7-
from scipy.interpolate import interp1d
8-
from scipy.linalg import lstsq
97
from sklearn.linear_model import HuberRegressor
10-
from sklearn.preprocessing import StandardScaler
118
from sklearn.exceptions import ConvergenceWarning
129

1310
import click
14-
import fiona
1511
import rasterio as rio
16-
from shapely.geometry import LineString, asShape
1712
import xarray as xr
1813

19-
from ..cli import starcall
20-
from ..tileio import as_window
21-
from ..config import DatasetParameter
22-
from ..corridor.ValleyBottomFeatures import MASK_VALLEY_BOTTOM
23-
from ..metadata import set_metadata
24-
from .. import speedup
14+
from fct.cli import starcall
15+
from fct.tileio import as_window
16+
from fct.config import DatasetParameter
17+
from fct.corridor.ValleyBottomFeatures import MASK_VALLEY_BOTTOM
2518

2619
class Parameters:
2720
"""
@@ -233,7 +226,13 @@ def values():
233226
bounds,
234227
params)
235228

236-
return xr.concat(values(), 'swath', 'all')
229+
try:
230+
vals = xr.concat(values(), 'swath', 'all')
231+
except ValueError:
232+
click.secho(f'No data for axis {axis}', fg='red')
233+
vals = None
234+
235+
return vals
237236

238237
def ValleyBottomHeight(swath_bounds, params: Parameters, processes: int = 1, **kwargs):
239238

@@ -286,4 +285,5 @@ def arguments():
286285
with click.progressbar(pooled, length=length) as iterator:
287286
values = list(iterator)
288287

288+
values = [v for v in values if v is not None]
289289
return xr.concat(values, 'swath', 'all')

fct/tileio.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
import numpy as np
1212
import rasterio as rio
13-
from rasterio.windows import Window
13+
from rasterio.windows import Window, from_bounds
1414
from rasterio.warp import Resampling
1515

1616
from .config import config
@@ -43,21 +43,29 @@ def border(height, width):
4343
yield i, j
4444
offset = 0
4545

46+
# def as_window(bounds, transform):
47+
# """
48+
# Convert real world bounds (minx, miny, maxx, maxy)
49+
# to raster window using defined RasterIO geo-transform
50+
# """
51+
52+
# minx, miny, maxx, maxy = bounds
53+
54+
# row_offset, col_offset = fct.index(minx, maxy, transform)
55+
# row_end, col_end = fct.index(maxx, miny, transform)
56+
57+
# height = row_end - row_offset
58+
# width = col_end - col_offset
59+
60+
# return Window(col_offset, row_offset, width, height)
61+
4662
def as_window(bounds, transform):
4763
"""
4864
Convert real world bounds (minx, miny, maxx, maxy)
4965
to raster window using defined RasterIO geo-transform
5066
"""
5167

52-
minx, miny, maxx, maxy = bounds
53-
54-
row_offset, col_offset = fct.index(minx, maxy, transform)
55-
row_end, col_end = fct.index(maxx, miny, transform)
56-
57-
height = row_end - row_offset
58-
width = col_end - col_offset
59-
60-
return Window(col_offset, row_offset, width, height)
68+
return from_bounds(*bounds, transform)
6169

6270
def grow_window(window, padding):
6371
"""

requirements/fct.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ cython
22
wheel
33
numpy==1.23.5
44
pyyaml==6.0
5-
xarray==2023.1.0
5+
xarray==2024.6.0
66
netcdf4==1.6.2
7-
scipy==1.10.0
7+
scipy==1.14.0
88
rasterio==1.3.5
99
fiona==1.9.0
1010
shapely==1.7.0
@@ -14,3 +14,4 @@ duecredit==0.9.2
1414
python-dotenv==0.21.1
1515
matplotlib==3.6.3
1616
rastachimp==0.3
17+
shapelysmooth==0.2.0

0 commit comments

Comments
 (0)