示例#1
0
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
示例#2
0
    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