void StatsFilter::extractMetadata(PointTableRef table) { uint32_t position(0); for (auto di = m_stats.begin(); di != m_stats.end(); ++di) { const Summary& s = di->second; MetadataNode t = m_metadata.addList("statistic"); t.add("position", position++); s.extractMetadata(t); } // If we have X, Y, & Z dims, output bboxes auto xs = m_stats.find(Dimension::Id::X); auto ys = m_stats.find(Dimension::Id::Y); auto zs = m_stats.find(Dimension::Id::Z); if (xs != m_stats.end() && ys != m_stats.end() && zs != m_stats.end()) { BOX3D box(xs->second.minimum(), ys->second.minimum(), zs->second.minimum(), xs->second.maximum(), ys->second.maximum(), zs->second.maximum()); pdal::Polygon p(box); MetadataNode mbox = Utils::toMetadata(box); MetadataNode box_metadata = m_metadata.add("bbox"); MetadataNode metadata = box_metadata.add("native"); MetadataNode boundary = metadata.add("boundary", p.json()); MetadataNode bbox = metadata.add(mbox); SpatialReference ref = table.anySpatialReference(); // if we don't get an SRS from the PointTableRef, // we won't add another metadata node if (!ref.empty()) { p.setSpatialReference(ref); SpatialReference epsg4326("EPSG:4326"); pdal::Polygon pdd = p.transform(epsg4326); BOX3D ddbox = pdd.bounds(); MetadataNode epsg_4326_box = Utils::toMetadata(ddbox); MetadataNode dddbox = box_metadata.add("EPSG:4326"); dddbox.add(epsg_4326_box); MetadataNode ddboundary = dddbox.add("boundary", pdd.json()); } } }
void HexBin::done(PointTableRef table) { m_grid->processSample(); try { m_grid->findShapes(); m_grid->findParentPaths(); } catch (hexer::hexer_error& e) { m_metadata.add("error", e.what(), "Hexer threw an error and was unable to compute a boundary"); m_metadata.add("boundary", "MULTIPOLYGON EMPTY", "Empty polygon -- unable to compute boundary"); return; } std::ostringstream offsets; offsets << "MULTIPOINT ("; for (int i = 0; i < 6; ++i) { hexer::Point p = m_grid->offset(i); offsets << p.m_x << " " << p.m_y; if (i != 5) offsets << ", "; } offsets << ")"; m_metadata.add("edge_length", m_edgeLength, "The edge length of the " "hexagon to use in situations where you do not want to estimate " "based on a sample"); m_metadata.add("estimated_edge", m_grid->height(), "Estimated computed edge distance"); m_metadata.add("threshold", m_grid->denseLimit(), "Minimum number of points inside a hexagon to be considered full"); m_metadata.add("sample_size", m_sampleSize, "Number of samples to use " "when estimating hexagon edge size. Specify 0.0 or omit options " "for edge_size if you want to compute one."); m_metadata.add("hex_offsets", offsets.str(), "Offset of hex corners from " "hex centers."); std::ostringstream polygon; polygon.setf(std::ios_base::fixed, std::ios_base::floatfield); polygon.precision(m_precision); m_grid->toWKT(polygon); if (m_outputTesselation) { MetadataNode hexes = m_metadata.add("hexagons"); for (HexIter hi = m_grid->hexBegin(); hi != m_grid->hexEnd(); ++hi) { HexInfo h = *hi; MetadataNode hex = hexes.addList("hexagon"); hex.add("density", h.density()); hex.add("gridpos", Utils::toString(h.xgrid()) + " " + Utils::toString((h.ygrid()))); std::ostringstream oss; // Using stream limits precision (default 6) oss << "POINT (" << h.x() << " " << h.y() << ")"; hex.add("center", oss.str()); } m_metadata.add("hex_boundary", polygon.str(), "Boundary MULTIPOLYGON of domain"); } /*** We want to make these bumps on edges go away, which means that we want to elimnate both B and C. If we take a line from A -> C, we need the tolerance to eliminate B. After that we're left with the triangle ACD and we want to eliminate C. The perpendicular distance from AD to C is the hexagon height / 2, so we set the tolerance a little larger than that. This is larger than the perpendicular distance needed to eliminate B in ABC, so should serve for both cases. B ______ C / \ A / \ D ***/ double tolerance = 1.1 * m_grid->height() / 2; double cull = m_cullArg->set() ? m_cullArea : (6 * tolerance * tolerance); SpatialReference srs(table.anySpatialReference()); pdal::Polygon p(polygon.str(), srs); pdal::Polygon density_p(polygon.str(), srs); // If the SRS was geographic, use relevant // UTM for area and density computation if (srs.isGeographic()) { // Compute a UTM polygon BOX3D box = p.bounds(); int zone = SpatialReference::calculateZone(box.minx, box.miny); auto makezone = [] (int zone) -> std::string { std::ostringstream z; // Use WGS84 UTM zones z << "EPSG:327" << abs(zone); return z.str(); }; SpatialReference utm(makezone(zone)); density_p = p.transform(utm); } if (m_doSmooth) p = p.simplify(tolerance, cull); std::string boundary_text = p.wkt(m_precision); m_metadata.add("boundary", boundary_text, "Approximated MULTIPOLYGON of domain"); m_metadata.addWithType("boundary_json", p.json(), "json", "Approximated MULTIPOLYGON of domain"); double area = density_p.area(); double density = (double) m_count/ area ; if (std::isinf(density)) { density = -1.0; area = -1.0; } m_metadata.add("density", density, "Number of points per square unit (total area)"); m_metadata.add("area", area, "Area in square units of tessellated polygon"); double moving_avg(0.0); double avg_count(0.0); double hex_area(((3 * SQRT_3)/2.0) * (m_grid->height() * m_grid->height())); int n(0); point_count_t totalCount(0); double totalArea(0.0); for (HexIter hi = m_grid->hexBegin(); hi != m_grid->hexEnd(); ++hi) { HexInfo h = *hi; totalCount += h.density(); totalArea += hex_area; ++n; } double avg_density = totalArea /(double) totalCount; m_metadata.add("avg_pt_per_sq_unit", avg_density, "Number of points " "per square unit (tessellated area within inclusions)"); double avg_spacing = std::sqrt(1/density); m_metadata.add("avg_pt_spacing", avg_spacing, "Avg point spacing (x/y units)"); }