def main(args): ''' Main function, invoked from either command line or qc_wrap ''' pargs = parser.parse_args(args[1:]) lasname = pargs.las_file kmname = constants.get_tilename(lasname) msg = "Running %s on block: %s, %s" print(msg % (os.path.basename(args[0]), kmname, time.asctime())) if pargs.schema is not None: report.set_schema(pargs.schema) reporter = report.ReportSpikes(pargs.use_local) if pargs.zlim < 0: print("zlim must be positive!") usage() if pargs.slope < 0 or pargs.slope >= 90: print("Specify a slope angle in the range 0->90 degrees.") usage() cut_class = pargs.cut_class print("Cutting to class (terrain) {0:d}".format(cut_class)) pc = pointcloud.fromAny(lasname).cut_to_class(cut_class) if pc.get_size() < 10: print("Too few points in pointcloud.") return print("Sorting spatially...") pc.sort_spatially(FILTER_RAD) slope_arg = np.tan(np.radians(pargs.slope))**2 msg = "Using steepnes parameters: angle: {0:.2f} degrees, delta-z: {1:.2f}" print(msg.format(pargs.slope, pargs.zlim)) print("Filtering, radius: {0:.2f}".format(FILTER_RAD)) dz = pc.spike_filter(FILTER_RAD, slope_arg, pargs.zlim) mask = (dz != 0) dz = dz[mask] pc = pc.cut(mask) print("Spikes: {0:d}".format(mask.sum())) for i in range(pc.size): x, y = pc.xy[i] z = pc.z[i] mdz = dz[i] c = pc.c[i] pid = pc.pid[i] print("spike: x: {0:.2f} y: {1:.2f} mean-dz: {2:.2f}".format( x, y, mdz)) wkt_geom = "POINT({0:.2f} {1:.2f})".format(x, y) reporter.report(kmname, FILTER_RAD, mdz, x, y, z, c, pid, wkt_geom=wkt_geom)
def main(args): pargs=parser.parse_args(args[1:]) lasname=pargs.las_file roadname=pargs.road_data cut=pargs.cut_to if pargs.schema is not None: report.set_schema(pargs.schema) reporter=report.ReportZcheckRoad(pargs.use_local) done=zcheck_base.zcheck_base(lasname,roadname,angle_tolerance,xy_tolerance,z_tolerance,cut,reporter,buffer_dist=buffer_dist,layername=pargs.layername,layersql=pargs.layersql)
def main(args): try: pargs = parser.parse_args(args[1:]) except Exception as e: print(str(e)) return 1 lasname = pargs.las_file kmname = constants.get_tilename(lasname) print("Running %s on block: %s, %s" % (progname, kmname, time.asctime())) if pargs.schema is not None: report.set_schema(pargs.schema) reporter = report.ReportWobbly(pargs.use_local) pc = pointcloud.fromAny(lasname).cut_to_class(pargs.cut_to) print("%d points of class %d in this tile..." % (pc.get_size(), pargs.cut_to)) if pc.get_size() < 3: print("Few points of class %d in this tile..." % pargs.cut_to) return 0 print("Using z-limit %.2f m" % pargs.zmin) pc.sort_spatially(pargs.frad) meanz = pc.mean_filter(pargs.frad) diff = pc.z - meanz M = (np.fabs(diff) > pargs.zmin) n = M.sum() print("Found %d wobbly points" % n) if n > 0: pc = pc.cut(M) diff = diff[M] ds, lyr = polygonise_points(pc, 2 * pargs.frad, 1) nf = lyr.GetFeatureCount() for i in range(nf): fet = lyr.GetNextFeature() geom = fet.GetGeometryRef() arr_geom = array_geometry.ogrpoly2array(geom, flatten=True) N = array_geometry.points_in_polygon(pc.xy, arr_geom) n = N.sum() if n > 0: d = diff[N] m1 = d.min() m2 = d.max() else: m1 = -999 m2 = -999 reporter.report(kmname, pargs.cut_to, pargs.frad, n, m1, m2, ogr_geom=geom) return 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)
def main(args): ''' Core function. Called either stand-alone or from qc_wrap. ''' try: pargs = parser.parse_args(args[1:]) except Exception as error_str: print(str(error_str)) return 1 kmname = constants.get_tilename(pargs.las_file) print("Running %s on block: %s, %s" % (PROGNAME, kmname, time.asctime())) cell_size = pargs.cs ncols_f = TILE_SIZE / cell_size ncols = int(ncols_f) if ncols != ncols_f: print("TILE_SIZE: %d must be divisible by cell size..." % (TILE_SIZE)) usage() return 1 print("Using cell size: %.2f" % cell_size) use_local = pargs.use_local if pargs.schema is not None: report.set_schema(pargs.schema) reporter = report.ReportDensity(use_local) outdir = pargs.outdir if not os.path.exists(outdir): os.mkdir(outdir) lasname = pargs.las_file waterconnection = pargs.ref_data outname_base = "den_{0:.0f}_".format(cell_size) + os.path.splitext( os.path.basename(lasname))[0] + ".tif" outname = os.path.join(outdir, outname_base) print("Reading %s, writing %s" % (lasname, outname)) try: (x_min, y_min, x_max, y_max) = constants.tilename_to_extent(kmname) except Exception as error_str: print("Exception: %s" % str(error_str)) print("Bad 1km formatting of las file: %s" % lasname) return 1 las_file = laspy.file.File(lasname, mode='r') nx = int((x_max - x_min) / cell_size) ny = int((y_max - y_min) / cell_size) ds_grid = gdal.GetDriverByName('GTiff').Create(outname, nx, ny, 1, gdal.GDT_Float32) georef = (x_min, cell_size, 0, y_max, 0, -cell_size) ds_grid.SetGeoTransform(georef) band = ds_grid.GetRasterBand(1) band.SetNoDataValue(ND_VAL) # make local copies so we don't have to call the x and y getter functions # of las_file a nx*ny times xs = las_file.x ys = las_file.y # determine densities den_grid = np.ndarray(shape=(nx, ny), dtype=float) for i in range(nx): for j in range(ny): I = np.ones(las_file.header.count, dtype=bool) if i < nx - 1: I &= np.logical_and(xs >= x_min + i * cell_size, xs < x_min + (i + 1) * cell_size) else: I &= np.logical_and(xs >= x_min + i * cell_size, xs <= x_min + (i + 1) * cell_size) if j < ny - 1: I &= np.logical_and(ys >= y_min + j * cell_size, ys < y_min + (j + 1) * cell_size) else: I &= np.logical_and(ys >= y_min + j * cell_size, ys <= y_min + (j + 1) * cell_size) den_grid[ny - j - 1][i] = np.sum(I) / (cell_size * cell_size) band.WriteArray(den_grid) las_file.close() t1 = time.clock() if pargs.lakesql is None and pargs.seasql is None: print('No layer selection specified!') print( 'Assuming that all water polys are in first layer of connection...' ) lake_mask = vector_io.burn_vector_layer( waterconnection, georef, den_grid.shape, None, None, ) else: lake_mask = np.zeros(den_grid.shape, dtype=np.bool) if pargs.lakesql is not None: print("Burning lakes...") lake_mask |= vector_io.burn_vector_layer( waterconnection, georef, den_grid.shape, None, pargs.lakesql, ) if pargs.seasql is not None: print("Burning sea...") lake_mask |= vector_io.burn_vector_layer( waterconnection, georef, den_grid.shape, None, pargs.seasql, ) t2 = time.clock() print("Burning 'water' took: %.3f s" % (t2 - t1)) # what to do with nodata?? nd_mask = (den_grid == ND_VAL) den_grid[den_grid == ND_VAL] = 0 n_lake = lake_mask.sum() print("Number of no-data densities: %d" % (nd_mask.sum())) print("Number of water cells : %d" % (n_lake)) if n_lake < den_grid.size: not_lake = den_grid[np.logical_not(lake_mask)] den = not_lake.min() mean_den = not_lake.mean() else: den = ALL_LAKE mean_den = ALL_LAKE print("Minumum density : %.2f" % den) wkt = constants.tilename_to_extent(kmname, return_wkt=True) reporter.report(kmname, den, mean_den, cell_size, wkt_geom=wkt) return 0
def main(args): ''' Main script functionality. Can be invoked 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 = get_tilename(pargs.las_file) print("Running %s on block: %s, %s" % (PROGNAME, kmname, time.asctime())) if pargs.schema is not None: report.set_schema(pargs.schema) reporter = report.ReportClassCount(pargs.use_local) pc = pointcloud.fromAny(pargs.las_file) n_points_total = pc.get_size() if n_points_total == 0: print( "Something is terribly terribly wrong here! Simon - vi skal melde en fjel" ) pc_temp = pc.cut_to_class(constants.created_unused) n_created_unused = pc_temp.get_size() pc_temp = pc.cut_to_class(constants.surface) n_surface = pc_temp.get_size() pc_temp = pc.cut_to_class(constants.terrain) n_terrain = pc_temp.get_size() pc_temp = pc.cut_to_class(constants.low_veg) n_low_veg = pc_temp.get_size() pc_temp = pc.cut_to_class(constants.high_veg) n_high_veg = pc_temp.get_size() pc_temp = pc.cut_to_class(constants.med_veg) n_med_veg = pc_temp.get_size() pc_temp = pc.cut_to_class(constants.building) n_building = pc_temp.get_size() pc_temp = pc.cut_to_class(constants.outliers) n_outliers = pc_temp.get_size() pc_temp = pc.cut_to_class(constants.mod_key) n_mod_key = pc_temp.get_size() pc_temp = pc.cut_to_class(constants.water) n_water = pc_temp.get_size() pc_temp = pc.cut_to_class(constants.ignored) n_ignored = pc_temp.get_size() pc_temp = pc.cut_to_class(constants.bridge) n_bridge = pc_temp.get_size() # new classes pc_temp = pc.cut_to_class(constants.high_noise) n_high_noise = pc_temp.get_size() pc_temp = pc.cut_to_class(constants.power_line) n_power_line = pc_temp.get_size() pc_temp = pc.cut_to_class(constants.terrain_in_buildings) n_terrain_in_buildings = pc_temp.get_size() pc_temp = pc.cut_to_class(constants.low_veg_in_buildings) n_low_veg_in_buildings = pc_temp.get_size() pc_temp = pc.cut_to_class(constants.man_excl) n_man_excl = pc_temp.get_size() polywkt = tilename_to_extent(kmname, return_wkt=True) print(polywkt) reporter.report(kmname, n_created_unused, n_surface, n_terrain, n_low_veg, n_med_veg, n_high_veg, n_building, n_outliers, n_mod_key, n_water, n_ignored, n_power_line, n_bridge, n_high_noise, n_terrain_in_buildings, n_low_veg_in_buildings, n_man_excl, n_points_total, wkt_geom=polywkt)
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 polyname = pargs.poly_data use_local = pargs.use_local if pargs.schema is not None: report.set_schema(pargs.schema) reporter = report.ReportBuildingAbsposCheck(use_local) ################################## pc = pointcloud.fromAny(lasname).cut_to_class(cut_to_classes) try: extent = np.asarray(constants.tilename_to_extent(kmname)) except Exception as e: print("Could not get extent from tilename.") extent = None polys = vector_io.get_geometries(polyname, pargs.layername, pargs.layersql, extent) fn = 0 sl = "-" * 65 for poly in polys: n_corners_found = 0 fn += 1 print("%s\nChecking feature %d\n%s\n" % (sl, fn, sl)) a_poly = array_geometry.ogrgeom2array(poly) pcp = pc.cut_to_polygon(a_poly) if pcp.get_size() < 500: print("Few points in polygon...") continue a_poly = a_poly[0] all_post = np.zeros_like(a_poly) #array of vertices found all_pre = np.zeros_like( a_poly) #array of vertices in polygon, correpsonding to found... pcp.triangulate() geom = pcp.get_triangle_geometry() m = geom[:, 1].mean() sd = geom[:, 1].std() if (m > 1.5 or 0.5 * sd > m): print("Feature %d, bad geometry...." % fn) print("{} {}".format(m, sd)) continue #geom is ok - we proceed with a buffer around da house poly_buf = poly.Buffer(2.0) a_poly2 = array_geometry.ogrgeom2array(poly_buf) pcp = pc.cut_to_polygon(a_poly2) print("Points in buffer: %d" % pcp.get_size()) pcp.triangulate() geom = pcp.get_triangle_geometry() tanv2 = tan(radians(cut_angle))**2 #set a mask to mark the triangles we want to consider as marking the house boundary mask = np.logical_and(geom[:, 0] > tanv2, geom[:, 2] > z_limit) #we consider the 'high' lying vertices - could also just select the highest of the three vertices... p_mask = (pcp.z > pcp.z.min() + 2) #and only consider those points which lie close to the outer bd of the house... p_mask &= array_geometry.points_in_buffer( pcp.xy, a_poly, 1.2) #a larger shift than 1.2 ?? #this just selects vertices where p_mask is true, from triangles where mask is true - nothing else... bd_mask = pcp.get_boundary_vertices(mask, p_mask) bd_pts = pcp.xy[bd_mask] #subtract mean to get better numeric stability... xy_t = bd_pts.mean(axis=0) xy = bd_pts - xy_t a_poly -= xy_t if DEBUG: plot_points(a_poly, xy) #now find those corners! lines_ok = dict() found_lines = dict() for vertex in range(a_poly.shape[0] - 1): #check line emanating from vertex... p1 = a_poly[vertex] p2 = a_poly[vertex + 1] ok, pts = check_distribution(p1, p2, xy) lines_ok[vertex] = (ok, pts) #now find corners vertex = 0 #handle the 0'th corner specially... while vertex < a_poly.shape[0] - 2: if lines_ok[vertex][0] and lines_ok[vertex + 1][0]: #proceed print("%s\nCorner %d should be findable..." % ("+" * 50, vertex + 1)) corner_found = find_corner(vertex, lines_ok, found_lines, a_poly) all_pre[n_corners_found] = a_poly[vertex + 1] all_post[n_corners_found] = corner_found #print a_poly[vertex+1],corner_found,vertex n_corners_found += 1 vertex += 1 else: #skip to next findable corner vertex += 2 if lines_ok[0][0] and lines_ok[a_poly.shape[0] - 2][0]: print("Corner 0 should also be findable...") corner_found = find_corner(a_poly.shape[0] - 2, lines_ok, found_lines, a_poly) all_pre[n_corners_found] = a_poly[0] all_post[n_corners_found] = corner_found n_corners_found += 1 print("\n********** In total for feature %d:" % fn) print("Corners found: %d" % n_corners_found) if n_corners_found > 0: all_post = all_post[:n_corners_found] all_pre = all_pre[:n_corners_found] all_dxy = all_post - all_pre mdxy = all_dxy.mean(axis=0) sdxy = np.std(all_dxy, axis=0) ndxy = norm(all_dxy) params = (1, mdxy[0], mdxy[1]) print("Mean dxy: %.3f, %.3f" % (mdxy[0], mdxy[1])) print("Sd : %.3f, %.3f" % (sdxy[0], sdxy[1])) print("Max absolute : %.3f m" % (ndxy.max())) print("Mean absolute: %.3f m" % (ndxy.mean())) if n_corners_found > 1: print("Helmert transformation (pre to post):") params = helmert2d(all_pre, all_post) print("Scale: %.5f ppm" % ((params[0] - 1) * 1e6)) print("dx: %.3f m" % params[1]) print("dy: %.3f m" % params[2]) print("Residuals:") all_post_ = params[0] * all_pre + params[1:] all_dxy = all_post - all_post_ mdxy = all_dxy.mean(axis=0) sdxy = np.std(all_dxy, axis=0) ndxy = norm(all_dxy) print("Mean dxy: %.3f, %.3f" % (mdxy[0], mdxy[1])) print("Sd : %.3f, %.3f" % (sdxy[0], sdxy[1])) print("Max absolute : %.3f m" % (ndxy.max())) print("Mean absolute: %.3f m" % (ndxy.mean())) reporter.report(kmname, params[0], params[1], params[2], n_corners_found, ogr_geom=poly)
def run_check(p_number, testname, db_name, add_args, runid, use_local, schema, use_ref_data, lock): ''' Main checker rutine which should be defined for all processes. ''' logger = multiprocessing.log_to_stderr() test_func = qc.get_test(testname) #Set up some globals in various modules... per process. if runid is not None: report.set_run_id(runid) if use_local: # rather than sending args to scripts, which might not have implemented # handling that particular argument, set a global attr in report. report.set_use_local(True) elif schema is not None: report.set_schema(schema) #LOAD THE DATABASE con = sqlite.connect(db_name) if con is None: logger.error( "[qc_wrap]: Process: {0:d}, unable to fetch process db".format( p_number)) return cur = con.cursor() timestamp = (time.asctime().split()[-2]).replace(':', '_') logname = testname + '_' + timestamp + '_' + str(p_number) + '.log' logname = os.path.join(LOGDIR, logname) logfile = open(logname, 'w') stdout = osutils.redirect_stdout(logfile) stderr = osutils.redirect_stderr(logfile) filler = '*-*' * 23 print(filler) print( '[qc_wrap]: Running {test} routine at {time}, process: {proc}, run id: {rid}' .format(test=testname, time=time.asctime(), proc=p_number, rid=runid)) print(filler) done = 0 cur.execute('select count() from ' + testname + ' where status=0') n_left = cur.fetchone()[0] while n_left > 0: print(filler) print("[qc_wrap]: Number of tiles left: {0:d}".format(n_left)) print(filler) #Critical section# lock.acquire() cur.execute("select id,las_path,ref_path from " + testname + " where status=0") data = cur.fetchone() if data is None: print("[qc_wrap]: odd - seems to be no more tiles left...") lock.release() break fid, lasname, vname = data cur.execute( "update " + testname + " set status=?,prc_id=?,exe_start=? where id=?", (STATUS_PROCESSING, p_number, time.asctime(), fid)) try: con.commit() except Exception as err_msg: stderr.write( "[qc_wrap]: Unable to update tile to finish status...\n" + err_msg + "\n") break finally: lock.release() #end critical section# print("[qc_wrap]: Doing lasfile {0:s}...".format(lasname)) send_args = [testname, lasname] if use_ref_data: send_args.append(vname) send_args += add_args try: return_code = test_func(send_args) except Exception as err_msg: return_code = -1 msg = str(err_msg) status = STATUS_ERROR stderr.write("[qc_wrap]: Exception caught:\n" + msg + "\n") stderr.write("[qc_wrap]: Traceback:\n" + traceback.format_exc() + "\n") else: #set new status msg = "ok" status = STATUS_OK try: return_code = int(return_code) except (NameError, ValueError, TypeError): return_code = 0 cur.execute( "update " + testname + " set status=?,exe_end=?,rcode=?,msg=? where id=?", (status, time.asctime(), return_code, msg, fid)) done += 1 try: con.commit() except Exception as err_msg: stderr.write( "[qc_wrap]: Unable to update tile to finish status...\n" + err_msg + "\n") #go on to next one... cur.execute("select count() from " + testname + " where status=0") n_left = cur.fetchone()[0] print("[qc_wrap]: Checked %d tiles, finished at %s" % (done, time.asctime())) cur.close() con.close() #avoid writing to a closed fp... stdout.close() stderr.close() logfile.close()
def main(args): pargs = parser.parse_args(args[1:]) lasname = pargs.las_file polyname = pargs.build_polys kmname = constants.get_tilename(lasname) print("Running %s on block: %s, %s" % (os.path.basename(args[0]), kmname, time.asctime())) use_local = pargs.use_local if pargs.schema is not None: report.set_schema(pargs.schema) reporter = report.ReportRoofridgeCheck(use_local) cut_class = pargs.cut_class print("Using class(es): %s" % (cut_class)) # default step values for search... steps1 = 32 steps2 = 14 search_factor = pargs.search_factor if search_factor != 1: # can turn search steps up or down steps1 = int(search_factor * steps1) steps2 = int(search_factor * steps2) print("Incresing search factor by: %.2f" % search_factor) print( "Running time will increase exponentionally with search factor...") pc = pointcloud.fromAny(lasname).cut_to_class(cut_class).cut_to_z_interval( Z_MIN, Z_MAX) try: extent = np.asarray(constants.tilename_to_extent(kmname)) except Exception: print("Could not get extent from tilename.") extent = None polys = vector_io.get_geometries(polyname, pargs.layername, pargs.layersql, extent) fn = 0 sl = "+" * 60 is_sloppy = pargs.sloppy use_all = pargs.use_all for poly in polys: print(sl) fn += 1 print("Checking feature number %d" % fn) a_poly = array_geometry.ogrgeom2array(poly) # secret argument to use all buildings... if (len(a_poly) > 1 or a_poly[0].shape[0] != 5) and (not use_all) and (not is_sloppy): print("Only houses with 4 corners accepted... continuing...") continue pcp = pc.cut_to_polygon(a_poly) # hmmm, these consts should perhaps be made more visible... if (pcp.get_size() < 500 and (not is_sloppy)) or (pcp.get_size() < 10): print("Few points in polygon...") continue # Go to a more numerically stable coord system - from now on only consider outer ring... a_poly = a_poly[0] xy_t = a_poly.mean(axis=0) a_poly -= xy_t pcp.xy -= xy_t pcp.triangulate() geom = pcp.get_triangle_geometry() m = geom[:, 1].mean() sd = geom[:, 1].std() if (m > 1.5 or 0.5 * sd > m) and (not is_sloppy): print("Feature %d, bad geometry...." % fn) print(m, sd) continue planes = cluster(pcp, steps1, steps2) if len(planes) < 2: print("Feature %d, didn't find enough planes..." % fn) pair, equation = find_planar_pairs(planes) if pair is not None: p1 = planes[pair[0]] p2 = planes[pair[1]] z1 = p1[0] * pcp.xy[:, 0] + p1[1] * pcp.xy[:, 1] + p1[2] z2 = p2[0] * pcp.xy[:, 0] + p2[1] * pcp.xy[:, 1] + p2[2] print("%s" % ("*" * 60)) print("Statistics for feature %d" % fn) if DEBUG: plot3d(pcp.xy, pcp.z, z1, z2) intersections, distances, rotations = get_intersections( a_poly, equation) if intersections.shape[0] == 2: line_x = intersections[:, 0] line_y = intersections[:, 1] z_vals = p1[0] * intersections[:, 0] + p1[ 1] * intersections[:, 1] + p1[2] if abs(z_vals[0] - z_vals[1]) > 0.01: print("Numeric instabilty for z-calculation...") z_val = float(np.mean(z_vals)) print("Z for intersection is %.2f m" % z_val) if abs(equation[1]) > 1e-3: a = -equation[0] / equation[1] b = equation[2] / equation[1] line_y = a * line_x + b elif abs(equation[0]) > 1e-3: a = -equation[1] / equation[0] b = equation[2] / equation[0] line_x = a * line_y + b if DEBUG: plot_intersections(a_poly, intersections, line_x, line_y) # transform back to real coords line_x += xy_t[0] line_y += xy_t[1] wkt = "LINESTRING(%.3f %.3f %.3f, %.3f %.3f %.3f)" % ( line_x[0], line_y[0], z_val, line_x[1], line_y[1], z_val) print("WKT: %s" % wkt) reporter.report(kmname, rotations[0], distances[0], distances[1], wkt_geom=wkt) else: print( "Hmmm - something wrong, didn't get exactly two intersections..." )
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'...")
def main(args): pargs = parser.parse_args(args[1:]) lasname = pargs.las_file use_local = pargs.use_local if pargs.schema is not None: report.set_schema(pargs.schema) if pargs.height is not None: reporter = report.ReportClouds(use_local) CS = 4 CELL_COUNT_LIM = 4 else: reporter = report.ReportAutoBuilding(use_local) CS = 1 CELL_COUNT_LIM = 2 kmname = constants.get_tilename(lasname) print("Running %s on block: %s, %s" % (os.path.basename(args[0]), kmname, time.asctime())) try: xul, yll, xlr, yul = constants.tilename_to_extent(kmname) except Exception as e: print("Exception: %s" % str(e)) print("Bad 1km formatting of las file name: %s" % lasname) return 1 if pargs.height is not None: print("Cutting to z above %.2f m" % (pargs.height)) pc = pointcloud.fromAny(lasname).cut_to_z_interval( pargs.height, default_max_z) else: print("Cutting to class %d" % pargs.cut_to) pc = pointcloud.fromAny(lasname).cut_to_class(pargs.cut_to) if pc.get_size() == 0: print("No points after restriction...") return 0 cs = CS ncols = TILE_SIZE // cs nrows = ncols georef = [xul, cs, 0, yul, 0, -cs] arr_coords = ((pc.xy - (georef[0], georef[3])) / (georef[1], georef[5])).astype(np.int32) M = np.logical_and(arr_coords[:, 0] >= 0, arr_coords[:, 0] < ncols) M &= np.logical_and(arr_coords[:, 1] >= 0, arr_coords[:, 1] < nrows) arr_coords = arr_coords[M] # Wow - this gridding is sooo simple! and fast! #create flattened index B = arr_coords[:, 1] * ncols + arr_coords[:, 0] bins = np.arange(0, ncols * nrows + 1) h, b = np.histogram(B, bins) print("{} {} {}".format(h.shape, h.max(), h.min())) h = h.reshape((nrows, ncols)) if DEBUG: plt.imshow(h) plt.show() M = (h >= CELL_COUNT_LIM).astype(np.uint8) #Now create a GDAL memory raster mem_driver = gdal.GetDriverByName("MEM") mask_ds = mem_driver.Create("dummy", int(M.shape[1]), int(M.shape[0]), 1, gdal.GDT_Byte) mask_ds.SetGeoTransform(georef) mask_ds.GetRasterBand(1).WriteArray(M) #write zeros to output #Ok - so now polygonize that - use the mask as ehem... mask... m_drv = ogr.GetDriverByName("Memory") ds = m_drv.CreateDataSource("dummy") if ds is None: print("Creation of output ds failed.\n") return lyr = ds.CreateLayer("polys", None, ogr.wkbPolygon) fd = ogr.FieldDefn(dst_fieldname, ogr.OFTInteger) lyr.CreateField(fd) dst_field = 0 print("Polygonizing.....") gdal.Polygonize(mask_ds.GetRasterBand(1), mask_ds.GetRasterBand(1), lyr, dst_field) lyr.ResetReading() nf = lyr.GetFeatureCount() for i in range(nf): fet = lyr.GetNextFeature() geom = fet.GetGeometryRef() reporter.report(kmname, ogr_geom=geom) return 0
def main(args): ''' Run road delta check. Invoked from either command line or qc_wrap.py ''' pargs = parser.parse_args(args[1:]) lasname = pargs.las_file linename = pargs.lines kmname = constants.get_tilename(lasname) print("Running %s on block: %s, %s" % (os.path.basename(args[0]), kmname, time.asctime())) if pargs.schema is not None: report.set_schema(pargs.schema) reporter = report.ReportDeltaRoads(pargs.use_local) cut_class = pargs.cut_class pc = pointcloud.fromAny(lasname).cut_to_class(cut_class) if pc.get_size() < 5: print("Too few points to bother..") return 1 pc.triangulate() geom = pc.get_triangle_geometry() print("Using z-steepnes limit {0:.2f} m".format(pargs.zlim)) mask = np.logical_and(geom[:, 1] < XY_MAX, geom[:, 2] > pargs.zlim) geom = geom[mask] # save for reporting if not mask.any(): print("No steep triangles found...") return 0 # only the centers of the interesting triangles centers = pc.triangulation.get_triangle_centers()[mask] print("{0:d} steep triangles in tile.".format(centers.shape[0])) try: extent = np.asarray(constants.tilename_to_extent(kmname)) except Exception: print("Could not get extent from tilename.") extent = None lines = vector_io.get_geometries(linename, pargs.layername, pargs.layersql, extent) feature_count = 0 for line in lines: xy = array_geometry.ogrline2array(line, flatten=True) if xy.shape[0] == 0: print("Seemingly an unsupported geometry...") continue # select the triangle centers which lie within line_buffer of the road segment mask = array_geometry.points_in_buffer(centers, xy, LINE_BUFFER) critical = centers[mask] print("*" * 50) print("{0:d} steep centers along line {1:d}".format( critical.shape[0], feature_count)) feature_count += 1 if critical.shape[0] > 0: z_box = geom[mask][:, 2] z1 = z_box.max() z2 = z_box.min() wkt = "MULTIPOINT(" for point in critical: wkt += "{0:.2f} {1:.2f},".format(point[0], point[1]) wkt = wkt[:-1] + ")" reporter.report(kmname, z1, z2, wkt_geom=wkt)
con.commit() continue testname,schema,runid,targs=data logger.info("Was told to do job with id %s, test %s, on data (%s,%s)" %(job_id,testname,path,ref_path)) #now just run the script.... hmm - perhaps import with importlib and run it?? stdout.write(sl+"[proc_client] Doing definition %s from %s, test: %s\n"%(job_id,db_cstr,testname)) args={"__name__":"qc_wrap","path":path} try: targs=json.loads(targs) #convert to a python list test_func=qc.get_test(testname) use_ref_data=qc.tests[testname][0] use_reporting=qc.tests[testname][1] #both of these can be None - but that's ok. if use_reporting: report.set_run_id(runid) report.set_schema(schema) send_args=[testname,path] if use_ref_data: assert(len(ref_path)>0) send_args.append(ref_path) send_args+=targs rc=test_func(send_args) except Exception,e: stderr.write("[proc_client]: Exception caught:\n"+str(e)+"\n") stderr.write("[proc_client]: Traceback:\n"+traceback.format_exc()+"\n") logger.error("Caught: \n"+str(e)) msg=str(e)[:128] #truncate msg for now - or use larger field width. cur.execute("update proc_jobs set status=%s,msg=%s where ogc_fid=%s",(STATUS_ERROR,msg,id)) con.commit() else:
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 polyname = pargs.poly_data use_local = pargs.use_local if pargs.schema is not None: report.set_schema(pargs.schema) reporter = report.ReportBuildingRelposCheck(use_local) ################################## pc = pointcloud.fromAny(lasname).cut_to_z_interval( -10, 200).cut_to_class(cut_to_classes) try: extent = np.asarray(constants.tilename_to_extent(kmname)) except Exception as e: print("Could not get extent from tilename.") extent = None polys = vector_io.get_geometries(polyname) fn = 0 sl = "-" * 65 pcs = dict() for id in pc.get_pids(): print("%s\n" % ("+" * 70)) print("Strip id: %d" % id) pc_ = pc.cut_to_strip(id) if pc_.get_size() > 500: pcs[id] = pc_ else: print("Not enough points....") del pc done = [] for id1 in pcs: pc1 = pcs[id1] for id2 in pcs: if id1 == id2 or (id1, id2) in done or (id2, id1) in done: continue done.append((id1, id2)) pc2 = pcs[id2] ml = "-" * 70 print("%s\nChecking strip %d against strip %d\n%s" % (ml, id1, id2, ml)) if not pc1.might_overlap(pc2): if DEBUG: print( "Strip %d and strip %d does not seem to overlap. Continuing..." % (id1, id2)) print("DEBUG: Strip1 bounds:\n%s\nStrip2 bounds:\n%s" % (pc1.get_bounds(), pc2.get_bounds())) continue for poly in polys: centroid = poly.Centroid() centroid.FlattenTo2D() if LIGHT_DEBUG: print("Geom type: %s" % centroid.GetGeometryName()) n_corners_found = 0 fn += 1 print("%s\nChecking feature %d\n%s\n" % (sl, fn, sl)) a_poly = array_geometry.ogrgeom2array(poly) pcp1 = pc1.cut_to_polygon(a_poly) if pcp1.get_size() < 300: print("Few (%d) points in polygon..." % pcp1.get_size()) continue pcp2 = pc2.cut_to_polygon(a_poly) if pcp2.get_size() < 300: print("Few (%d) points in polygon..." % pcp2.get_size()) continue a_poly = a_poly[0] poly_buf = poly.Buffer(2.0) xy_t = a_poly.mean( axis=0 ) #transform later to center of mass system for numerical stability... #transform a_poly coords to center of mass system here a_poly -= xy_t a_poly2 = array_geometry.ogrgeom2array(poly_buf) #dicts to store the found corners in the two strips... found1 = dict() found2 = dict() for pc, pcp, store in [(pc1, pcp1, found1), (pc2, pcp2, found2)]: pcp.triangulate() geom = pcp.get_triangle_geometry() m = geom[:, 1].mean() sd = geom[:, 1].std() if (m > 1.5 or 0.5 * sd > m): print("Feature %d, bad geometry...." % fn) print("{} {}".format(m, sd)) break #geom is ok - we proceed with a buffer around da house pcp = pc.cut_to_polygon(a_poly2) ###### ## Transform pointcloud to center of mass system here... ####### pcp.xy -= xy_t print("Points in buffer: %d" % pcp.get_size()) pcp.triangulate() geom = pcp.get_triangle_geometry() tanv2 = tan(radians(cut_angle))**2 #set a mask to mark the triangles we want to consider as marking the house boundary mask = np.logical_and(geom[:, 0] > tanv2, geom[:, 2] > z_limit) #we consider the 'high' lying vertices - could also just select the highest of the three vertices... p_mask = (pcp.z > pcp.z.min() + 2) #and only consider those points which lie close to the outer bd of the house... p_mask &= array_geometry.points_in_buffer( pcp.xy, a_poly, 1.2) #a larger shift than 1.2 ?? #this just selects vertices where p_mask is true, from triangles where mask is true - nothing else... bd_mask = pcp.get_boundary_vertices(mask, p_mask) xy = pcp.xy[bd_mask] print("Boundary points: %d" % xy.shape[0]) if DEBUG: plot_points(a_poly, xy) #now find those corners! lines_ok = dict() found_lines = dict() for vertex in range( a_poly.shape[0] - 1): #check line emanating from vertex... p1 = a_poly[vertex] p2 = a_poly[vertex + 1] ok, pts = check_distribution(p1, p2, xy) lines_ok[vertex] = (ok, pts) #now find corners vertex = 0 #handle the 0'th corner specially... while vertex < a_poly.shape[0] - 2: if lines_ok[vertex][0] and lines_ok[vertex + 1][0]: #proceed print("%s\nCorner %d should be findable..." % ("+" * 50, vertex + 1)) corner_found = find_corner(vertex, lines_ok, found_lines, a_poly) diff = norm2(a_poly[vertex + 1] - corner_found) if ( diff < TOL_CORNER ): #seems reasonable that this is a true corner... store[vertex + 1] = corner_found n_corners_found += 1 #print a_poly[vertex+1],corner_found,vertex vertex += 1 else: #skip to next findable corner vertex += 2 if lines_ok[0][0] and lines_ok[a_poly.shape[0] - 2][0]: print("Corner 0 should also be findable...") corner_found = find_corner(a_poly.shape[0] - 2, lines_ok, found_lines, a_poly) diff = norm2(a_poly[0] - corner_found) if (diff < TOL_CORNER ): #seems reasonable that this is a true corner... store[0] = corner_found n_corners_found += 1 print("Corners found: %d" % n_corners_found) if n_corners_found == 0: #no need to do another check... break print( "Found %d corners in strip %d, and %d corners in strip %d" % (len(found1), id1, len(found2), id2)) if len(found1) > 0 and len(found2) > 0: match1 = [] match2 = [] for cn in found1: if cn in found2: match1.append(found1[cn]) match2.append(found2[cn]) if len(match1) > 0: match1 = np.array(match1) match2 = np.array(match2) n_corners_found = match1.shape[0] print("\n********** In total for feature %d:" % fn) print("Corners found in both strips: %d" % n_corners_found) all_dxy = match1 - match2 mdxy = all_dxy.mean(axis=0) sdxy = np.std(all_dxy, axis=0) ndxy = norm(all_dxy) params = (1, mdxy[0], mdxy[1]) print("Mean dxy: %.3f, %.3f" % (mdxy[0], mdxy[1])) print("Sd : %.3f, %.3f" % (sdxy[0], sdxy[1])) print("Max absolute : %.3f m" % (ndxy.max())) print("Mean absolute: %.3f m" % (ndxy.mean())) if n_corners_found > 1: print( "Helmert transformation (corners1 to corners2):" ) params = helmert2d(match1, match2) #2->1 or 1->2 ? print("Scale: %.5f ppm" % ((params[0] - 1) * 1e6)) print("dx: %.3f m" % params[1]) print("dy: %.3f m" % params[2]) print("Residuals:") match2_ = params[0] * match1 + params[1:] all_dxy = match2_ - match2 mdxy_ = all_dxy.mean(axis=0) sdxy_ = np.std(all_dxy, axis=0) ndxy_ = norm(all_dxy) if DEBUG or LIGHT_DEBUG: plot3([match1, match2, match2_]) #center of mass distances only!! cm_vector = (match2.mean(axis=0) - match1.mean(axis=0)) cm_dist = norm2(cm_vector) print("Mean dxy: %.3f, %.3f" % (mdxy_[0], mdxy_[1])) print("Sd : %.3f, %.3f" % (sdxy_[0], sdxy_[1])) print("Max absolute : %.3f m" % (ndxy_.max())) print("Mean absolute: %.3f m" % (ndxy_.mean())) reporter.report(kmname, id1, id2, cm_vector[0], cm_vector[1], cm_dist, params[0], params[1], params[2], sdxy_[0], sdxy_[1], n_corners_found, ogr_geom=centroid)
def run_check(p_number, testname, db_name, add_args, runid, use_local, schema, use_ref_data, lock): ''' Main checker rutine which should be defined for all processes. ''' logger = multiprocessing.log_to_stderr() test_func = qc.get_test(testname) #Set up some globals in various modules... per process. if runid is not None: report.set_run_id(runid) if use_local: # rather than sending args to scripts, which might not have implemented # handling that particular argument, set a global attr in report. report.set_use_local(True) elif schema is not None: report.set_schema(schema) #LOAD THE DATABASE con = sqlite.connect(db_name) if con is None: logger.error( "[qc_wrap]: Process: {0:d}, unable to fetch process db".format( p_number)) return cur = con.cursor() timestamp = (time.asctime().split()[-2]).replace(':', '_') logname = testname + '_' + timestamp + '_' + str(p_number) + '.log' logname = os.path.join(LOGDIR, logname) logfile = open(logname, 'w') stdout = osutils.redirect_stdout(logfile) stderr = osutils.redirect_stderr(logfile) filler = '*-*' * 23 print(filler) print( '[qc_wrap]: Running {test} routine at {time}, process: {proc}, run id: {rid}' .format(test=testname, time=time.asctime(), proc=p_number, rid=runid)) print(filler) done = 0 cur.execute('select count() from ' + testname + ' where status=0') n_left = cur.fetchone()[0] while n_left > 0: print(filler) print("[qc_wrap]: Number of tiles left: {0:d}".format(n_left)) print(filler) #Critical section# lock.acquire() cur.execute("select id,las_path,ref_path from " + testname + " where status=0") data = cur.fetchone() if data is None: print("[qc_wrap]: odd - seems to be no more tiles left...") lock.release() break fid, lasname, vname = data cur.execute( "update " + testname + " set status=?,prc_id=?,exe_start=? where id=?", (STATUS_PROCESSING, p_number, time.asctime(), fid)) try: con.commit() except Exception, err_msg: stderr.write( "[qc_wrap]: Unable to update tile to finish status...\n" + err_msg + "\n") break finally:
def main(args): pargs = parser.parse_args(args[1:]) lasname = pargs.las_file polyname = pargs.build_polys kmname = constants.get_tilename(lasname) print("Running %s on block: %s, %s" % (os.path.basename(args[0]), kmname, time.asctime())) if pargs.schema is not None: report.set_schema(pargs.schema) reporter = report.ReportRoofridgeStripCheck(pargs.use_local) cut_class = pargs.cut_class # default step values for search... steps1 = 30 steps2 = 13 search_factor = pargs.search_factor if search_factor != 1: # can turn search steps up or down steps1 = int(search_factor * steps1) steps2 = int(search_factor * steps2) print("Incresing search factor by: %.2f" % search_factor) print("Running time will increase exponentionally with search factor...") pc = pointcloud.fromAny(lasname).cut_to_class(cut_class).cut_to_z_interval(Z_MIN, Z_MAX) try: extent = np.asarray(constants.tilename_to_extent(kmname)) except Exception: print("Could not get extent from tilename.") extent = None polys = vector_io.get_geometries(polyname, pargs.layername, pargs.layersql, extent) fn = 0 sl = "+" * 60 is_sloppy = pargs.sloppy use_all = pargs.use_all for poly in polys: print(sl) fn += 1 print("Checking feature number %d" % fn) a_poly = array_geometry.ogrgeom2array(poly) # secret argument to use all buildings... if (len(a_poly) > 1 or a_poly[0].shape[0] != 5) and (not use_all) and (not is_sloppy): print("Only houses with 4 corners accepted... continuing...") continue pcp = pc.cut_to_polygon(a_poly) strips = pcp.get_pids() if len(strips) != 2: print("Not exactly two overlapping strips... continuing...") continue # Go to a more numerically stable coord system - from now on only consider outer ring... a_poly = a_poly[0] xy_t = a_poly.mean(axis=0) # center of mass system a_poly -= xy_t lines = [] # for storing the two found lines... for sid in strips: print("-*-" * 15) print("Looking at strip %d" % sid) pcp_ = pcp.cut_to_strip(sid) # hmmm, these consts should perhaps be made more visible... if (pcp_.get_size() < 500 and (not is_sloppy)) or (pcp_.get_size() < 10): print("Few points in polygon... %d" % pcp_.get_size()) continue pcp_.xy -= xy_t pcp_.triangulate() geom = pcp_.get_triangle_geometry() m = geom[:, 1].mean() sd = geom[:, 1].std() if (m > 1.5 or 0.5 * sd > m) and (not is_sloppy): print("Feature %d, strip %d, bad geometry...." % (fn, sid)) break planes = cluster(pcp_, steps1, steps2) if len(planes) < 2: print("Feature %d, strip %d, didn't find enough planes..." % (fn, sid)) pair, equation = find_planar_pairs(planes) if pair is not None: p1 = planes[pair[0]] print("%s" % ("*" * 60)) print("Statistics for feature %d" % fn) # Now we need to find some points on the line near the house... (0,0) is # the center of mass norm_normal = equation[0]**2 + equation[1]**2 if norm_normal < 1e-10: print("Numeric instablity, small normal") break # this should be on the line cm_line = np.asarray(equation[:2]) * (equation[2] / norm_normal) line_dir = np.asarray((-equation[1], equation[0])) / (sqrt(norm_normal)) end1 = cm_line + line_dir * LINE_RAD end2 = cm_line - line_dir * LINE_RAD intersections = np.vstack((end1, end2)) line_x = intersections[:, 0] line_y = intersections[:, 1] z_vals = p1[0] * intersections[:, 0] + p1[1] * intersections[:, 1] + p1[2] if abs(z_vals[0] - z_vals[1]) > 0.01: print("Numeric instabilty for z-calculation...") z_val = float(np.mean(z_vals)) print("Z for intersection is %.2f m" % z_val) # transform back to real coords line_x += xy_t[0] line_y += xy_t[1] wkt = "LINESTRING(%.3f %.3f %.3f, %.3f %.3f %.3f)" % ( line_x[0], line_y[0], z_val, line_x[1], line_y[1], z_val) print("WKT: %s" % wkt) lines.append([sid, wkt, z_val, cm_line, line_dir]) if len(lines) == 2: # check for parallelity id1 = lines[0][0] id2 = lines[1][0] z1 = lines[0][2] z2 = lines[1][2] if abs(z1 - z2) > 0.5: print("Large difference in z-values for the two lines!") else: ids = "{0:d}_{1:d}".format(id1, id2) inner_prod = (lines[0][4] * lines[1][4]).sum() inner_prod = max(-1, inner_prod) inner_prod = min(1, inner_prod) if DEBUG: print("Inner product: %.4f" % inner_prod) ang = abs(degrees(acos(inner_prod))) if ang > 175: ang = abs(180 - ang) if ang < 15: v = (lines[0][3] - lines[1][3]) d = np.sqrt((v**2).sum()) if d < 5: for line in lines: reporter.report(kmname, id1, id2, ids, d, ang, line[2], wkt_geom=line[1]) else: print("Large distance between centers %s, %s, %.2f" % (lines[0][3], lines[1][3], d)) else: print("Pair found - but not very well aligned - angle: %.2f" % ang) else: print("Pair not found...")