FeatureCursor* createFeatureCursor( const Symbology::Query& query ) { TileKey key = *query.tileKey(); int z = key.getLevelOfDetail(); int tileX = key.getTileX(); int tileY = key.getTileY(); unsigned int numRows, numCols; key.getProfile()->getNumTiles(key.getLevelOfDetail(), numCols, numRows); tileY = numRows - tileY - 1; //Get the image sqlite3_stmt* select = NULL; std::string queryStr = "SELECT tile_data from tiles where zoom_level = ? AND tile_column = ? AND tile_row = ?"; int rc = sqlite3_prepare_v2( _database, queryStr.c_str(), -1, &select, 0L ); if ( rc != SQLITE_OK ) { OE_WARN << LC << "Failed to prepare SQL: " << queryStr << "; " << sqlite3_errmsg(_database) << std::endl; return NULL; } bool valid = true; sqlite3_bind_int( select, 1, z ); sqlite3_bind_int( select, 2, tileX ); sqlite3_bind_int( select, 3, tileY ); rc = sqlite3_step( select ); FeatureList features; if ( rc == SQLITE_ROW) { // the pointer returned from _blob gets freed internally by sqlite, supposedly const char* data = (const char*)sqlite3_column_blob( select, 0 ); int dataLen = sqlite3_column_bytes( select, 0 ); std::string dataBuffer( data, dataLen ); std::stringstream in(dataBuffer); MVT::read(in, key, features); } else { OE_DEBUG << LC << "SQL QUERY failed for " << queryStr << ": " << std::endl; valid = false; } sqlite3_finalize( select ); // apply filters before returning. applyFilters( features ); if (!features.empty()) { //OE_NOTICE << "Returning " << features.size() << " features" << std::endl; return new FeatureListCursor(features); } return 0; }
virtual void traverse( FeatureTile* tile) { if (_added && _cropMethod != CropFilter::METHOD_CROPPING) return; bool traverse = true; GeoExtent featureExtent(_feature->getSRS(), _feature->getGeometry()->getBounds()); if (featureExtent.intersects( tile->getExtent())) { //If the node contains the feature, and it doesn't contain the max number of features add it. If it's already full then //split it. if (tile->getKey().getLevelOfDetail() >= (unsigned int)_firstLevel && (tile->getFeatures().size() < (unsigned int)_maxFeatures || tile->getKey().getLevelOfDetail() == _maxLevel || tile->getKey().getLevelOfDetail() == _levelAdded)) { if (_levelAdded < 0 || _levelAdded == tile->getKey().getLevelOfDetail()) { osg::ref_ptr< Feature > clone = new Feature( *_feature, osg::CopyOp::DEEP_COPY_ALL ); FeatureList features; features.push_back( clone ); CropFilter cropFilter(_cropMethod); FilterContext context(0); context.extent() = tile->getExtent(); cropFilter.push( features, context ); if (!features.empty() && clone->getGeometry() && clone->getGeometry()->isValid()) { //tile->getFeatures().push_back( clone ); tile->getFeatures().push_back( clone->getFID() ); _added = true; _levelAdded = tile->getKey().getLevelOfDetail(); _numAdded++; traverse = false; } } if (traverse || _cropMethod == CropFilter::METHOD_CROPPING) { tile->traverse( this ); } } else { tile->split(); tile->traverse( this ); } } }
osg::HeightField* createHeightField( const TileKey& key, ProgressCallback* progress) { if (key.getLevelOfDetail() > _maxDataLevel) { //OE_NOTICE << "Reached maximum data resolution key=" << key.getLevelOfDetail() << " max=" << _maxDataLevel << std::endl; return NULL; } int tileSize = _options.tileSize().value(); //Allocate the heightfield osg::ref_ptr<osg::HeightField> hf = new osg::HeightField; hf->allocate(tileSize, tileSize); for (unsigned int i = 0; i < hf->getHeightList().size(); ++i) hf->getHeightList()[i] = NO_DATA_VALUE; if (intersects(key)) { //Get the extents of the tile double xmin, ymin, xmax, ymax; key.getExtent().getBounds(xmin, ymin, xmax, ymax); const SpatialReference* featureSRS = _features->getFeatureProfile()->getSRS(); GeoExtent extentInFeatureSRS = key.getExtent().transform( featureSRS ); const SpatialReference* keySRS = key.getProfile()->getSRS(); // populate feature list // assemble a spatial query. It helps if your features have a spatial index. Query query; query.bounds() = extentInFeatureSRS.bounds(); FeatureList featureList; osg::ref_ptr<FeatureCursor> cursor = _features->createFeatureCursor(query); while ( cursor.valid() && cursor->hasMore() ) { Feature* f = cursor->nextFeature(); if ( f && f->getGeometry() ) featureList.push_back(f); } // We now have a feature list in feature SRS. bool transformRequired = !keySRS->isHorizEquivalentTo(featureSRS); if (!featureList.empty()) { // Iterate over the output heightfield and sample the data that was read into it. double dx = (xmax - xmin) / (tileSize-1); double dy = (ymax - ymin) / (tileSize-1); for (int c = 0; c < tileSize; ++c) { double geoX = xmin + (dx * (double)c); for (int r = 0; r < tileSize; ++r) { double geoY = ymin + (dy * (double)r); float h = NO_DATA_VALUE; for (FeatureList::iterator f = featureList.begin(); f != featureList.end(); ++f) { osgEarth::Symbology::Polygon* boundary = dynamic_cast<osgEarth::Symbology::Polygon*>((*f)->getGeometry()); if (!boundary) { OE_WARN << LC << "NOT A POLYGON" << std::endl; } else { GeoPoint geo(keySRS, geoX, geoY, 0.0, ALTMODE_ABSOLUTE); if ( transformRequired ) geo = geo.transform(featureSRS); if ( boundary->contains2D(geo.x(), geo.y()) ) { h = (*f)->getDouble(_options.attr().value()); if ( keySRS->isGeographic() ) { // for a round earth, must adjust the final elevation accounting for the // curvature of the earth; so we have to adjust it in the feature boundary's // local tangent plane. Bounds bounds = boundary->getBounds(); GeoPoint anchor( featureSRS, bounds.center().x(), bounds.center().y(), h, ALTMODE_ABSOLUTE ); if ( transformRequired ) anchor = anchor.transform(keySRS); // For transforming between ECEF and local tangent plane: osg::Matrix localToWorld, worldToLocal; anchor.createLocalToWorld(localToWorld); worldToLocal.invert( localToWorld ); // Get the ECEF location of the anchor point: osg::Vec3d ecef; geo.toWorld( ecef ); // Move it into Local Tangent Plane coordinates: osg::Vec3d local = ecef * worldToLocal; // Reset the Z to zero, since the LTP is centered on the "h" elevation: local.z() = 0.0; // Back into ECEF: ecef = local * localToWorld; // And back into lat/long/alt: geo.fromWorld( geo.getSRS(), ecef); h = geo.z(); } break; } } } hf->setHeight(c, r, h-0.1); } } } } return hf.release(); }
FeatureCursor* createFeatureCursor( const Symbology::Query& query ) { TileKey key = *query.tileKey(); #if 0 // Debug Polygon* poly = new Polygon(); poly->push_back(key.getExtent().xMin(), key.getExtent().yMin()); poly->push_back(key.getExtent().xMax(), key.getExtent().yMin()); poly->push_back(key.getExtent().xMax(), key.getExtent().yMax()); poly->push_back(key.getExtent().xMin(), key.getExtent().yMax()); FeatureList features; Feature* feature = new Feature(poly, SpatialReference::create("wgs84")); features.push_back( feature ); return new FeatureListCursor( features ); #else osg::ref_ptr< osgEarth::ImageLayer > layer = query.getMap()->getImageLayerByName(*_options.layer()); if (layer.valid()) { GeoImage image = layer->createImage( key ); FeatureList features; if (image.valid()) { double pixWidth = key.getExtent().width() / (double)image.getImage()->s(); double pixHeight = key.getExtent().height() / (double)image.getImage()->t(); ImageUtils::PixelReader reader(image.getImage()); for (unsigned int r = 0; r < image.getImage()->t(); r++) { double y = key.getExtent().yMin() + (double)r * pixHeight; double minX = 0; double maxX = 0; float value = 0.0; for (unsigned int c = 0; c < image.getImage()->s(); c++) { double x = key.getExtent().xMin() + (double)c * pixWidth; osg::Vec4f color = reader(c, r); // Starting a new row. Initialize the values. if (c == 0) { minX = x; maxX = x + pixWidth; value = color.r(); } // Ending a row, finish the polygon. else if (c == image.getImage()->s() -1) { // Increment the maxX to finish the row. maxX = x + pixWidth; Polygon* poly = new Polygon(); poly->push_back(minX, y); poly->push_back(maxX, y); poly->push_back(maxX, y+pixHeight); poly->push_back(minX, y+pixHeight); Feature* feature = new Feature(poly, SpatialReference::create("wgs84")); feature->set(*_options.attribute(), value); features.push_back( feature ); minX = x; maxX = x + pixWidth; value = color.r(); } // The value is different, so complete the polygon and start a new one. else if (color.r() != value) { Polygon* poly = new Polygon(); poly->push_back(minX, y); poly->push_back(maxX, y); poly->push_back(maxX, y+pixHeight); poly->push_back(minX, y+pixHeight); Feature* feature = new Feature(poly, SpatialReference::create("wgs84")); feature->set(*_options.attribute(), value); features.push_back( feature ); minX = x; maxX = x + pixWidth; value = color.r(); } // The value is the same as the previous value, continue the polygon by increasing the maxX. else if (color.r() == value) { maxX = x + pixWidth; } } } if (!features.empty()) { //OE_NOTICE << LC << "Returning " << features.size() << " features" << std::endl; return new FeatureListCursor( features ); } } } else { OE_NOTICE << LC << "Couldn't get layer " << *_options.layer() << std::endl; } return 0; #endif }
FeatureCursor* createFeatureCursor(const Symbology::Query& query, ProgressCallback* progress) { FeatureCursor* result = 0L; std::string url = createURL( query ); // the URL wil lbe empty if it was invalid or outside the level bounds of the layer. if (url.empty()) return 0L; OE_DEBUG << LC << url << std::endl; URI uri(url, _options.url()->context()); // read the data: ReadResult r = uri.readString(_readOptions.get(), progress); const std::string& buffer = r.getString(); const Config& meta = r.metadata(); bool dataOK = false; FeatureList features; if ( !buffer.empty() ) { // Get the mime-type from the metadata record if possible std::string mimeType = r.metadata().value( IOMetadata::CONTENT_TYPE ); //If the mimetype is empty then try to set it from the format specification if (mimeType.empty()) { if (_options.format().value() == "json") mimeType = "json"; else if (_options.format().value().compare("gml") == 0) mimeType = "text/xml"; else if (_options.format().value().compare("pbf") == 0) mimeType = "application/x-protobuf"; } dataOK = getFeatures( buffer, *query.tileKey(), mimeType, features ); } if ( dataOK ) { OE_DEBUG << LC << "Read " << features.size() << " features" << std::endl; } //If we have any filters, process them here before the cursor is created if (getFilters() && !getFilters()->empty() && !features.empty()) { FilterContext cx; cx.setProfile(getFeatureProfile()); cx.extent() = query.tileKey()->getExtent(); for (FeatureFilterChain::const_iterator i = getFilters()->begin(); i != getFilters()->end(); ++i) { FeatureFilter* filter = i->get(); cx = filter->push(features, cx); } } // If we have any features and we have an fid attribute, override the fid of the features if (_options.fidAttribute().isSet()) { for (FeatureList::iterator itr = features.begin(); itr != features.end(); ++itr) { std::string attr = itr->get()->getString(_options.fidAttribute().get()); FeatureID fid = as<long>(attr, 0); itr->get()->setFID( fid ); } } result = new FeatureListCursor(features); return result; }
FilterContext push(FeatureList& input, FilterContext& context) { if (_featureSource.valid()) { // Get any features that intersect this query. FeatureList boundaries; getFeatures(context.extent().get(), boundaries ); // The list of output features FeatureList output; if (boundaries.empty()) { // No intersecting features. If contains is false, then just the output to the input. if (contains() == false) { output = input; } } else { // Transform the boundaries into the coordinate system of the features for (FeatureList::iterator itr = boundaries.begin(); itr != boundaries.end(); ++itr) { itr->get()->transform( context.profile()->getSRS() ); } for(FeatureList::const_iterator f = input.begin(); f != input.end(); ++f) { Feature* feature = f->get(); if ( feature && feature->getGeometry() ) { osg::Vec2d c = feature->getGeometry()->getBounds().center2d(); if ( contains() == true ) { // coarsest: if (_featureSource->getFeatureProfile()->getExtent().contains(GeoPoint(feature->getSRS(), c.x(), c.y()))) { for (FeatureList::iterator itr = boundaries.begin(); itr != boundaries.end(); ++itr) { Ring* ring = dynamic_cast< Ring*>(itr->get()->getGeometry()); if (ring && ring->contains2D(c.x(), c.y())) { output.push_back( feature ); } } } } else { bool contained = false; // coarsest: if (_featureSource->getFeatureProfile()->getExtent().contains(GeoPoint(feature->getSRS(), c.x(), c.y()))) { for (FeatureList::iterator itr = boundaries.begin(); itr != boundaries.end(); ++itr) { Ring* ring = dynamic_cast< Ring*>(itr->get()->getGeometry()); if (ring && ring->contains2D(c.x(), c.y())) { contained = true; break; } } } if ( !contained ) { output.push_back( feature ); } } } } } OE_INFO << LC << "Allowed " << output.size() << " out of " << input.size() << " features\n"; input = output; } return context; }