Пример #1
0
def main(args):
    '''
    Core functionality. Called by qc_wrap.py and __file__
    '''
    try:
        pargs = parser.parse_args(args[1:])
    except Exception as error_msg:
        print(str(error_msg))
        return 1

    kmname = constants.get_tilename(pargs.las_file)
    print('Running %s on block: %s, %s' % (PROGNAME, kmname, time.asctime()))
    if not os.path.exists(pargs.outdir):
        os.mkdir(pargs.outdir)

    path = pargs.las_file
    filename = os.path.basename(path)
    out_path = os.path.join(pargs.outdir, filename)

    las_in = laspy.read(path)
    las_out = laspy.LasData(las_in.header)

    points = las_in.points
    las_out.points = points

    xy = np.column_stack((las_in.x, las_in.y))
    geoid = grid.fromGDAL(GEOID_GRID, upcast=True)
    geoid_offset = geoid.interpolate(xy)

    # Apply vertical offset from geoid grid
    las_out.z -= geoid_offset

    las_out.write(out_path)

    return 0
Пример #2
0
def main(args):
    '''
    Main function that can be called from either the command line or via qc_wrap.py
    '''
    try:
        pargs = parser.parse_args(args[1:])
    except Exception as error_msg:
        print(str(error_msg))
        return 1

    kmname = constants.get_tilename(pargs.las_file)
    print("Running %s on block: %s, %s" % (PROGNAME, kmname, time.asctime()))

    if pargs.below_poly:
        below_poly = True
        ptype = "below_poly"
    else:
        below_poly = False
    if pargs.type is not None:
        ptype = pargs.type
    else:
        ptype = "undefined"
    if below_poly:
        print("Only using points which lie below polygon mean z!")
    pc = pointcloud.fromAny(pargs.las_file)
    print("Classes in pointcloud: %s" % pc.get_classes())

    try:
        extent = np.asarray(constants.tilename_to_extent(kmname))
    except Exception:
        print("Could not get extent from tilename.")
        extent = None

    polygons = vector_io.get_geometries(pargs.ref_data, pargs.layername,
                                        pargs.layersql, extent)
    feature_count = 0
    use_local = pargs.use_local

    if pargs.schema is not None:
        report.set_schema(pargs.schema)

    reporter = report.ReportClassCheck(use_local)
    for polygon in polygons:
        if below_poly:
            if polygon.GetCoordinateDimension() < 3:
                print(
                    "Error: polygon not 3D - below_poly does not make sense!")
                continue
            a_polygon3d = array_geometry.ogrpoly2array(polygon,
                                                       flatten=False)[0]
            #warping loop here....
            if pargs.toE:
                geoid = grid.fromGDAL(GEOID_GRID, upcast=True)
                print("Using geoid from %s to warp to ellipsoidal heights." %
                      GEOID_GRID)
                toE = geoid.interpolate(a_polygon3d[:, :2].copy())
                mask = toE == geoid.nd_val
                if mask.any():
                    raise Warning(
                        "Warping to ellipsoidal heights produced no-data values!"
                    )
                a_polygon3d[:, 2] += toE
            mean_z = a_polygon3d[:, 2].mean()
            if mean_z < SENSIBLE_Z_MIN or mean_z > SENSIBLE_Z_MAX:
                msg = "Warning: This feature seems to have unrealistic mean z value: {0:.2f} m"
                print(msg.format(mean_z))
                continue
            del a_polygon3d
        else:
            mean_z = -1
        polygon.FlattenTo2D()
        feature_count += 1
        separator = "-" * 70
        print("%s\nFeature %d\n%s" % (separator, feature_count, separator))
        a_polygon = array_geometry.ogrpoly2array(polygon)
        pc_in_poly = pc.cut_to_polygon(a_polygon)

        if below_poly:
            pc_in_poly = pc_in_poly.cut_to_z_interval(-999, mean_z)
        n_all = pc_in_poly.get_size()
        freqs = [0] * (len(constants.classes) + 1)  #list of frequencies...

        if n_all > 0:
            c_all = pc_in_poly.get_classes()
            if below_poly and DEBUG:
                print("Mean z of polygon is:        %.2f m" % mean_z)
                print("Mean z of points below is:   %.2f m" %
                      pc_in_poly.z.mean())
            print("Number of points in polygon:  %d" % n_all)
            print("Classes in polygon:           %s" % str(c_all))
            # important for reporting that the order here is the same as in the table
            # definition in report.py!!
            n_found = 0
            for i, cls in enumerate(constants.classes):
                if cls in c_all:
                    pcc = pc_in_poly.cut_to_class(cls)
                    n_c = pcc.get_size()
                    f_c = n_c / float(n_all)
                    n_found += n_c
                    print("Class %d::" % cls)
                    print("   #Points:  %d" % n_c)
                    print("   Fraction: %.3f" % f_c)
                    freqs[i] = f_c
            f_other = (n_all - n_found) / float(n_all)
            freqs[-1] = f_other
        send_args = [kmname] + freqs + [n_all, ptype]
        reporter.report(*send_args, ogr_geom=polygon)
