Expected:
Merging 3 rasters results in a merged raster.
Actual:
Unable to allocate array with shape (1, 3875, 1291898) and data type int16: MemoryError
Traceback (most recent call last):
File "/var/task/lambda_function.py", line 169, in handler
demall, afdemall = merge.merge(dem_cropped_datasets)
File "/opt/python/rasterio/merge.py", line 141, in merge
dest = np.zeros((output_count, output_height, output_width), dtype=dtype)
MemoryError: Unable to allocate array with shape (1, 3875, 1291898) and data type int16
It appears that Rasterio's merge.merge() method doesn't work well with merging tiles which cross over the International Dateline. My guess is because the left most longitude of the tiles is around +178 and the right most longitude of the tiles is around -179. Which is understandably not a usual situation as you'd normally expect L < R.
It looks like Rasterio is trying to create a merged dataset which wraps the longest way around the earth, rather than the IDL.
I assume the code for merge could be altered to account for the IDL and take the shortest route when creating the array for merging?
dataset_left_of_idl = rasterio.open('path/to/dataset_left.tif', 'r')
dataset_right_of_idl = rasterio.open('path/to/dataset_right.tif', 'r')
datasets_to_merge = [dataset_left_of_idl, dataset_right_of_idl]
merged_datasets, merged_datasets_affine = merge.merge(datasets_to_merge)
I'm using SRTM Tiles from https://e4ftl01.cr.usgs.gov/, you need an account to access it, but if you do, the specific tiles I'm using are:
https://e4ftl01.cr.usgs.gov/MEASURES/SRTMGL1.003/2000.02.11/S17E179.SRTMGL1.hgt.zip
https://e4ftl01.cr.usgs.gov/MEASURES/SRTMGL1.003/2000.02.11/S17W180.SRTMGL1.hgt.zip
https://e4ftl01.cr.usgs.gov/MEASURES/SRTMGL1.003/2000.02.11/S16W180.SRTMGL1.hgt.zip
They cover the North East of Fiji.
AWS Lambda (With 3GB max memory)
Rasterio 1.1.0 via pip
GDAL 2.4.2
@sgillies many thanks for the label 馃槃
What is the normal time period for fixes potentially going in?
I'd be interested in contributing a fix but unsure where to start within the merge function... If you've got a rough idea I'd be up for taking a crack at this!
@ciaranevans here's the thing, which may be a blocker: rasterio's merge requires all inputs to have the same coordinate reference system and requires the output to have that same coordinate reference system. Unless your inputs are in a coordinate reference system centered on the antimeridian, we can't make an output centered on the antimeridian.
A workaround/hack that you could try: wrap the georeferencing of your western hemisphere inputs around so all your files have positive longitudes and merge them to get an output centered on 180 degrees east.
Woops wrong button.
@sgillies thanks for the reply! Ah right, I thought it might be a bit of a PITA!
I took a chance and calculated the distance between the left most bound of the left tile and the right most bound of the right tile and then set the bounds I passed into merge as
left, bottom, left + distance_between_left_and_right, top
It passed and wrote a tiff but the data inside didn't look right. Almost looked empty, I'll give it another go just to make sure I'm not doing something silly there. Otherwise I'll see if I can re-project them all into a joint CRS and see if that works!
It's unfortunate that in the dataset we're trying to generate, there's only that one place in Fiji that falls over haha!
@sgillies tweet was made before I raised this, removed, will be giving your workaround a go!
So, with a bit more Googling and experimenting, we've found a work around @sgillies
Steps go as follows:
calculate_default_transform() to required CRS (In this case, we're going from the SRTM tiles CRS to the MGRS squares{'CENTER_LONG': 180}
Appears that https://trac.osgeo.org/gdal/wiki/ConfigOptions#CENTER_LONG will alter the origin of your longitudes
rasterio.merge() as normalGives me:

I think the gaps are due to the reprojection. There is no tile available for the top left so that gap is expected.
Fiji is such a good test case. And I'm glad to know about CENTER_LONG, that's an obscure one.
There's a lot of overlap beween warping and merging; the latter is kind of a short cut when the input and output have the same CRS. I'm curious about what would happen if you skipped MGRS and called reproject using the same src_crs and dst_crs but with CENTER_LONG set to 180.0, with a destination file that had its left bounds at 179 degrees (say) and right bounds at 181 degrees. Would you be willing to try that?
Yeah, annoyingly the GDAL docs don't elaborate on what it actually is 馃槀
Just so I'm sure I understand your question, you'd like me to try the reproject with 180 center using the same CRS' for src and dst and then merge after? Happy to give it a go!
Or even skip the merge. If you create an output dataset that spans the inputs and has a central longitude of 180, you can reproject the inputs, one-by-one, directly into it*.
* Maybe, I haven't tried this.
Ah I see what you mean. I'll try give it a go tomorrow (finished work for today as I'm in the UK)
I should probably know this, but what's a easy way to make a dataset just based on bounds? I'm always just loading and transforming data rather than making it from scratch! 馃槗
@sgillies been giving this a bit of a go.
Steps:
dst is the empty tiff and hgt(1/2) are the tiles.dst_arr = dst.read()
kwargs = {'CENTER_LONG': 180}
warp.reproject(source=hgt1.read(),
destination=dst_arr,
src_transform=hgt1.transform,
src_crs=CRS.from_dict(init='epsg:4326'),
dst_transform=dst.transform,
dst_crs=CRS.from_dict(init='epsg:4326'),
resample=enums.Resampling.bilinear,
kwargs=kwargs)
show(dst_arr)
This is fine for the tile to the left of the AM:

