osg::BoundingSphered
FeatureModelGraph::getBoundInWorldCoords( const GeoExtent& extent ) const
{
    osg::Vec3d center, corner;

    GeoExtent workingExtent;

    if ( extent.getSRS()->isEquivalentTo( _usableMapExtent.getSRS() ) )
    {
        workingExtent = extent;
    }
    else
    {
        workingExtent = extent.transform( _usableMapExtent.getSRS() ); // safe.
    }

    workingExtent.getCentroid( center.x(), center.y() );
    corner.x() = workingExtent.xMin();
    corner.y() = workingExtent.yMin();

    if ( _session->getMapInfo().isGeocentric() )
    {
        workingExtent.getSRS()->transformToECEF( center, center );
        workingExtent.getSRS()->transformToECEF( corner, corner );
    }

    return osg::BoundingSphered( center, (center-corner).length() );
}
osg::BoundingSphered
FeatureModelGraph::getBoundInWorldCoords(const GeoExtent& extent,
                                         const MapFrame*  mapf ) const
{
    osg::Vec3d center, corner;
    GeoExtent workingExtent;

    if ( !extent.isValid() )
    {
        return osg::BoundingSphered();
    }

    if ( extent.getSRS()->isEquivalentTo( _usableMapExtent.getSRS() ) )
    {
        workingExtent = extent;
    }
    else
    {
        workingExtent = extent.transform( _usableMapExtent.getSRS() ); // safe.
    }

    workingExtent.getCentroid( center.x(), center.y() );
    
    double centerZ = 0.0;    
    if ( mapf )
    {
        // Use an appropriate resolution for this extents width
        double resolution = workingExtent.width();
        ElevationQuery query( *mapf );
        GeoPoint p( mapf->getProfile()->getSRS(), center, ALTMODE_ABSOLUTE );
        query.getElevation( p, center.z(), resolution );
        centerZ = center.z();
    }    

    corner.x() = workingExtent.xMin();
    corner.y() = workingExtent.yMin();
    corner.z() = 0;

    if ( _session->getMapInfo().isGeocentric() )
    {
        const SpatialReference* ecefSRS = workingExtent.getSRS()->getECEF();
        workingExtent.getSRS()->transform( center, ecefSRS, center );
        workingExtent.getSRS()->transform( corner, ecefSRS, corner );
        //workingExtent.getSRS()->transformToECEF( center, center );
        //workingExtent.getSRS()->transformToECEF( corner, corner );
    }

    if (workingExtent.getSRS()->isGeographic() &&
        ( workingExtent.width() >= 90 || workingExtent.height() >= 90 ) )
    {
        return osg::BoundingSphered( osg::Vec3d(0,0,0), 2*center.length() );
    }

    return osg::BoundingSphered( center, (center-corner).length() );
}
osg::BoundingSphered
FeatureModelGraph::getBoundInWorldCoords(const GeoExtent& extent,
                                         const MapFrame*  mapf ) const
{
    osg::Vec3d center, corner;
    //double z = 0.0;
    GeoExtent workingExtent;

    if ( extent.getSRS()->isEquivalentTo( _usableMapExtent.getSRS() ) )
    {
        workingExtent = extent;
    }
    else
    {
        workingExtent = extent.transform( _usableMapExtent.getSRS() ); // safe.
    }

    workingExtent.getCentroid( center.x(), center.y() );
    
    double centerZ = 0.0;    
    if ( mapf )
    {
        // Use an appropriate resolution for this extents width
        double resolution = workingExtent.width();             
        ElevationQuery query( *mapf );
        query.getElevation( GeoPoint(mapf->getProfile()->getSRS(),center), center.z(), resolution );
        centerZ = center.z();
    }    

    corner.x() = workingExtent.xMin();
    corner.y() = workingExtent.yMin();
    corner.z() = 0;

    if ( _session->getMapInfo().isGeocentric() )
    {
        workingExtent.getSRS()->transformToECEF( center, center );
        workingExtent.getSRS()->transformToECEF( corner, corner );
    }

    return osg::BoundingSphered( center, (center-corner).length() );
}
Exemple #4
0
void
FeaturesToNodeFilter::computeLocalizers( const FilterContext& context )
{
    if ( context.isGeoreferenced() )
    {
        if ( context.getSession()->getMapInfo().isGeocentric() )
        {
            const SpatialReference* geogSRS = context.profile()->getSRS()->getGeographicSRS();
            GeoExtent geodExtent = context.extent()->transform( geogSRS );
            if ( geodExtent.width() < 180.0 )
            {
                osg::Vec3d centroid, centroidECEF;
                geodExtent.getCentroid( centroid.x(), centroid.y() );
                geogSRS->transform( centroid, geogSRS->getECEF(), centroidECEF );
                geogSRS->getECEF()->createLocalToWorld( centroidECEF, _local2world );
                _world2local.invert( _local2world );
            }
        }

        else // projected
        {
            if ( context.extent().isSet() )
            {
                osg::Vec3d centroid;
                context.extent()->getCentroid(centroid.x(), centroid.y());

                context.extent()->getSRS()->transform(
                    centroid,
                    context.getSession()->getMapInfo().getProfile()->getSRS(),
                    centroid );

                _world2local.makeTranslate( -centroid );
                _local2world.invert( _world2local );
            }
        }
    }
}
Exemple #5
0
void
FeaturesToNodeFilter::computeLocalizers( const FilterContext& context, const osgEarth::GeoExtent &extent, osg::Matrixd &out_w2l, osg::Matrixd &out_l2w )
{
    if ( context.isGeoreferenced() )
    {
        if ( context.getSession()->getMapInfo().isGeocentric() )
        {
            const SpatialReference* geogSRS = context.profile()->getSRS()->getGeographicSRS();
            GeoExtent geodExtent = extent.transform( geogSRS );
            if ( geodExtent.width() < 180.0 )
            {
                osg::Vec3d centroid, centroidECEF;
                geodExtent.getCentroid( centroid.x(), centroid.y() );
                geogSRS->transform( centroid, geogSRS->getECEF(), centroidECEF );
                geogSRS->getECEF()->createLocalToWorld( centroidECEF, out_l2w );
                out_w2l.invert( out_l2w );
            }
        }

        else // projected
        {
            if ( extent.isValid() )
            {
                osg::Vec3d centroid;
                extent.getCentroid(centroid.x(), centroid.y());

                extent.getSRS()->transform(
                    centroid,
                    context.getSession()->getMapInfo().getProfile()->getSRS(),
                    centroid );

                out_w2l.makeTranslate( -centroid );
                out_l2w.invert( out_w2l );
            }
        }
    }
}
/* method: getCentroid of class  GeoExtent */
static int tolua_Lua_ScriptEngine_tolua_GeoExtent_getCentroid00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
 tolua_Error tolua_err;
 if (
 !tolua_isusertype(tolua_S,1,"GeoExtent",0,&tolua_err) ||
 !tolua_isnoobj(tolua_S,2,&tolua_err)
 )
 goto tolua_lerror;
 else