Пример #3
0
def main(args):
    '''
    Main processing function
    '''

    pargs = parser.parse_args(args[1:])
    lasname = pargs.las_file
    kmname = constants.get_tilename(lasname)
    layer_def = pargs.layer_def
    fargs = dict.fromkeys(NAMES, None)
    if pargs.layer_def is not None:
        if layer_def.endswith(".json"):
            with open(layer_def) as layer_def_file:
                jargs = json.load(layer_def_file)
        else:
            jargs = json.loads(layer_def)
        fargs.update(jargs)

    for name in NAMES:
        res_type = NAMES[name]
        if fargs[name] is not None:
            try:
                fargs[name] = res_type(fargs[name])
            except TypeError as error_msg:
                print(str(error_msg))
                print(name + " must be convertable to %s" % repr(res_type))

    try:
        extent = np.asarray(constants.tilename_to_extent(kmname))
    except (ValueError, AttributeError) as error_msg:
        print("Exception: %s" % str(error_msg))
        print("Bad 1km formatting of las file: %s" % lasname)
        return 1

    extent_buf = extent + (-BUFBUF, -BUFBUF, BUFBUF, BUFBUF)
    cell_buf_extent = np.array([-CELL_BUF, -CELL_BUF, CELL_BUF, CELL_BUF], dtype=np.float64)
    grid_buf = (extent + cell_buf_extent * pargs.cell_size)
    buf_georef = [grid_buf[0], pargs.cell_size, 0, grid_buf[3], 0, -pargs.cell_size]

    #move these to a method in e.g. grid.py
    ncols = int(ceil((grid_buf[2] - grid_buf[0]) / pargs.cell_size))
    nrows = int(ceil((grid_buf[3] - grid_buf[1]) / pargs.cell_size))
    assert (extent_buf[:2] < grid_buf[:2]).all()
    assert modf((extent[2] - extent[0]) / pargs.cell_size)[0] == 0.0

    if not os.path.exists(pargs.output_dir):
        os.mkdir(pargs.output_dir)

    terrainname = os.path.join(pargs.output_dir, "dtm_" + kmname + ".tif")
    surfacename = os.path.join(pargs.output_dir, "dsm_" + kmname + ".tif")
    terrain_exists = os.path.exists(terrainname)
    surface_exists = os.path.exists(surfacename)
    if pargs.dsm:
        do_dsm = pargs.overwrite or (not surface_exists)
    else:
        do_dsm = False
    if do_dsm:
        do_dtm = True
    else:
        do_dtm = pargs.dtm and (pargs.overwrite or (not terrain_exists))
    if not (do_dtm or do_dsm):
        print("dtm already exists: %s" % terrain_exists)
        print("dsm already exists: %s" % surface_exists)
        print("Nothing to do - exiting...")
        return 2
    #### warn on smoothing #####
    if pargs.smooth_rad > CELL_BUF:
        print("Warning: smoothing radius is larger than grid buffer")

    tiles = get_neighbours(pargs.tile_cstr, kmname, pargs.rowcol_sql, pargs.tile_sql, pargs.remove_bridges_in_dtm)
    bufpc = None
    geoid = grid.fromGDAL(GEOID_GRID, upcast=True)
    for path, ground_cls, surf_cls, h_system in tiles:
        if os.path.exists(path):
            #check sanity
            assert set(ground_cls).issubset(set(surf_cls))
            assert h_system in ["dvr90", "E"]

            tile_pc = pointcloud.fromAny(path, include_return_number=True)
            tile_pc = tile_pc.cut_to_box(*extent_buf)
            tile_pc = tile_pc.cut_to_class(surf_cls)

            if tile_pc.get_size() > 0:
                mask = np.zeros((tile_pc.get_size(),), dtype=np.bool)
                #reclass hack
                for cls in ground_cls:
                    mask |= (tile_pc.c == cls)
                tile_pc.c[mask] = SYNTH_TERRAIN

                #warping to hsys
                if h_system != pargs.hsys and not pargs.nowarp:
                    if pargs.hsys == "E":
                        tile_pc.toE(geoid)
                    else:
                        tile_pc.toH(geoid)

                if bufpc is None:
                    bufpc = tile_pc
                else:
                    bufpc.extend(tile_pc)
            del tile_pc
        else:
            print("Neighbour " + path + " does not exist.")
    if bufpc is None:
        return 3

    if bufpc.get_size() <= 3:
        return 3

    rc1 = 0
    rc2 = 0
    dtm = None
    dsm = None
    triangle_mask = np.ndarray(0)

    water_mask, lake_raster, sea_mask, build_mask = setup_masks(fargs, nrows, ncols, buf_georef)

    # Remove terrain points in buildings
    if pargs.clean_buildings and (build_mask is not None) and build_mask.any():
        bmask_shrink = image.morphology.binary_erosion(build_mask)
        mask = bufpc.get_grid_mask(bmask_shrink, buf_georef)
        #validate thoroughly
        testpc1 = bufpc.cut(mask) # only building points(?)
        testpc2 = None
        try:
            testpc1.sort_spatially(2)
            mask &= (bufpc.c == SYNTH_TERRAIN)
            testpc2 = bufpc.cut(mask) # building points and terrain(?)
            in_building = ((testpc1.max_filter(2, xy=testpc2.xy, nd_val=ND_VAL) - testpc2.z) > 1)
            cut_buildings = np.zeros_like(mask, dtype=np.bool)
            if in_building.any():
                cut_buildings[mask] = in_building
            #so see if these are really, really inside buildings
            bufpc = bufpc.cut(np.logical_not(cut_buildings))
        except:
            pass
        finally:
            if testpc1 is not None:
                del testpc1
            if testpc2 is not None:
                del testpc2

    if do_dtm:
        terr_pc = bufpc.cut_to_class(SYNTH_TERRAIN)
        if terr_pc.get_size() > 3:
            dtm, trig_grid = gridit(terr_pc, grid_buf, pargs.cell_size, None, doround=pargs.round)
        else:
            rc1 = 3

        if dtm and not rc1:
            assert dtm.grid.shape == (nrows, ncols) #else something is horribly wrong...
            # Create a mask with triangles larger than triangle_limit.
            # Small triangles will be ignored and possibly filled out later.
            triangle_mask = trig_grid.grid > pargs.triangle_limit
        else:
            rc1 = 3

        if triangle_mask.any() and water_mask.any() and not rc1:
            if not pargs.no_expand_water:
                water_mask = expand_water(triangle_mask, water_mask)

            if build_mask is not None:
                water_mask &= np.logical_not(build_mask) #xor

            # Filling in large triangles
            mask = np.logical_and(triangle_mask, water_mask)
            zlow = array_geometry.tri_filter_low(
                terr_pc.z,
                terr_pc.triangulation.vertices,
                terr_pc.triangulation.ntrig,
                pargs.zlim)

            if pargs.debug:
                debug_difference = terr_pc.z - zlow
                print(debug_difference.mean(), (debug_difference != 0).sum())

            terr_pc.z = zlow
            dtm_low, trig_grid = gridit(terr_pc, grid_buf, pargs.cell_size, None, doround=pargs.round)
            dtm.grid[mask] = dtm_low.grid[mask]
            del dtm_low

            # Smooth water
            if pargs.flatten:
                flat_grid = array_geometry.masked_mean_filter(dtm.grid, mask, 4)
                dtm.grid[triangle_mask] = flat_grid[triangle_mask]

        #FIX THIS PART
        if pargs.smooth_rad > 0 and build_mask is not None and triangle_mask.any():
            # Smoothing below houses (probably)...
            mask = np.logical_and(triangle_mask, build_mask)
            dilated_mask = image.morphology.binary_dilation(mask)
            dilated_mask &= np.logical_not(water_mask)
            flat_grid = array_geometry.masked_mean_filter(dtm.grid, dilated_mask, pargs.smooth_rad)
            mask &= np.logical_not(water_mask)
            dtm.grid[mask] = flat_grid[mask]

            del flat_grid
            del trig_grid
            del dilated_mask
            del mask

        if pargs.burn_sea and (sea_mask is not None) and not rc1:
            dtm = burn_sea(dtm, sea_mask, triangle_mask, pargs.sea_z, pargs.sea_tolerance)

        # Burn lakes
        if lake_raster is not None and not rc1:
            burn_lakes(dtm, lake_raster, triangle_mask, pargs.lake_tolerance_dtm)

        if pargs.dtm and (pargs.overwrite or (not terrain_exists)):
            dtm.shrink(CELL_BUF).save(terrainname, dco=TIF_CREATION_OPTIONS, srs=SRS_WKT)

        del triangle_mask
        del terr_pc

    if do_dsm:
        surf_pc = bufpc.cut_to_return_number(1)
        del bufpc

        if surf_pc.get_size() > 3:
            dsm, trig_grid = gridit(surf_pc, grid_buf, pargs.cell_size, None, doround=pargs.round)
        else:
            rc2 = 3

        if dsm and not rc2:
            triangle_mask = trig_grid.grid > pargs.triangle_limit
        else:
            rc2 = 3

        #now we are in a position to handle water...
        if dtm and water_mask.any() and triangle_mask.any() and not rc2:
            # Fill large triangles
            mask = np.logical_and(triangle_mask, water_mask)
            dsm.grid[mask] = dtm.grid[mask]

            if pargs.debug:
                print(dsm.grid.shape)
                t_name = os.path.join(
                    pargs.output_dir, "triangles_" + kmname + ".tif")
                trig_grid.shrink(CELL_BUF).save(
                    t_name,
                    dco=["TILED=YES", "COMPRESS=LZW"])
                w_name = os.path.join(
                    pargs.output_dir, "water_" + kmname + ".tif")
                water_grid = grid.Grid(water_mask, dsm.geo_ref, 0)
                water_grid.shrink(CELL_BUF).save(w_name, dco=["TILED=YES", "COMPRESS=LZW"])

            if pargs.burn_sea and (sea_mask is not None):
                dsm = burn_sea(dsm, sea_mask, triangle_mask, pargs.sea_z, pargs.sea_tolerance)

            # Burn lakes
            if lake_raster is not None:
                burn_lakes(dsm, lake_raster, triangle_mask, pargs.lake_tolerance_dsm)

            del triangle_mask
        dsm.shrink(CELL_BUF).save(surfacename, dco=TIF_CREATION_OPTIONS, srs=SRS_WKT)

        del surf_pc

    return max(rc1, rc2)
