std::vector<GeometryCollection> classifyRings(const GeometryCollection& rings) { std::vector<GeometryCollection> polygons; std::size_t len = rings.size(); if (len <= 1) { polygons.push_back(rings); return polygons; } GeometryCollection polygon; int8_t ccw = 0; for (std::size_t i = 0; i < len; i++) { double area = signedArea(rings[i]); if (area == 0) continue; if (ccw == 0) ccw = (area < 0 ? -1 : 1); if (ccw == (area < 0 ? -1 : 1) && !polygon.empty()) { polygons.push_back(polygon); polygon.clear(); } polygon.push_back(rings[i]); } if (!polygon.empty()) polygons.push_back(polygon); return polygons; }
/** * Tiles the Geometry into the given number of columns and rows */ void tileGeometry(Geometry* geometry, const SpatialReference* featureSRS, unsigned int numCols, unsigned int numRows, GeometryCollection& out) { // Clear the output list. out.clear(); Bounds b = geometry->getBounds(); double tw = b.width() / (double)numCols; double th = b.height() / (double)numRows; // Get the average Z, since GEOS will set teh Z of new verts to that of the cropping polygon, // which is stupid but that's how it is. double z = 0.0; for(unsigned i=0; i<geometry->size(); ++i) z += geometry->at(i).z(); z /= geometry->size(); osg::ref_ptr<Polygon> poly = new Polygon; poly->resize( 4 ); for(int x=0; x<(int)numCols; ++x) { for(int y=0; y<(int)numRows; ++y) { (*poly)[0].set( b.xMin() + tw*(double)x, b.yMin() + th*(double)y, z ); (*poly)[1].set( b.xMin() + tw*(double)(x+1), b.yMin() + th*(double)y, z ); (*poly)[2].set( b.xMin() + tw*(double)(x+1), b.yMin() + th*(double)(y+1), z ); (*poly)[3].set( b.xMin() + tw*(double)x, b.yMin() + th*(double)(y+1), z ); osg::ref_ptr<Geometry> ringTile; if ( geometry->crop(poly.get(), ringTile) ) { // Use an iterator since crop could return a multi-polygon GeometryIterator gi( ringTile.get(), false ); while( gi.hasMore() ) { Geometry* geom = gi.next(); out.push_back( geom ); } } } } }
/** * Prepares a geometry into a grid if it is too big geospatially to have a sensible local tangent plane * We will also tile the geometry if it just has too many points to speed up the tesselator. */ void prepareForTesselation(Geometry* geometry, const SpatialReference* featureSRS, double targetTileSizeDeg, unsigned int maxPointsPerTile, GeometryCollection& out) { // Clear the output list. GeometryCollection tiles; unsigned int count = geometry->size(); unsigned int tx = 1; unsigned int ty = 1; // Tile the geometry if it's geospatial size is too large to have a sensible local tangent plane. GeoExtent featureExtentDeg = GeoExtent(featureSRS, geometry->getBounds()).transform(SpatialReference::create("wgs84")); // Tile based on the extent if ( featureExtentDeg.width() > targetTileSizeDeg || featureExtentDeg.height() > targetTileSizeDeg) { // Determine the tile size based on the extent. tx = ceil( featureExtentDeg.width() / targetTileSizeDeg ); ty = ceil (featureExtentDeg.height() / targetTileSizeDeg ); } else if (count > maxPointsPerTile) { // Determine the size based on the number of points. unsigned numTiles = ((double)count / (double)maxPointsPerTile) + 1u; tx = ceil(sqrt((double)numTiles)); ty = tx; } if (tx == 1 && ty == 1) { // The geometry doesn't need modified so just add it to the list. tiles.push_back( geometry ); } else { tileGeometry( geometry, featureSRS, tx, ty, tiles ); } out.clear(); #if 1 // Just copy the output tiles to the output. std::copy(tiles.begin(), tiles.end(), std::back_inserter(out)); #else // Calling this code will recursively subdivide the cells based on the number of points they have. // This works but it will produces a non-regular grid which doesn't render well in geocentric // due to the curvature of the earth so we disable it for now. // // Reduce the size of the tiles if needed. for (unsigned int i = 0; i < tiles.size(); i++) { if (tiles[i]->size() > maxPointsPerTile) { GeometryCollection tmp; downsizeGeometry(tiles[i].get(), featureSRS, maxPointsPerTile, tmp); std::copy(tmp.begin(), tmp.end(), std::back_inserter(out)); } else { out.push_back( tiles[i].get() ); } } #endif }