But as soon as I move onto a tile the other side:
dst_arr = dst.read()
kwargs = {'CENTER_LONG': 180}
warp.reproject(source=hgt1.read(),
destination=dst_arr,
src_transform=hgt1.transform,
src_crs=CRS.from_dict(init='epsg:4326'),
dst_transform=dst.transform,
dst_crs=CRS.from_dict(init='epsg:4326'),
resample=enums.Resampling.bilinear,
kwargs=kwargs)
warp.reproject(source=hgt2.read(),
destination=dst_arr,
src_transform=hgt2.transform,
src_crs=CRS.from_dict(init='epsg:4326'),
dst_transform=dst.transform,
dst_crs=CRS.from_dict(init='epsg:4326'),
resample=enums.Resampling.bilinear,
kwargs=kwargs)
show(dst_arr)
I get an empty dataset:

I've tried with just doing the right hand side of the AM tiles and get the same thing
Any thoughts? 馃槃
Not sure if this is the order of things you suggested to try!
@sgillies I think I got it!
Annoyingly simple in the end!
Looks like a blob appeared on my AM!

Zoomed in without the basemap as my QGIS cries

Steps to reproduce
left to 180 + distanceold affine(old.a, old.b, left, old.d, old.e, top_bound)I'm gonna assume that this will happily reproject then into the MGRS CRS required, but it's late so I will 100% confirm that tomorrow.
Sad times. For some reason QGIS reprojection works, see:
Merged tile in EPSG:32701

However, when I do the same with the following code in rasterio:
with rasterio.open('potentially_merged_2.tif', 'r') as merged:
merged_full = merged.read()
merged_full = np.clip(merged_full, 0, 400) # So colour ramp isn't awful
show(merged_full)
aff, w, h = warp.calculate_default_transform(
src_crs=CRS.from_dict(init='epsg:4326'),
dst_crs=CRS.from_dict(init=TILECRS),
width=merged.meta['width'],
height=merged.meta['height'],
left=merged.bounds.left,
right=merged.bounds.right,
top=merged.bounds.top,
bottom=merged.bounds.bottom,
resolution=10
)
dest_arr = np.zeros((1, h, w), dtype='int16')
warp.reproject(
source=merged.read(),
destination=dest_arr,
src_transform=merged.transform,
src_crs=CRS.from_dict(init='epsg:4326'),
dst_transform=aff,
dst_crs=CRS.from_dict(init=TILECRS),
resampling=enums.Resampling.bilinear,
kwargs={'CENTER_LONG':180}
)
show(dest_arr)
First tile is merged, second is reprojected

It's almost as if the width returned by calculate_default_transform() is too small, if I swap:
dest_arr = np.zeros((1, h, w), dtype='int16')
With:
dest_arr = np.zeros((1, h, h), dtype='int16')
And use h wherever I referred to w, I get:

My assumption is that calculate_default_transform() clips at the edge of the CRS...
@sgillies is their an alternative to figuring out the affine, height and width?
Ah. Read some more into the docs for calculate_default_transform() and only noticed the Notes section. Also noticed that CHECK_WITH_INVERT_PROJ is defaulted to True.
Tried with:
with rasterio.Env(CHECK_WITH_INVERT_PROJ=False): wrapping it all and:

Which doesn't mess with the height or width.
Good to know that exists, this whole Anti Meridian issue is definitely a learning experience 馃ぃ
So, I have finally, finally got a perfect tile. We found that calculate_default_transform() didn't like the AM as our tiles were being shifted hundreds of meters off from the ground truth. I think this was due to the fact that these tiles stretch over two UTM Zones (Sad trombone) 馃幒
We've got a work around though:
crop=True) the merged tiles with the BBox.jp2 tiles from https://registry.opendata.aws/sentinel-2/)kwargs={'CENTER_LONG:180}) 



Sorry for the spamming of this issue @sgillies! Hopefully anyone in a similar situation will find this all handy. Not sure what the artefacting is at the bottom of our SRTM tile, but in our downstream pipelines that will get replaced by adjacent images.
鉂わ笍 You Rasterio
Most interesting, but just WRT the title the International Date Line is not the same thing as 180 meridian.
Most interesting, but just WRT the title the International Date Line is not the same thing as 180 meridian.
Yes good shout, sorry we'd been using it colloquially at work but have changed now!
Thanks for summarizing @ciaranevans. I'm going to remove the bug label as merge can't merge at the edges of a reference system and the solution involves reprojecting the data.
No worries @sgillies thanks for the input through it!
Yes good shout, sorry we'd been using it colloquially at work but have changed now!
Ah good. I can stop being offended now.