Пример #4
0
def main(args):
    try:
        pargs = parser.parse_args(args[1:])
    except Exception as e:
        print(str(e))
        return 1
    kmname = constants.get_tilename(pargs.las_file)
    print("Running %s on block: %s, %s" % (progname, kmname, time.asctime()))
    lasname = pargs.las_file
    pointname = pargs.ref_data
    use_local = pargs.use_local
    if pargs.schema is not None:
        report.set_schema(pargs.schema)
    reporter = report.ReportZcheckAbs(use_local)

    try:
        extent = np.asarray(constants.tilename_to_extent(kmname))
    except Exception as e:
        print("Could not get extent from tilename.")
        extent = None
    pc_ref = None  #base reference pointcloud
    pc_refs = []  #list of possibly 'cropped' pointclouds...
    if pargs.multipoints:
        ftype = "multipoints"
        explode = False
    elif pargs.lines:
        ftype = "lines"
        explode = True
    geoms = vector_io.get_geometries(pointname,
                                     pargs.layername,
                                     pargs.layersql,
                                     extent,
                                     explode=explode)
    for geom in geoms:
        xyz = array_geometry.ogrgeom2array(geom, flatten=False)
        if xyz.shape[0] > 0:
            pc_refs.append(pointcloud.Pointcloud(xyz[:, :2], xyz[:, 2]))
    print("Found %d non-empty geometries" % len(pc_refs))
    if len(pc_refs) == 0:
        print("No input geometries in intersection...")
    if pargs.ftype is not None:
        ftype = pargs.ftype
    cut_input_to = pargs.cut_to
    print("Cutting input pointcloud to class %d" % cut_input_to)
    pc = pointcloud.fromAny(lasname).cut_to_class(
        cut_input_to)  #what to cut to here...??
    #warping loop here....
    if (pargs.toE):
        geoid = grid.fromGDAL(GEOID_GRID, upcast=True)
        print("Using geoid from %s to warp to ellipsoidal heights." %
              GEOID_GRID)
        for i in range(len(pc_refs)):
            toE = geoid.interpolate(pc_refs[i].xy)
            M = (toE == geoid.nd_val)
            if (M.any()):
                print(
                    "Warping to ellipsoidal heights produced no-data values!")
                M = np.logical_not(M)
                toE = toE[M]
                pc_refs[i] = pc_refs[i].cut(M)
            pc_refs[i].z += toE
    #Remove empty pointsets
    not_empty = []
    for pc_r in pc_refs:
        if pc_r.get_size() > 0:
            not_empty.append(pc_r)  #dont worry, just a pointer...
        else:
            raise Warning("Empty input set...")
    print("Checking %d point sets" % len(not_empty))
    #Loop over strips#

    for id in pc.get_pids():
        print("%s\n" % ("+" * 70))
        print("Strip id: %d" % id)
        pc_c = pc.cut_to_strip(id)
        if pc_c.get_size() < 50:
            print("Not enough points...")
            continue
        might_intersect = []
        for i, pc_r in enumerate(not_empty):
            if pc_c.might_overlap(pc_r):
                might_intersect.append(i)
        if len(might_intersect) == 0:
            print("Strip does not intersect any point 'patch'...")
            continue
        pc_c.triangulate()
        pc_c.calculate_validity_mask(angle_tolerance, xy_tolerance,
                                     z_tolerance)
        #now loop over the patches which might intersect this strip...
        any_checked = False
        for i in might_intersect:
            pc_r = not_empty[i].cut_to_box(*(pc_c.get_bounds()))
            if pc_r.get_size == 0:
                continue
            any_checked = True
            print("Stats for check of 'patch/set' %d against strip %d:" %
                  (i, id))
            stats = check_points(pc_c, pc_r)
            if stats is None:
                print("Not enough points in proper triangles...")
                continue
            m, sd, n = stats
            cm_x, cm_y = pc_r.xy.mean(axis=0)
            cm_z = pc_r.z.mean()
            print("Center of mass: %.2f %.2f %.2f" % (cm_x, cm_y, cm_z))
            cm_geom = ogr.Geometry(ogr.wkbPoint25D)
            cm_geom.SetPoint(0, cm_x, cm_y, cm_z)
            #what geometry should be reported, bounding box??
            reporter.report(kmname, id, ftype, m, sd, n, ogr_geom=cm_geom)
        if not any_checked:
            print("Strip did not intersect any point 'patch'...")