#endif
 {
  GeoExtent* self = (GeoExtent*)  tolua_tousertype(tolua_S,1,0);
#ifndef TOLUA_RELEASE
 if (!self) tolua_error(tolua_S,"invalid 'self' in function 'getCentroid'",NULL);
#endif
 {
  GeoPoint tolua_ret = (GeoPoint)  self->getCentroid();
 {
#ifdef __cplusplus
 void* tolua_obj = new GeoPoint(tolua_ret);
 tolua_pushusertype(tolua_S,tolua_clone(tolua_S,tolua_obj,tolua_collect_GeoPoint),"GeoPoint");
#else
 void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(GeoPoint));
 tolua_pushusertype(tolua_S,tolua_clone(tolua_S,tolua_obj,tolua_collect),"GeoPoint");
#endif
 }
 }
 }
 return 1;
#ifndef TOLUA_RELEASE
 tolua_lerror:
 tolua_error(tolua_S,"#ferror in function 'getCentroid'.",&tolua_err);
 return 0;
#endif
}
osg::Node*
GeodeticGraticule::buildTile( const TileKey& key, Map* map ) const
{
    if ( _options->levels().size() <= key.getLevelOfDetail() )
    {
        OE_WARN << LC << "Tried to create cell at non-existant level " << key.getLevelOfDetail() << std::endl;
        return 0L;
    }

    const GeodeticGraticuleOptions::Level& level = _options->levels()[key.getLevelOfDetail()]; //_levels[key.getLevelOfDetail()];


    // the "-2" here is because normal tile paging gives you one subdivision already,
    // so we only need to account for > 1 subdivision factor.
    unsigned cellsPerTile = level._subdivisionFactor <= 2u ? 1u : 1u << (level._subdivisionFactor-2u);
    unsigned cellsPerTileX = std::max(1u, cellsPerTile);
    unsigned cellsPerTileY = std::max(1u, cellsPerTile);


    GeoExtent tileExtent = key.getExtent();

    FeatureList latLines;
    FeatureList lonLines;

    static LatLongFormatter s_llf(LatLongFormatter::FORMAT_DECIMAL_DEGREES);

    double cellWidth = tileExtent.width() / cellsPerTileX;
    double cellHeight = tileExtent.height() / cellsPerTileY;

    const Style& lineStyle = level._lineStyle.isSet() ? *level._lineStyle : *_options->lineStyle();
    const Style& textStyle = level._textStyle.isSet() ? *level._textStyle : *_options->textStyle();

    bool hasText = textStyle.get<TextSymbol>() != 0L;

    osg::ref_ptr<osg::Group> labels;
    if ( hasText )
    {
        labels = new osg::Group();
        //TODO:  This is a bug, if you don't turn on decluttering the text labels are giant.  Need to determine what is wrong with LabelNodes without decluttering.
        Decluttering::setEnabled( labels->getOrCreateStateSet(), true );
    }

    // spatial ref for features:
    const SpatialReference* geoSRS = tileExtent.getSRS()->getGeographicSRS();

    // longitude lines
    for( unsigned cx = 0; cx < cellsPerTileX; ++cx )
    {
        double clon = tileExtent.xMin() + cellWidth * (double)cx;
        LineString* lon = new LineString(2);
        lon->push_back( osg::Vec3d(clon, tileExtent.yMin(), 0) );
        lon->push_back( osg::Vec3d(clon, tileExtent.yMax(), 0) );
        lonLines.push_back( new Feature(lon, geoSRS) );

        if ( hasText )
        {
            for( unsigned cy = 0; cy < cellsPerTileY; ++cy )
            {
                double clat = tileExtent.yMin() + (0.5*cellHeight) + cellHeight*(double)cy;
                LabelNode* label = new LabelNode(
                    _mapNode.get(),
                    GeoPoint(geoSRS, clon, clat),
                    s_llf.format(clon),
                    textStyle );
                labels->addChild( label );
            }
        }
    }

    // latitude lines
    for( unsigned cy = 0; cy < cellsPerTileY; ++cy )
    {
        double clat = tileExtent.yMin() + cellHeight * (double)cy;
        if ( clat == key.getProfile()->getExtent().yMin() )
            continue;

        LineString* lat = new LineString(2);
        lat->push_back( osg::Vec3d(tileExtent.xMin(), clat, 0) );
        lat->push_back( osg::Vec3d(tileExtent.xMax(), clat, 0) );
        latLines.push_back( new Feature(lat, geoSRS) );

        if ( hasText )
        {
            for( unsigned cx = 0; cx < cellsPerTileX; ++cx )
            {
                double clon = tileExtent.xMin() + (0.5*cellWidth) + cellWidth*(double)cy;
                LabelNode* label = new LabelNode(
                    _mapNode.get(),
                    GeoPoint(geoSRS, clon, clat),
                    s_llf.format(clat),
                    textStyle );
                labels->addChild( label );
            }
        }
    }

    osg::Group* group = new osg::Group();

    GeometryCompiler compiler;
    osg::ref_ptr<Session> session = new Session( map );
    FilterContext context( session.get(), _featureProfile.get(), tileExtent );

    // make sure we get sufficient tessellation:
    compiler.options().maxGranularity() = std::min(cellWidth, cellHeight) / 16.0;

    compiler.options().geoInterp() = GEOINTERP_GREAT_CIRCLE;
    osg::Node* lonNode = compiler.compile(lonLines, lineStyle, context);
    if ( lonNode )
        group->addChild( lonNode );

    compiler.options().geoInterp() = GEOINTERP_RHUMB_LINE;
    osg::Node* latNode = compiler.compile(latLines, lineStyle, context);
    if ( latNode )
        group->addChild( latNode );

    // add the labels.
    if ( labels.valid() )
        group->addChild( labels.get() );

    // get the geocentric tile center:
    osg::Vec3d tileCenter;
    tileExtent.getCentroid( tileCenter.x(), tileCenter.y() );

    const SpatialReference* ecefSRS = tileExtent.getSRS()->getECEF();
    osg::Vec3d centerECEF;
    tileExtent.getSRS()->transform( tileCenter, ecefSRS, centerECEF );
    //tileExtent.getSRS()->transformToECEF( tileCenter, centerECEF );

    osg::NodeCallback* ccc = 0L;
    // set up cluster culling.
    if ( tileExtent.getSRS()->isGeographic() && tileExtent.width() < 90.0 && tileExtent.height() < 90.0 )
    {
        ccc = ClusterCullingFactory::create( group, centerECEF );
    }

    // add a paging node for higher LODs:
    if ( key.getLevelOfDetail() + 1 < _options->levels().size() )
    {
        const GeodeticGraticuleOptions::Level& nextLevel = _options->levels()[key.getLevelOfDetail()+1];

        osg::BoundingSphere bs = group->getBound();

        std::string uri = Stringify() << key.str() << "_" << getID() << "." << GRID_MARKER << "." << GRATICULE_EXTENSION;

        osg::PagedLOD* plod = new osg::PagedLOD();
        plod->setCenter( bs.center() );
        plod->addChild( group, std::max(level._minRange,nextLevel._maxRange), FLT_MAX );
        plod->setFileName( 1, uri );
        plod->setRange( 1, 0, nextLevel._maxRange );
        group = plod;
    }

    // or, if this is the deepest level and there's a minRange set, we need an LOD:
    else if ( level._minRange > 0.0f )
    {
        osg::LOD* lod = new osg::LOD();
        lod->addChild( group, level._minRange, FLT_MAX );
        group = lod;
    }

    if ( ccc )
    {
        osg::Group* cccGroup = new osg::Group();
        cccGroup->addCullCallback( ccc );
        cccGroup->addChild( group );
        group = cccGroup;
    }

    return group;
}
    //override
    bool renderFeaturesForStyle(
        Session*           session,
        const Style&       style,
        const FeatureList& features,
        osg::Referenced*   buildData,
        const GeoExtent&   imageExtent,
        osg::Image*        image )
    {
        OE_DEBUG << LC << "Rendering " << features.size() << " features for " << imageExtent.toString() << "\n";

        // A processing context to use with the filters:
        FilterContext context( session );
        context.setProfile( getFeatureSource()->getFeatureProfile() );

        const LineSymbol*    masterLine = style.getSymbol<LineSymbol>();
        const PolygonSymbol* masterPoly = style.getSymbol<PolygonSymbol>();
        const CoverageSymbol* masterCov = style.getSymbol<CoverageSymbol>();

        // sort into bins, making a copy for lines that require buffering.
        FeatureList polygons;
        FeatureList lines;

        for(FeatureList::const_iterator f = features.begin(); f != features.end(); ++f)
        {
            if ( f->get()->getGeometry() )
            {
                bool hasPoly = false;
                bool hasLine = false;

                if ( masterPoly || f->get()->style()->has<PolygonSymbol>() )
                {
                    polygons.push_back( f->get() );
                    hasPoly = true;
                }

                if ( masterLine || f->get()->style()->has<LineSymbol>() )
                {
                    Feature* newFeature = new Feature( *f->get() );
                    if ( !newFeature->getGeometry()->isLinear() )
                    {
                        newFeature->setGeometry( newFeature->getGeometry()->cloneAs(Geometry::TYPE_RING) );
                    }
                    lines.push_back( newFeature );
                    hasLine = true;
                }

                // if there are no geometry symbols but there is a coverage symbol, default to polygons.
                if ( !hasLine && !hasPoly )
                {
                    if ( masterCov || f->get()->style()->has<CoverageSymbol>() )
                    {
                        polygons.push_back( f->get() );
                    }
                }
            }
        }

        // initialize:
        RenderFrame frame;
        frame.xmin = imageExtent.xMin();
        frame.ymin = imageExtent.yMin();
        frame.xf   = (double)image->s() / imageExtent.width();
        frame.yf   = (double)image->t() / imageExtent.height();

        if ( lines.size() > 0 )
        {
            // We are buffering in the features native extent, so we need to use the
            // transformed extent to get the proper "resolution" for the image
            const SpatialReference* featureSRS = context.profile()->getSRS();
            GeoExtent transformedExtent = imageExtent.transform(featureSRS);

            double trans_xf = (double)image->s() / transformedExtent.width();
            double trans_yf = (double)image->t() / transformedExtent.height();

            // resolution of the image (pixel extents):
            double xres = 1.0/trans_xf;
            double yres = 1.0/trans_yf;

            // downsample the line data so that it is no higher resolution than to image to which
            // we intend to rasterize it. If you don't do this, you run the risk of the buffer 
            // operation taking forever on very high-res input data.
            if ( _options.optimizeLineSampling() == true )
            {
                ResampleFilter resample;
                resample.minLength() = osg::minimum( xres, yres );
                context = resample.push( lines, context );
            }

            // now run the buffer operation on all lines:
            BufferFilter buffer;
            double lineWidth = 1.0;
            if ( masterLine )
            {
                buffer.capStyle() = masterLine->stroke()->lineCap().value();

                if ( masterLine->stroke()->width().isSet() )
                {
                    lineWidth = masterLine->stroke()->width().value();

                    GeoExtent imageExtentInFeatureSRS = imageExtent.transform(featureSRS);
                    double pixelWidth = imageExtentInFeatureSRS.width() / (double)image->s();

                    // if the width units are specified, process them:
                    if (masterLine->stroke()->widthUnits().isSet() &&
                        masterLine->stroke()->widthUnits().get() != Units::PIXELS)
                    {
                        const Units& featureUnits = featureSRS->getUnits();
                        const Units& strokeUnits  = masterLine->stroke()->widthUnits().value();

                        // if the units are different than those of the feature data, we need to
                        // do a units conversion.
                        if ( featureUnits != strokeUnits )
                        {
                            if ( Units::canConvert(strokeUnits, featureUnits) )
                            {
                                // linear to linear, no problem
                                lineWidth = strokeUnits.convertTo( featureUnits, lineWidth );
                            }
                            else if ( strokeUnits.isLinear() && featureUnits.isAngular() )
                            {
                                // linear to angular? approximate degrees per meter at the 
                                // latitude of the tile's centroid.
                                double lineWidthM = masterLine->stroke()->widthUnits()->convertTo(Units::METERS, lineWidth);
                                double mPerDegAtEquatorInv = 360.0/(featureSRS->getEllipsoid()->getRadiusEquator() * 2.0 * osg::PI);
                                double lon, lat;
                                imageExtent.getCentroid(lon, lat);
                                lineWidth = lineWidthM * mPerDegAtEquatorInv * cos(osg::DegreesToRadians(lat));
                            }
                        }

                        // enfore a minimum width of one pixel.
                        float minPixels = masterLine->stroke()->minPixels().getOrUse( 1.0f );
                        lineWidth = osg::clampAbove(lineWidth, pixelWidth*minPixels);
                    }

                    else // pixels
                    {
                        lineWidth *= pixelWidth;
                    }
                }
            }

            buffer.distance() = lineWidth * 0.5;   // since the distance is for one side
            buffer.push( lines, context );
        }

        // Transform the features into the map's SRS:
        TransformFilter xform( imageExtent.getSRS() );
        xform.setLocalizeCoordinates( false );
        FilterContext polysContext = xform.push( polygons, context );
        FilterContext linesContext = xform.push( lines, context );

        // set up the AGG renderer:
        agg::rendering_buffer rbuf( image->data(), image->s(), image->t(), image->s()*4 );

        // Create the renderer and the rasterizer
        agg::rasterizer ras;

        // Setup the rasterizer
        if ( _options.coverage() == true )
            ras.gamma(1.0);
        else
            ras.gamma(_options.gamma().get());

        ras.filling_rule(agg::fill_even_odd);

        // construct an extent for cropping the geometry to our tile.
        // extend just outside the actual extents so we don't get edge artifacts:
        GeoExtent cropExtent = GeoExtent(imageExtent);
        cropExtent.scale(1.1, 1.1);

        osg::ref_ptr<Symbology::Polygon> cropPoly = new Symbology::Polygon( 4 );
        cropPoly->push_back( osg::Vec3d( cropExtent.xMin(), cropExtent.yMin(), 0 ));
        cropPoly->push_back( osg::Vec3d( cropExtent.xMax(), cropExtent.yMin(), 0 ));
        cropPoly->push_back( osg::Vec3d( cropExtent.xMax(), cropExtent.yMax(), 0 ));
        cropPoly->push_back( osg::Vec3d( cropExtent.xMin(), cropExtent.yMax(), 0 ));

        // If there's a coverage symbol, make a copy of the expressions so we can evaluate them
        optional<NumericExpression> covValue;
        const CoverageSymbol* covsym = style.get<CoverageSymbol>();
        if (covsym && covsym->valueExpression().isSet())
            covValue = covsym->valueExpression().get();

        // render the polygons
        for(FeatureList::iterator i = polygons.begin(); i != polygons.end(); i++)
        {
            Feature*  feature  = i->get();
            Geometry* geometry = feature->getGeometry();

            osg::ref_ptr<Geometry> croppedGeometry;
            if ( geometry->crop( cropPoly.get(), croppedGeometry ) )
            {
                const PolygonSymbol* poly =
                    feature->style().isSet() && feature->style()->has<PolygonSymbol>() ? feature->style()->get<PolygonSymbol>() :
                    masterPoly;

                if ( _options.coverage() == true && covValue.isSet() )
                {
                    float value = (float)feature->eval(covValue.mutable_value(), &context);
                    rasterizeCoverage(croppedGeometry.get(), value, frame, ras, rbuf);
                }
                else
                {
                    osg::Vec4f color = poly->fill()->color();
                    rasterize(croppedGeometry.get(), color, frame, ras, rbuf);
                }
                
            }
        }

        // render the lines
        for(FeatureList::iterator i = lines.begin(); i != lines.end(); i++)
        {
            Feature*  feature  = i->get();
            Geometry* geometry = feature->getGeometry();

            osg::ref_ptr<Geometry> croppedGeometry;
            if ( geometry->crop( cropPoly.get(), croppedGeometry ) )
            {
                const LineSymbol* line =
                    feature->style().isSet() && feature->style()->has<LineSymbol>() ? feature->style()->get<LineSymbol>() :
                    masterLine;

                if ( _options.coverage() == true && covValue.isSet() )
                {
                    float value = (float)feature->eval(covValue.mutable_value(), &context);
                    rasterizeCoverage(croppedGeometry.get(), value, frame, ras, rbuf);
                }
                else
                {   osg::Vec4f color = line ? static_cast<osg::Vec4>(line->stroke()->color()) : osg::Vec4(1,1,1,1);
                    rasterize(croppedGeometry.get(), color, frame, ras, rbuf);
                }
            }
        }

        return true;
    }
