def build_masks(tile): ########################################## def transition_profile(ratio, ttype): if ttype == 'spline': return 3 * ratio**2 - 2 * ratio**3 elif ttype == 'linear': return ratio elif ttype == 'parabolic': return 2 * ratio - ratio**2 ########################################## UI.red_flag = False UI.logprint("Step 2.5 for tile lat=", tile.lat, ", lon=", tile.lon, ": starting.") UI.vprint( 0, "\nStep 2.5 : Building masks for tile " + FNAMES.short_latlon(tile.lat, tile.lon) + " : \n--------\n") timer = time.time() if not os.path.exists(FNAMES.mesh_file(tile.build_dir, tile.lat, tile.lon)): UI.lvprint(0, "ERROR: Mesh file ", FNAMES.mesh_file(tile.build_dir, tile.lat, tile.lon), "absent.") UI.exit_message_and_bottom_line('') return 0 if not os.path.exists(FNAMES.mask_dir(tile.lat, tile.lon)): os.makedirs(FNAMES.mask_dir(tile.lat, tile.lon)) mesh_file_name_list = [] for close_lat in range(tile.lat - 1, tile.lat + 2): for close_lon in range(tile.lon - 1, tile.lon + 2): close_build_dir = tile.build_dir if tile.grouped else tile.build_dir.replace( FNAMES.tile_dir(tile.lat, tile.lon), FNAMES.tile_dir(close_lat, close_lon)) close_mesh_file_name = FNAMES.mesh_file(close_build_dir, close_lat, close_lon) if os.path.isfile(close_mesh_file_name): mesh_file_name_list.append(close_mesh_file_name) #################### dico_masks = {} dico_masks_inland = {} #################### [til_x_min, til_y_min] = GEO.wgs84_to_orthogrid(tile.lat + 1, tile.lon, tile.mask_zl) [til_x_max, til_y_max] = GEO.wgs84_to_orthogrid(tile.lat, tile.lon + 1, tile.mask_zl) UI.vprint(1, "-> Deleting existing masks") for til_x in range(til_x_min, til_x_max + 1, 16): for til_y in range(til_y_min, til_y_max + 1, 16): try: os.remove( os.path.join(FNAMES.mask_dir(tile.lat, tile.lon), FNAMES.legacy_mask(til_x, til_y))) except: pass UI.vprint(1, "-> Reading mesh data") for mesh_file_name in mesh_file_name_list: try: f_mesh = open(mesh_file_name, "r") UI.vprint(1, " * ", mesh_file_name) except: UI.lvprint(1, "Mesh file ", mesh_file_name, " could not be read. Skipped.") continue for i in range(0, 4): f_mesh.readline() nbr_pt_in = int(f_mesh.readline()) pt_in = numpy.zeros(5 * nbr_pt_in, 'float') for i in range(0, nbr_pt_in): pt_in[5 * i:5 * i + 3] = [float(x) for x in f_mesh.readline().split()[:3]] for i in range(0, 3): f_mesh.readline() for i in range(0, nbr_pt_in): pt_in[5 * i + 3:5 * i + 5] = [float(x) for x in f_mesh.readline().split()[:2]] for i in range(0, 2): # skip 2 lines f_mesh.readline() nbr_tri_in = int(f_mesh.readline()) # read nbr of tris step_stones = nbr_tri_in // 100 percent = -1 UI.vprint( 2, " Attribution process of masks buffers to water triangles for " + str(mesh_file_name) + ".") for i in range(0, nbr_tri_in): if i % step_stones == 0: percent += 1 UI.progress_bar(1, int(percent * 5 / 10)) if UI.red_flag: UI.exit_message_and_bottom_line() return 0 (n1, n2, n3, tri_type) = [int(x) - 1 for x in f_mesh.readline().split()[:4]] tri_type += 1 tri_type = (tri_type & 2) or (tri_type & 1) if (not tri_type) or (tri_type == 1 and not tile.use_masks_for_inland): continue (lon1, lat1) = pt_in[5 * n1:5 * n1 + 2] (lon2, lat2) = pt_in[5 * n2:5 * n2 + 2] (lon3, lat3) = pt_in[5 * n3:5 * n3 + 2] bary_lat = (lat1 + lat2 + lat3) / 3 bary_lon = (lon1 + lon2 + lon3) / 3 (til_x, til_y) = GEO.wgs84_to_orthogrid(bary_lat, bary_lon, tile.mask_zl) if til_x < til_x_min - 16 or til_x > til_x_max + 16 or til_y < til_y_min - 16 or til_y > til_y_max + 16: continue (til_x2, til_y2) = GEO.wgs84_to_orthogrid(bary_lat, bary_lon, tile.mask_zl + 2) a = (til_x2 // 16) % 4 b = (til_y2 // 16) % 4 if (til_x, til_y) in dico_masks: dico_masks[(til_x, til_y)].append( (lat1, lon1, lat2, lon2, lat3, lon3)) else: dico_masks[(til_x, til_y)] = [(lat1, lon1, lat2, lon2, lat3, lon3)] if a == 0: if (til_x - 16, til_y) in dico_masks: dico_masks[(til_x - 16, til_y)].append( (lat1, lon1, lat2, lon2, lat3, lon3)) else: dico_masks[(til_x - 16, til_y)] = [(lat1, lon1, lat2, lon2, lat3, lon3)] if b == 0: if (til_x - 16, til_y - 16) in dico_masks: dico_masks[(til_x - 16, til_y - 16)].append( (lat1, lon1, lat2, lon2, lat3, lon3)) else: dico_masks[(til_x - 16, til_y - 16)] = [ (lat1, lon1, lat2, lon2, lat3, lon3) ] elif b == 3: if (til_x - 16, til_y + 16) in dico_masks: dico_masks[(til_x - 16, til_y + 16)].append( (lat1, lon1, lat2, lon2, lat3, lon3)) else: dico_masks[(til_x - 16, til_y + 16)] = [ (lat1, lon1, lat2, lon2, lat3, lon3) ] elif a == 3: if (til_x + 16, til_y) in dico_masks: dico_masks[(til_x + 16, til_y)].append( (lat1, lon1, lat2, lon2, lat3, lon3)) else: dico_masks[(til_x + 16, til_y)] = [(lat1, lon1, lat2, lon2, lat3, lon3)] if b == 0: if (til_x + 16, til_y - 16) in dico_masks: dico_masks[(til_x + 16, til_y - 16)].append( (lat1, lon1, lat2, lon2, lat3, lon3)) else: dico_masks[(til_x + 16, til_y - 16)] = [ (lat1, lon1, lat2, lon2, lat3, lon3) ] elif b == 3: if (til_x + 16, til_y + 16) in dico_masks: dico_masks[(til_x + 16, til_y + 16)].append( (lat1, lon1, lat2, lon2, lat3, lon3)) else: dico_masks[(til_x + 16, til_y + 16)] = [ (lat1, lon1, lat2, lon2, lat3, lon3) ] if b == 0: if (til_x, til_y - 16) in dico_masks: dico_masks[(til_x, til_y - 16)].append( (lat1, lon1, lat2, lon2, lat3, lon3)) else: dico_masks[(til_x, til_y - 16)] = [(lat1, lon1, lat2, lon2, lat3, lon3)] elif b == 3: if (til_x, til_y + 16) in dico_masks: dico_masks[(til_x, til_y + 16)].append( (lat1, lon1, lat2, lon2, lat3, lon3)) else: dico_masks[(til_x, til_y + 16)] = [(lat1, lon1, lat2, lon2, lat3, lon3)] f_mesh.close() if not tile.use_masks_for_inland: UI.vprint(2, " Taking care of inland water near shoreline") f_mesh = open(mesh_file_name, "r") for i in range(0, 4): f_mesh.readline() nbr_pt_in = int(f_mesh.readline()) for i in range(0, 2 * nbr_pt_in + 5): f_mesh.readline() nbr_tri_in = int(f_mesh.readline()) # read nbr of tris step_stones = nbr_tri_in // 100 percent = -1 for i in range(0, nbr_tri_in): if i % step_stones == 0: percent += 1 UI.progress_bar(1, int(percent * 5 / 10)) if UI.red_flag: UI.exit_message_and_bottom_line() return 0 (n1, n2, n3, tri_type) = [ int(x) - 1 for x in f_mesh.readline().split()[:4] ] tri_type += 1 tri_type = (tri_type & 2) or (tri_type & 1) if not (tri_type == 1): continue (lon1, lat1) = pt_in[5 * n1:5 * n1 + 2] (lon2, lat2) = pt_in[5 * n2:5 * n2 + 2] (lon3, lat3) = pt_in[5 * n3:5 * n3 + 2] bary_lat = (lat1 + lat2 + lat3) / 3 bary_lon = (lon1 + lon2 + lon3) / 3 (til_x, til_y) = GEO.wgs84_to_orthogrid(bary_lat, bary_lon, tile.mask_zl) if til_x < til_x_min - 16 or til_x > til_x_max + 16 or til_y < til_y_min - 16 or til_y > til_y_max + 16: continue (til_x2, til_y2) = GEO.wgs84_to_orthogrid(bary_lat, bary_lon, tile.mask_zl + 2) a = (til_x2 // 16) % 4 b = (til_y2 // 16) % 4 # Here an inland water tri is added ONLY if sea water tri were already added for this mask extent if (til_x, til_y) in dico_masks: if (til_x, til_y) in dico_masks_inland: dico_masks_inland[(til_x, til_y)].append( (lat1, lon1, lat2, lon2, lat3, lon3)) else: dico_masks_inland[(til_x, til_y)] = [ (lat1, lon1, lat2, lon2, lat3, lon3) ] f_mesh.close() UI.vprint(1, "-> Construction of the masks") if tile.masks_use_DEM_too: tile.ensure_elevation_data() task_len = len(dico_masks) task_done = 0 for (til_x, til_y) in dico_masks: if UI.red_flag: UI.exit_message_and_bottom_line() return 0 task_done += 1 UI.progress_bar(1, 50 + int(49 * task_done / task_len)) if til_x < til_x_min or til_x > til_x_max or til_y < til_y_min or til_y > til_y_max: continue (latm0, lonm0) = GEO.gtile_to_wgs84(til_x, til_y, tile.mask_zl) (px0, py0) = GEO.wgs84_to_pix(latm0, lonm0, tile.mask_zl) px0 -= 1024 py0 -= 1024 # 1) We start with a black mask mask_im = Image.new("L", (4096 + 2 * 1024, 4096 + 2 * 1024), 'black') mask_draw = ImageDraw.Draw(mask_im) # 2) We fill it with white over the extent of each tile around for which we had a mesh available for mesh_file_name in mesh_file_name_list: latlonstr = mesh_file_name.split('.mes')[-2][-7:] lathere = int(latlonstr[0:3]) lonhere = int(latlonstr[3:7]) (px1, py1) = GEO.wgs84_to_pix(lathere, lonhere, tile.mask_zl) (px2, py2) = GEO.wgs84_to_pix(lathere, lonhere + 1, tile.mask_zl) (px3, py3) = GEO.wgs84_to_pix(lathere + 1, lonhere + 1, tile.mask_zl) (px4, py4) = GEO.wgs84_to_pix(lathere + 1, lonhere, tile.mask_zl) px1 -= px0 px2 -= px0 px3 -= px0 px4 -= px0 py1 -= py0 py2 -= py0 py3 -= py0 py4 -= py0 mask_draw.polygon([(px1, py1), (px2, py2), (px3, py3), (px4, py4)], fill='white') # 3a) We overwrite the withe part of the mask with grey (ratio_water dependent) where inland water was detected in the first part above if (til_x, til_y) in dico_masks_inland: for (lat1, lon1, lat2, lon2, lat3, lon3) in dico_masks_inland[(til_x, til_y)]: (px1, py1) = GEO.wgs84_to_pix(lat1, lon1, tile.mask_zl) (px2, py2) = GEO.wgs84_to_pix(lat2, lon2, tile.mask_zl) (px3, py3) = GEO.wgs84_to_pix(lat3, lon3, tile.mask_zl) px1 -= px0 px2 -= px0 px3 -= px0 py1 -= py0 py2 -= py0 py3 -= py0 mask_draw.polygon([(px1, py1), (px2, py2), (px3, py3)], fill=int(255 * (1 - tile.ratio_water))) # 3b) We overwrite the withe + grey part of the mask with black where sea water was detected in the first part above for (lat1, lon1, lat2, lon2, lat3, lon3) in dico_masks[(til_x, til_y)]: (px1, py1) = GEO.wgs84_to_pix(lat1, lon1, tile.mask_zl) (px2, py2) = GEO.wgs84_to_pix(lat2, lon2, tile.mask_zl) (px3, py3) = GEO.wgs84_to_pix(lat3, lon3, tile.mask_zl) px1 -= px0 px2 -= px0 px3 -= px0 py1 -= py0 py2 -= py0 py3 -= py0 mask_draw.polygon([(px1, py1), (px2, py2), (px3, py3)], fill='black') del (mask_draw) #mask_im=mask_im.convert("L") img_array = numpy.array(mask_im, dtype=numpy.uint8) if tile.masks_use_DEM_too: #computing the part of the mask coming from the DEM: (latmax, lonmin) = GEO.pix_to_wgs84(px0, py0, tile.mask_zl) (latmin, lonmax) = GEO.pix_to_wgs84(px0 + 6144, py0 + 6144, tile.mask_zl) (x03857, y03857) = GEO.transform('4326', '3857', lonmin, latmax) (x13857, y13857) = GEO.transform('4326', '3857', lonmax, latmin) ((lonmin, lonmax, latmin, latmax), demarr4326) = tile.dem.super_level_set( 1, (lonmin, lonmax, latmin, latmax)) if demarr4326.any(): demim4326 = Image.fromarray( demarr4326.astype(numpy.uint8) * 255) del (demarr4326) s_bbox = (lonmin, latmax, lonmax, latmin) t_bbox = (x03857, y03857, x13857, y13857) demim3857 = IMG.gdalwarp_alternative(s_bbox, '4326', demim4326, t_bbox, '3857', (6144, 6144)) demim3857 = demim3857.filter( ImageFilter.GaussianBlur( 0.3 * 2**(tile.mask_zl - 14))) # slight increase of area dem_array = (numpy.array(demim3857, dtype=numpy.uint8) > 0).astype(numpy.uint8) * 255 del (demim3857) del (demim4326) img_array = numpy.maximum(img_array, dem_array) custom_mask_array = numpy.zeros((4096, 4096), dtype=numpy.uint8) if tile.masks_custom_extent: (latm1, lonm1) = GEO.gtile_to_wgs84(til_x + 16, til_y + 16, tile.mask_zl) bbox_4326 = (lonm0, latm0, lonm1, latm1) masks_im = IMG.has_data(bbox_4326, tile.masks_extent_code, True, mask_size=(4096, 4096), is_sharp_resize=False, is_mask_layer=False) if masks_im: custom_mask_array = (numpy.array(masks_im, dtype=numpy.uint8) * tile.ratio_water).astype(numpy.uint8) if (img_array.max() == 0) and ( custom_mask_array.max() == 0 ): # no need to test if the mask is all white since it would otherwise not be present in dico_mask UI.vprint(1, " Skipping", FNAMES.legacy_mask(til_x, til_y)) continue else: UI.vprint(1, " Creating", FNAMES.legacy_mask(til_x, til_y)) # Blur of the mask pxscal = GEO.webmercator_pixel_size(tile.lat + 0.5, tile.mask_zl) if tile.masking_mode == "sand": blur_width = int(tile.masks_width / pxscal) elif tile.masking_mode == "rocks": blur_width = tile.masks_width / pxscal elif tile.masking_mode == "3steps": blur_width = [L / pxscal for L in tile.masks_width] if tile.masking_mode == "sand" and blur_width: # convolution with a hat function b_img_array = numpy.array(img_array) kernel = numpy.array(range(1, 2 * blur_width)) kernel[blur_width:] = range(blur_width - 1, 0, -1) kernel = kernel / blur_width**2 for i in range(0, len(b_img_array)): b_img_array[i] = numpy.convolve(b_img_array[i], kernel, 'same') b_img_array = b_img_array.transpose() for i in range(0, len(b_img_array)): b_img_array[i] = numpy.convolve(b_img_array[i], kernel, 'same') b_img_array = b_img_array.transpose() b_img_array = 2 * numpy.minimum(b_img_array, 127) b_img_array = numpy.array(b_img_array, dtype=numpy.uint8) elif tile.masking_mode == "rocks" and blur_width: # slight increase of the mask, then gaussian blur, nonlinear map and a tiny bit of smoothing again on a short scale along the shore b_img_array=(numpy.array(Image.fromarray(img_array).convert("L").\ filter(ImageFilter.GaussianBlur(blur_width/1.7)),dtype=numpy.uint8)>0).astype(numpy.uint8)*255 #blur it b_img_array=numpy.array(Image.fromarray(b_img_array).convert("L").\ filter(ImageFilter.GaussianBlur(blur_width)),dtype=numpy.uint8) #nonlinear transform to make the transition quicker at the shore (gaussian is too flat) gamma = 2.5 b_img_array=(((numpy.tan((b_img_array.astype(numpy.float32)-127.5)/128*atan(3))-numpy.tan(-127.5/128*atan(3)))\ *254/(2*numpy.tan(127.5/128*atan(3))))**gamma/(255**(gamma-1))).astype(numpy.uint8) #b_img_array=numpy.minimum(b_img_array,200) #still some slight smoothing at the shore b_img_array=numpy.maximum(b_img_array,numpy.array(Image.fromarray(img_array).convert("L").\ filter(ImageFilter.GaussianBlur(2**(tile.mask_zl-14))),dtype=numpy.uint8)) elif tile.masking_mode == "3steps": # why trying something so complicated... transin = blur_width[0] midzone = blur_width[1] transout = blur_width[2] shore_level = 255 sea_level = int(tile.ratio_water * 255) b_img_array = b_mask_array = numpy.array(img_array) # First the transition at the shore # We go from shore_level to sea_level in transin meters stepsin = int(transin / 3) for i in range(stepsin): value = shore_level + transition_profile( (i + 1) / stepsin, 'parabolic') * (sea_level - shore_level) b_mask_array=(numpy.array(Image.fromarray(b_mask_array).convert("L").\ filter(ImageFilter.GaussianBlur(1)),dtype=numpy.uint8)>0).astype(numpy.uint8)*255 b_img_array[(b_img_array == 0) * (b_mask_array != 0)] = value UI.vprint(2, value) # Next the intermediate zone at constant transparency sea_b_radius = midzone / 3 sea_b_radius_buffered = (midzone + transout) / 2 b_mask_array=(numpy.array(Image.fromarray(b_mask_array).convert("L").\ filter(ImageFilter.GaussianBlur(sea_b_radius_buffered)),dtype=numpy.uint8)>0).astype(numpy.uint8)*255 b_mask_array=(numpy.array(Image.fromarray(b_mask_array).convert("L").\ filter(ImageFilter.GaussianBlur(sea_b_radius_buffered-sea_b_radius)),dtype=numpy.uint8)==255).astype(numpy.uint8)*255 b_img_array[(b_img_array == 0) * (b_mask_array != 0)] = sea_level # Finally the transition to the X-Plane sea # We go from sea_level to 0 in transout meters stepsout = int(transout / 3) for i in range(stepsout): value = sea_level * (1 - transition_profile( (i + 1) / stepsout, 'linear')) b_mask_array=(numpy.array(Image.fromarray(b_mask_array).convert("L").\ filter(ImageFilter.GaussianBlur(1)),dtype=numpy.uint8)>0).astype(numpy.uint8)*255 b_img_array[(b_img_array == 0) * (b_mask_array != 0)] = value UI.vprint(2, value) # To smoothen the thresolding introduced above we do a global short extent gaussian blur b_img_array=numpy.array(Image.fromarray(b_img_array).convert("L").\ filter(ImageFilter.GaussianBlur(2)),dtype=numpy.uint8) else: # Just a (futile) copy b_img_array = numpy.array(img_array) # Ensure land is kept to 255 on the mask to avoid unecessary ones, crop to final size, and take the # max with the possible custom extent mask img_array = numpy.maximum(img_array, b_img_array)[1024:4096 + 1024, 1024:4096 + 1024] img_array = numpy.maximum(img_array, custom_mask_array) if not (img_array.max() == 0 or img_array.min() == 255): masks_im = Image.fromarray( img_array) #.filter(ImageFilter.GaussianBlur(3)) masks_im.save( os.path.join(FNAMES.mask_dir(tile.lat, tile.lon), FNAMES.legacy_mask(til_x, til_y))) UI.vprint(2, " Done.") else: UI.vprint(1, " Ends-up being discarded.") UI.progress_bar(1, 100) UI.timings_and_bottom_line(timer) UI.logprint("Step 2.5 for tile lat=", tile.lat, ", lon=", tile.lon, ": normal exit.") return
def build_mask(til_x, til_y): if til_x < til_x_min or til_x > til_x_max or til_y < til_y_min or til_y > til_y_max: return 1 (latm0, lonm0) = GEO.gtile_to_wgs84(til_x, til_y, tile.mask_zl) (px0, py0) = GEO.wgs84_to_pix(latm0, lonm0, tile.mask_zl) px0 -= 1024 py0 -= 1024 # 1) We start with a black mask mask_im = Image.new("L", (4096 + 2 * 1024, 4096 + 2 * 1024), 'black') mask_draw = ImageDraw.Draw(mask_im) # 2) We fill it with white over the extent of each tile around for which we had a mesh available for mesh_file_name in mesh_file_name_list: latlonstr = mesh_file_name.split('.mes')[-2][-7:] lathere = int(latlonstr[0:3]) lonhere = int(latlonstr[3:7]) (px1, py1) = GEO.wgs84_to_pix(lathere, lonhere, tile.mask_zl) (px2, py2) = GEO.wgs84_to_pix(lathere, lonhere + 1, tile.mask_zl) (px3, py3) = GEO.wgs84_to_pix(lathere + 1, lonhere + 1, tile.mask_zl) (px4, py4) = GEO.wgs84_to_pix(lathere + 1, lonhere, tile.mask_zl) px1 -= px0 px2 -= px0 px3 -= px0 px4 -= px0 py1 -= py0 py2 -= py0 py3 -= py0 py4 -= py0 mask_draw.polygon([(px1, py1), (px2, py2), (px3, py3), (px4, py4)], fill='white') # 3a) We overwrite the white part of the mask with grey (ratio_water dependent) where inland water was detected in the first part above if (til_x, til_y) in dico_masks_inland: for (lat1, lon1, lat2, lon2, lat3, lon3) in dico_masks_inland[(til_x, til_y)]: (px1, py1) = GEO.wgs84_to_pix(lat1, lon1, tile.mask_zl) (px2, py2) = GEO.wgs84_to_pix(lat2, lon2, tile.mask_zl) (px3, py3) = GEO.wgs84_to_pix(lat3, lon3, tile.mask_zl) px1 -= px0 px2 -= px0 px3 -= px0 py1 -= py0 py2 -= py0 py3 -= py0 mask_draw.polygon( [(px1, py1), (px2, py2), (px3, py3)], fill=sea_level) # int(255*(1-tile.ratio_water))) # 3b) We overwrite the white + grey part of the mask with black where sea water was detected in the first part above for (lat1, lon1, lat2, lon2, lat3, lon3) in dico_masks[(til_x, til_y)]: (px1, py1) = GEO.wgs84_to_pix(lat1, lon1, tile.mask_zl) (px2, py2) = GEO.wgs84_to_pix(lat2, lon2, tile.mask_zl) (px3, py3) = GEO.wgs84_to_pix(lat3, lon3, tile.mask_zl) px1 -= px0 px2 -= px0 px3 -= px0 py1 -= py0 py2 -= py0 py3 -= py0 mask_draw.polygon([(px1, py1), (px2, py2), (px3, py3)], fill='black') del (mask_draw) # mask_im=mask_im.convert("L") img_array = numpy.array(mask_im, dtype=numpy.uint8) if tile.masks_use_DEM_too: # computing the part of the mask coming from the DEM: (latmax, lonmin) = GEO.pix_to_wgs84(px0, py0, tile.mask_zl) (latmin, lonmax) = GEO.pix_to_wgs84(px0 + 6144, py0 + 6144, tile.mask_zl) (x03857, y03857) = GEO.transform('4326', '3857', lonmin, latmax) (x13857, y13857) = GEO.transform('4326', '3857', lonmax, latmin) ((lonmin, lonmax, latmin, latmax), demarr4326) = tile.dem.super_level_set( mask_altitude_above, (lonmin, lonmax, latmin, latmax)) if demarr4326.any(): demim4326 = Image.fromarray( demarr4326.astype(numpy.uint8) * 255) del (demarr4326) s_bbox = (lonmin, latmax, lonmax, latmin) t_bbox = (x03857, y03857, x13857, y13857) demim3857 = IMG.gdalwarp_alternative(s_bbox, '4326', demim4326, t_bbox, '3857', (6144, 6144)) demim3857 = demim3857.filter( ImageFilter.GaussianBlur( 0.3 * 2**(tile.mask_zl - 14))) # slight increase of area dem_array = (numpy.array(demim3857, dtype=numpy.uint8) > 0).astype(numpy.uint8) * 255 del (demim3857) del (demim4326) img_array = numpy.maximum(img_array, dem_array) custom_mask_array = numpy.zeros((4096, 4096), dtype=numpy.uint8) if tile.masks_custom_extent: (latm1, lonm1) = GEO.gtile_to_wgs84(til_x + 16, til_y + 16, tile.mask_zl) bbox_4326 = (lonm0, latm0, lonm1, latm1) masks_im = IMG.has_data(bbox_4326, tile.masks_custom_extent, True, mask_size=(4096, 4096), is_sharp_resize=False, is_mask_layer=False) if masks_im: custom_mask_array = (numpy.array(masks_im, dtype=numpy.uint8) * (sea_level / 255)).astype(numpy.uint8) if (img_array.max() == 0) and ( custom_mask_array.max() == 0 ): # no need to test if the mask is all white since it would otherwise not be present in dico_mask UI.vprint(1, " Skipping", FNAMES.legacy_mask(til_x, til_y)) return 1 else: UI.vprint(1, " Creating", FNAMES.legacy_mask(til_x, til_y)) # Blur of the mask pxscal = GEO.webmercator_pixel_size(tile.lat + 0.5, tile.mask_zl) if tile.masking_mode == "sand": blur_width = int(tile.masks_width / pxscal) elif tile.masking_mode == "rocks": blur_width = tile.masks_width / (2 * pxscal) elif tile.masking_mode == "3steps": blur_width = [L / pxscal for L in tile.masks_width] if tile.masking_mode == "sand" and blur_width: # convolution with a hat function b_img_array = numpy.array(img_array) kernel = numpy.array(range(1, 2 * blur_width)) kernel[blur_width:] = range(blur_width - 1, 0, -1) kernel = kernel / blur_width**2 for i in range(0, len(b_img_array)): b_img_array[i] = numpy.convolve(b_img_array[i], kernel, 'same') b_img_array = b_img_array.transpose() for i in range(0, len(b_img_array)): b_img_array[i] = numpy.convolve(b_img_array[i], kernel, 'same') b_img_array = b_img_array.transpose() b_img_array = 2 * numpy.minimum(b_img_array, 127) b_img_array = numpy.array(b_img_array, dtype=numpy.uint8) elif tile.masking_mode == "rocks" and blur_width: # slight increase of the mask, then gaussian blur, nonlinear map and a tiny bit of smoothing again on a short scale along the shore b_img_array = (numpy.array(Image.fromarray(img_array).convert("L").\ filter(ImageFilter.GaussianBlur(blur_width / 1.7)), dtype=numpy.uint8) > 0).astype(numpy.uint8) * 255 # blur it b_img_array = numpy.array(Image.fromarray(b_img_array).convert("L").\ filter(ImageFilter.GaussianBlur(blur_width)), dtype=numpy.uint8) # nonlinear transform to make the transition quicker at the shore (gaussian is too flat) gamma = 2.5 b_img_array = (((numpy.tan((b_img_array.astype(numpy.float32) - 127.5) / 128 * atan(3)) - numpy.tan(-127.5 / 128 * atan(3)))\ *254 / (2 * numpy.tan(127.5 / 128 * atan(3)))) ** gamma / (255 ** (gamma - 1))).astype(numpy.uint8) # b_img_array=(1.4*(255-((256-b_img_array.astype(numpy.float32))/256.0)**0.2*255)).astype(numpy.uint8) # b_img_array=numpy.minimum(b_img_array,200) # still some slight smoothing at the shore b_img_array = numpy.maximum(b_img_array, numpy.array(Image.fromarray(img_array).convert("L").\ filter(ImageFilter.GaussianBlur(2 ** (tile.mask_zl - 14))), dtype=numpy.uint8)) elif tile.masking_mode == "3steps": # why trying something so complicated... transin = blur_width[0] midzone = blur_width[1] transout = blur_width[2] # print(transin,midzone,transout) shore_level = 255 b_img_array = b_mask_array = numpy.array(img_array) # First the transition at the shore # We go from shore_level to sea_level in transin meters stepsin = int(transin / 3) for i in range(stepsin): value = shore_level + transition_profile( (i + 1) / stepsin, 'parabolic') * (sea_level - shore_level) b_mask_array = (numpy.array(Image.fromarray(b_mask_array).convert("L").\ filter(ImageFilter.GaussianBlur(1)), dtype=numpy.uint8) > 0).astype(numpy.uint8) * 255 b_img_array[(b_img_array == 0) * (b_mask_array != 0)] = value UI.vprint(2, value) # Next the intermediate zone at constant transparency sea_b_radius = midzone / 3 sea_b_radius_buffered = (midzone + transout) / 3 b_mask_array = (numpy.array(Image.fromarray(b_mask_array).convert("L").\ filter(ImageFilter.GaussianBlur(sea_b_radius_buffered)), dtype=numpy.uint8) > 0).astype(numpy.uint8) * 255 b_mask_array = (numpy.array(Image.fromarray(b_mask_array).convert("L").\ filter(ImageFilter.GaussianBlur(sea_b_radius_buffered - sea_b_radius)), dtype=numpy.uint8) == 255).astype(numpy.uint8) * 255 b_img_array[(b_img_array == 0) * (b_mask_array != 0)] = sea_level # Finally the transition to the X-Plane sea # We go from sea_level to 0 in transout meters stepsout = int(transout / 3) for i in range(stepsout): value = sea_level * (1 - transition_profile( (i + 1) / stepsout, 'linear')) b_mask_array = (numpy.array(Image.fromarray(b_mask_array).convert("L").\ filter(ImageFilter.GaussianBlur(1)), dtype=numpy.uint8) > 0).astype(numpy.uint8) * 255 b_img_array[(b_img_array == 0) * (b_mask_array != 0)] = value UI.vprint(2, value) # To smoothen the thresolding introduced above we do a global short extent gaussian blur b_img_array = numpy.array(Image.fromarray(b_img_array).convert("L").\ filter(ImageFilter.GaussianBlur(2)), dtype=numpy.uint8) else: # Just a (futile) copy b_img_array = numpy.array(img_array) # Ensure land is kept to 255 on the mask to avoid unecessary ones, crop to final size, and take the # max with the possible custom extent mask img_array = numpy.maximum((img_array > 0).astype(numpy.uint8) * 255, b_img_array)[1024:4096 + 1024, 1024:4096 + 1024] img_array = numpy.maximum(img_array, custom_mask_array) if not (img_array.max() == 0 or img_array.min() == 255): masks_im = Image.fromarray( img_array) # .filter(ImageFilter.GaussianBlur(3)) masks_im.save( os.path.join(dest_dir, FNAMES.legacy_mask(til_x, til_y))) UI.vprint(2, " Done.") else: UI.vprint(1, " Ends-up being discarded.") return 1