Exemple #9
0
// Calculates a sub-extent of a larger extent, given the number of children and
// the child number. This currently assumes the subdivision ordering used by
// VirtualPlanetBuilder.
GeoExtent
TerrainUtils::getSubExtent(const GeoExtent& extent,
                           int              num_children,
                           int              child_no)
{
    GeoPoint centroid = extent.getCentroid();
    GeoExtent sub_extent;

    switch( num_children )
    {
    case 0:
    case 1:
        sub_extent = extent;
        break;

    case 2:
        if ( child_no == 0 )
        {
            sub_extent = GeoExtent(
                             extent.getXMin(),
                             extent.getYMin(),
                             centroid.x(),
                             extent.getYMax(),
                             extent.getSRS() );
        }
        else
        {
            sub_extent = GeoExtent(
                             centroid.x(),
                             extent.getYMin(),
                             extent.getXMax(),
                             extent.getYMax(),
                             extent.getSRS() );
        }
        break;

    case 4:
        if ( child_no == 2 )
        {
            sub_extent = GeoExtent(
                             extent.getXMin(),
                             centroid.y(),
                             centroid.x(),
                             extent.getYMax(),
                             extent.getSRS() );
        }
        else if ( child_no == 3 )
        {
            sub_extent = GeoExtent(
                             centroid.x(),
                             centroid.y(),
                             extent.getXMax(),
                             extent.getYMax(),
                             extent.getSRS() );
        }
        else if ( child_no == 0 )
        {
            sub_extent = GeoExtent(
                             extent.getXMin(),
                             extent.getYMin(),
                             centroid.x(),
                             centroid.y(),
                             extent.getSRS() );
        }
        else if ( child_no == 1 )
        {
            sub_extent = GeoExtent(
                             centroid.x(),
                             extent.getYMin(),
                             extent.getXMax(),
                             centroid.y(),
                             extent.getSRS() );
        }
    }

    return sub_extent;
}