Exemplo n.º 1
0
bool SelectionBase::ExpressionPart::match(const SPFeatureI &feature,SelectionBase *operation) const
{
    if ( _type == ExpressionPart::ptFEATURETYPE){
        return hasType(feature->geometryType(), _geometryType);
    }
    if ( _type == ExpressionPart::ptENVELOPE && _envelope.isValid())   {
        return _envelope.contains(feature->geometry().get());
    }
    if ( _type == ExpressionPart::ptPOLYGON && _polygon.get() != 0)   {
        return _polygon->contains(feature->geometry().get());
    }

    if ( _type == ExpressionPart::ptATTRIBUTESELECTION )   {
        QVariant val = feature(_leftSide);
        ColumnDefinition coldef = feature->attributedefinition(_leftSide);
        if ( coldef.datadef().domain()->ilwisType() == itITEMDOMAIN){
            val = coldef.datadef().domain()->impliedValue(val);
        }
        if ( QString(val.typeName()) == "QString" && QString(_rightSide.typeName()) == "QString"){
            return operation->compare1(_operator,val.toString(), _rightSide.toString());
        } else {
            bool ok1,ok2;
            double v1 = val.toDouble(&ok1);
            double v2 = _rightSide.toDouble(&ok2);
            bool ok3 = operation->compare2(_operator, v1, v2, true);
            return ok1&& ok2 && ok3;
        }
    }
    return false;
}
Exemplo n.º 2
0
FeatureDrawing SimplePointSetter::setSpatialAttributes(const SPFeatureI &feature, QVector<QVector3D> &vertices, QVector<QVector3D> &) const
{
    Envelope env = _rootDrawer->attribute("coverageenvelope").value<Envelope>();
    double size = env.xlength() / 150.0;
    const UPGeometry& geometry = feature->geometry();
    int n = geometry->getNumGeometries();
    FeatureDrawing drawing(itPOINT);
    for(int  geom = 0; geom < n; ++geom ){
        const geos::geom::Geometry *subgeom = geometry->getGeometryN(geom);
        if (!subgeom)
            continue;
        const geos::geom::Coordinate *crd = subgeom->getCoordinate();
        Coordinate coord = *crd;
        if ( coordinateConversionNeeded()){
            coord = _targetSystem->coord2coord(_sourceSystem, *crd);
        }

        drawing._indices.push_back(VertexIndex(vertices.size(),5,GL_LINE_STRIP,feature->featureid()));
        double z = coord.z == rUNDEF || std::isnan(coord.z)? 0 : coord.z;
        vertices.push_back(QVector3D(coord.x + size, coord.y + size, z));
        vertices.push_back(QVector3D(coord.x - size, coord.y + size, z));
        vertices.push_back(QVector3D(coord.x - size, coord.y - size, z));
        vertices.push_back(QVector3D(coord.x + size, coord.y - size, z));
        vertices.push_back(QVector3D(coord.x + size, coord.y + size, z));
        drawing._center = QVector3D(coord.x, coord.y, coord.z);
    }
    return drawing;

}
bool PostgresqlFeatureCoverageLoader::loadData(FeatureCoverage *fcoverage) const
{
    //qDebug() << "PostgresqlFeatureCoverageLoader::loadData()";

    ITable table;
    PostgresqlDatabaseUtil pgUtil(_resource,_options);
    Resource tableResource = pgUtil.resourceForType(itFLATTABLE);
    table.prepare(tableResource, _options);

    PostgresqlTableLoader tableLoader(table->source(), _options);
    if (!tableLoader.loadData(table.ptr())) {
        ERROR1("Could not load table data for table '%1'", table->name());
        return false;
    }

    // metadata already set it to correct number, creating new features will up the count agains; so reset to 0.
    fcoverage->setFeatureCount(itFEATURE, iUNDEF, FeatureInfo::ALLFEATURES);

    QList<MetaGeometryColumn> metaGeometries;
    pgUtil.getMetaForGeometryColumns(metaGeometries);
    QSqlQuery query = pgUtil.doQuery(selectGeometries(metaGeometries), "featurecoverageloader");
    quint32 geometriesPerFeature = metaGeometries.size();

    IDomain semantics;
    pgUtil.prepareSubFeatureSemantics(semantics, metaGeometries);

    while (query.next()) {
        if (geometriesPerFeature == 0) {
            fcoverage->newFeature(0);
        } else {
            // index 0 is root, indeces > 0 are subfeatures of root
            bool atRoot = true;
            SPFeatureI rootFeature;
            // iterate semantics to keep predefined order
            ItemRangeIterator iter(semantics->range<>().data());
            while (iter.isValid()) {
                QString geomName = (*iter)->name();
                ICoordinateSystem crs;
                std::for_each(metaGeometries.begin(), metaGeometries.end(), [&crs,geomName](MetaGeometryColumn c) {
                    if (c.geomColumn == geomName) {
                        crs = c.crs;
                    }
                });
                if (atRoot) {
                    atRoot = false;
                    geos::geom::Geometry *rootGeometry = createGeometry(query, geomName, crs);
                    rootFeature = fcoverage->newFeature(rootGeometry, false);
                } else {
                    geos::geom::Geometry *subGeometry = createGeometry(query, geomName, crs);
                    rootFeature->createSubFeature(geomName,subGeometry);
                }
                ++iter;
            }
        }
    }
    fcoverage->attributesFromTable(table);

    return true;
}
Exemplo n.º 4
0
FeatureDrawing SimplePolygonSetter::setSpatialAttributes(const SPFeatureI &feature, QVector<QVector3D> &vertices, QVector<QVector3D> &normals) const
{
    IlwisTesselator tesselator;
    const UPGeometry& geometry = feature->geometry();
    int n = geometry->getNumGeometries();
    FeatureDrawing drawing(itPOLYGON);
    for(int  geom = 0; geom < n; ++geom ){
        const geos::geom::Geometry *subgeom = geometry->getGeometryN(geom);
        if (!subgeom)
            continue;
        tesselator.tesselate(_targetSystem,_sourceSystem, subgeom,feature->featureid(), vertices, drawing._indices);
    }
    return drawing;
}
bool FeatureConnector::loadBinaryPolygons30(FeatureCoverage *fcoverage, ITable& tbl) {
    BinaryIlwis3Table polTable;
    if ( !polTable.load(_odf)) {
        return ERROR1(ERR_COULD_NOT_OPEN_READING_1,_odf->fileinfo().fileName())    ;
    }

    BinaryIlwis3Table topTable;
    if ( !topTable.load(_odf,"top")) {
        return ERROR1(ERR_COULD_NOT_OPEN_READING_1,_odf->fileinfo().fileName())    ;
    }

    qint32 colValue = polTable.index("PolygonValue");
    qint32 colTopStart = polTable.index("TopStart");
    qint32 colArea = polTable.index("Area");
    int nrPolygons = polTable.rows();
    bool isNumeric = _odf->value("BaseMap","Range") != sUNDEF;

    double v;
    for(int i = 0; i < nrPolygons; ++i) {
        polTable.get(i,colArea, v);
        if ( v < 0)
            continue;
        polTable.get(i,colTopStart,v);
        qint32 index = v;
        std::vector<std::vector<Coordinate2d>> rings;
        if (getRings(index, topTable, polTable, rings)) {
            if ( rings.size() == 0)
                continue;
            Polygon polygon;
            polygon.outer().resize(rings[0].size());
            std::copy(rings[0].begin(), rings[0].end(), polygon.outer().begin());
            for(int j = 1; j < rings.size(); ++j) {
                polygon.inners()[j-1].resize(rings[j].size());
                std::copy(rings[j].begin(), rings[j].end(), polygon.inners()[j-1].begin());
            }
            polTable.get(i, colValue, v);
            if ( isNumeric) {
                tbl->cell(COVERAGEKEYCOLUMN, i, QVariant(i));
                tbl->cell(FEATUREVALUECOLUMN, i, QVariant(v));
                fcoverage->newFeature({polygon});
            } else {
                quint32 itemId = v;
                tbl->cell(COVERAGEKEYCOLUMN, i, QVariant(itemId));
                SPFeatureI feature = fcoverage->newFeature({polygon});
                tbl->cell(FEATUREIDCOLUMN, i, QVariant(feature->featureid()));
            }
        }
    }
    return true;
}
bool GdalFeatureConnector::store(IlwisObject *obj, const IOOptions&   )
{

    if (!loadDriver())
        return false;

    IFeatureCoverage coverage;
    coverage.set(static_cast<FeatureCoverage *>(obj));

    std::vector<SourceHandles> datasources(3);
    std::vector<bool> validAttributes;
    if(!setDataSourceAndLayers(coverage, datasources, validAttributes))
        return false;

    std::vector<ColumnDefinition> defs;
    for(int i=0; i < coverage->attributeTable()->columnCount(); ++i){
        defs.push_back(coverage->attributeTable()->columndefinition(i));
    }

    FeatureIterator fiter(coverage);
    FeatureIterator endIter = end(coverage);
    for(; fiter != endIter; ++fiter) {
        SPFeatureI feature = *fiter;
        IlwisTypes geomType = feature->geometryType();
        if ( geomType != 0){
            OGRLayerH lyr = datasources[ilwisType2Index(geomType)]._layers[0];
            OGRFeatureH hfeature = gdal()->createFeature(gdal()->getLayerDef(lyr));
            if (hfeature) {
                setAttributes(hfeature, feature, validAttributes, defs);
                const geos::geom::Geometry* geometry = feature->geometry().get();
                if (gdal()->setGeometryDirectly(hfeature,createFeature(geometry)) != OGRERR_NONE)
                    ERROR2(ERR_COULD_NOT_ALLOCATE_2, TR("geometry"), _filename.toString());
                if (gdal()->addFeature2Layer(lyr, hfeature) != OGRERR_NONE) {
                    ERROR2(ERR_COULD_NOT_ALLOCATE_2, TR("feature"), _filename.toString());
                }
                gdal()->destroyFeature(hfeature);
            };
        }
    }
    for(auto& datasource : datasources){
        if ( datasource._source != 0)
            gdal()->destroyDataSource(datasource._source);
    }

    return true;

}
bool FeatureConnector::loadBinaryPolygons37(FeatureCoverage *fcoverage, ITable& tbl) {
    QString datafile = _odf->value("PolygonMapStore","DataPol");
    datafile = context()->workingCatalog()->filesystemLocation().toLocalFile() + "/" + datafile;
    QFile file(datafile);

    if (!file.exists()){
        kernel()->issues()->log(TR(ERR_MISSING_DATA_FILE_1).arg(file.fileName()));
        return false;
    }
    if(!file.open(QIODevice::ReadOnly )){
        kernel()->issues()->log(TR(ERR_COULD_NOT_OPEN_READING_1).arg(file.fileName()));
        return false;
    }
    QDataStream stream(&file);
    int nrPolygons = fcoverage->featureCount(itPOLYGON);
    SPAttributeRecord record( new AttributeRecord(tbl,FEATUREIDCOLUMN));
    bool isNumeric = _odf->value("BaseMap","Range") != sUNDEF;

    for(int j=0; j < nrPolygons; ++j) {
        Polygon pol;
        readRing(stream, pol.outer());
        double value;
        quint32 numberOfHoles;
        stream.readRawData((char *)&value, 8);
        stream.readRawData((char *)&numberOfHoles, 4);
        pol.inners().resize(numberOfHoles);
        for(quint32 i=0; i< numberOfHoles;++i)
            readRing(stream, pol.inners()[i]);
        if ( isNumeric) {
            tbl->cell(COVERAGEKEYCOLUMN, j, QVariant(j));
            tbl->cell(FEATUREVALUECOLUMN, j, QVariant(value));
            SPFeatureI feature = fcoverage->newFeature({pol});
            tbl->cell(FEATUREIDCOLUMN, j, QVariant(feature->featureid()));
        } else {
            quint32 itemId = value;
            tbl->cell(COVERAGEKEYCOLUMN, j, QVariant(itemId));
            SPFeatureI feature = fcoverage->newFeature({pol});
            tbl->cell(FEATUREIDCOLUMN, j, QVariant(feature->featureid()));
        }

    }
    file.close();

    return true;
}
Exemplo n.º 8
0
SPFeatureI FeatureCoverage::newFeatureFrom(const SPFeatureI& existingFeature) {
    Locker lock(_mutex);
    if ( existingFeature.isNull() || existingFeature->isValid() == false)
        return SPFeatureI();
    SPFeatureI newFeature = new Feature(this);
    for(int i=0; i < existingFeature->trackSize(); ++i)
        newFeature->set(existingFeature->geometry(),i);
    _features.push_back(newFeature);
    quint32 cnt = featureCount(newFeature->ilwisType());
    setFeatureCount(newFeature->ilwisType(),++cnt );
    return _features.back();
}
void GdalFeatureConnector::setAttributes(OGRFeatureH hfeature, SPFeatureI& feature, const std::vector<bool>& validAttributes, const std::vector<ColumnDefinition>& columnDef) {
    int index = 0;
    for(int i=0; i < feature->attributeColumnCount(); ++i){
        if ( !validAttributes[i])
            continue;
        IDomain dom = columnDef[i].datadef().domain<>();
        if(hasType(dom->valueType(),itINTEGER)) {
            gdal()->setIntegerAttribute(hfeature,index,feature->cell(i).toInt());
        } else if (hasType(dom->valueType(),itDOUBLE | itFLOAT)) {
            gdal()->setDoubleAttribute(hfeature,index,feature->cell(i).toDouble());
        } else if (hasType(dom->valueType(),itTHEMATICITEM | itNAMEDITEM | itINDEXEDITEM | itNUMERICITEM | itTIMEITEM)) {
            gdal()->setStringAttribute(hfeature,index,dom->impliedValue(feature->cell(i)).toString().toLocal8Bit());
        } else if (hasType(dom->valueType(), itDATETIME)) {
            QVariant v = feature->cell(i);
            if ( QString(v.typeName()).compare("Ilwis::Time") != 0){
                ERROR2(ERR_COULD_NOT_CONVERT_2,v.toString(), "time");
                gdal()->setDateTimeAttribute(hfeature,index,0,0,0,0,0,0,0);
            }else{
                Time time = v.value<Ilwis::Time>();
                gdal()->setDateTimeAttribute(hfeature,index,
                                             time.get(Time::tpYEAR),
                                             time.get(Time::tpMONTH),
                                             time.get(Time::tpDAYOFMONTH),
                                             time.get(Time::tpHOUR),
                                             time.get(Time::tpMINUTE),
                                             time.get(Time::tpSECOND),
                                             0);//TODO TimeZone??
            }
        } else if (hasType(dom->valueType(),itSTRING)){
            gdal()->setStringAttribute(hfeature,index,feature->cell(i).toString().toLocal8Bit());
        }
        ++index;
    }

}
bool FeatureConnector::loadBinarySegments(FeatureCoverage *fcoverage) {
    BinaryIlwis3Table mpsTable;
    if ( !mpsTable.load(_odf)) {
        return ERROR1(ERR_COULD_NOT_OPEN_READING_1,_odf->fileinfo().fileName())    ;
    }
    int colCoords = mpsTable.index("Coords");
    int colItemId = mpsTable.index("SegmentValue");
    bool isNumeric = _odf->value("BaseMap","Range") != sUNDEF;
    ITable tbl = fcoverage->attributeTable();
//    if ( isNumeric) // in other case nr of record already has been set as it is based on a real table
//        tbl->setRows(mpsTable.rows());

    double value;
    for(quint32 i= 0; i < mpsTable.rows(); ++i) {
        std::vector<Coordinate > coords;
        mpsTable.get(i,colCoords,coords);
        Line2D<Coordinate2d > line;
        line.resize(coords.size());
        std::copy(coords.begin(), coords.end(), line.begin());
        mpsTable.get(i, colItemId,value);
        if ( isNumeric) {
            tbl->cell(COVERAGEKEYCOLUMN, i, QVariant(i));
            tbl->cell(FEATUREVALUECOLUMN, i, QVariant(value));
            SPFeatureI feature = fcoverage->newFeature({line});
            tbl->cell(FEATUREIDCOLUMN, i, QVariant(feature->featureid()));

        } else {
            quint32 itemId = value;
            tbl->cell(COVERAGEKEYCOLUMN, i, QVariant(itemId));
            SPFeatureI feature = fcoverage->newFeature({line});
            tbl->cell(FEATUREIDCOLUMN, i, QVariant(feature->featureid()));
        }


    }
    return true;


}
Exemplo n.º 11
0
void Polygon2Line::extractBoundary(const UPGeometry& g, quint32& record){
        const geos::geom::Geometry *geom1 = g.get();
        std::vector<geos::geom::CoordinateSequence *> coords = GeometryHelper::geometry2coords(geom1);
        geos::geom::Geometry *geometry;
        if ( coords.size() == 1)
            geometry = _outputfeatures->geomfactory()->createLineString(coords[0]);

        else if ( coords.size() > 1){
            std::vector<geos::geom::Geometry *> lines;
            for(int i=0; i < coords.size(); ++i)
                lines.push_back(_outputfeatures->geomfactory()->createLineString(coords[i]));
            geometry = _outputfeatures->geomfactory()->createMultiLineString(lines);
        }

        if ( geometry){
            SPFeatureI outfeature = _outputfeatures->newFeature(geometry);
            if (_singleId){
                outfeature->setCell(0,0);
            }else {
                outfeature->setCell(0, record);
            }
        }
}
Exemplo n.º 12
0
SPFeatureI FeatureCoverage::newFeatureFrom(const SPFeatureI& existingFeature, const ICoordinateSystem& csySource) {
    Locker<> lock(_mutex);

    auto transform = [&](const UPGeometry& geom)->geos::geom::Geometry * {
        if ( geom.get() == 0)
            return 0;

        geos::geom::Geometry *newgeom = geom->clone();
        if ( csySource.isValid() && !csySource->isEqual(coordinateSystem().ptr())){
            CsyTransform trans(csySource, coordinateSystem());
            newgeom->apply_rw(&trans);
            newgeom->geometryChangedAction();
        }
        GeometryHelper::setCoordinateSystem(newgeom, coordinateSystem().ptr());

        return newgeom;
    };
    if (!connector()->dataIsLoaded()) {
        connector()->loadData(this);
    }
    auto *newfeature = createNewFeature(existingFeature->geometryType());
    const UPGeometry& geom = existingFeature->geometry();
    newfeature->geometry(transform(geom)) ;

    auto variantIndexes = _attributeDefinition.indexes();
    for(auto index : variantIndexes){
        const auto& variant = existingFeature[index];
        auto *variantFeature = createNewFeature(variant->geometryType());
        const auto& geom = variant->geometry();
        variantFeature->geometry(transform(geom)) ;
        newfeature->setSubFeature(index, variantFeature);


    }
    _features.push_back(newfeature);
    return _features.back();
}
bool FeatureConnector::loadBinaryPoints(FeatureCoverage *fcoverage) {
    BinaryIlwis3Table mppTable;
    if ( !mppTable.load(_odf)) {
        return ERROR1(ERR_COULD_NOT_OPEN_READING_1,_odf->fileinfo().fileName())    ;
    }
    // two cases; the old case; 2 columns for x and y. and the new case one column for coord
    int coordColumnX = mppTable.index("x");
    int coordColumnY = mppTable.index("y");
    int coordColumn = mppTable.index("Coordinate");
    int colItemId = mppTable.index("Name");

    ITable tbl = fcoverage->attributeTable();
    bool newCase =  coordColumnX == iUNDEF;

    for(quint32 i= 0; i < mppTable.rows(); ++i) {
        Coordinate c;
        double itemIdT;
        if ( newCase) {
            mppTable.get(i, coordColumn, c);
        } else {
            double x,y;
            mppTable.get(i, coordColumnX, x);
            mppTable.get(i, coordColumnY, y);
            c = Coordinate(x,y);
        }
        mppTable.get(i, colItemId,itemIdT);
        quint32 itemId = itemIdT;
        tbl->cell(COVERAGEKEYCOLUMN, i, QVariant(itemId));

        SPFeatureI feature = fcoverage->newFeature({c});

        tbl->cell(FEATUREIDCOLUMN, i, QVariant(feature->featureid()));

    }
    return true;
}
bool PostgresqlFeatureCoverageLoader::storeData(FeatureCoverage *fcoverage) const
{
    bool queryOk = true;
    ITable baseData = fcoverage->attributeTable();
    PostgresqlParameters params (_resource.url(true).toString());
    PostgresqlDatabaseUtil pgUtil(params);
    SqlStatementHelper sqlHelper(params);
   bool newTable = false;
   if ( !pgUtil.tableExists()){
       if(!pgUtil.createTable(fcoverage)){
           return false;
       }
       newTable = true;
   }

   IDomain semantics; // subfeature semantics
    QList<QString> primaryKeys; // readonly keys
    QList<MetaGeometryColumn> metaGeomColumns; // geometry columns
    pgUtil.getMetaForGeometryColumns(metaGeomColumns);
    pgUtil.prepareSubFeatureSemantics(semantics, metaGeomColumns, _options);
    pgUtil.getPrimaryKeys(primaryKeys);
    // add geoms to update/insert data table
    FeatureIterator featureIter(fcoverage);
    featureIter.flow(FeatureIterator::fDEPTHFIRST);

    QString columnNamesCommaSeparated = sqlHelper.columnNamesCommaSeparated(baseData.ptr());

    QString code = fcoverage->coordinateSystem()->code();
    QString srid = code.right(code.indexOf(":"));
    QString qtablename = params.table().toLower();
    while(featureIter != featureIter.end()) {
        SPFeatureI feature = (*featureIter);
        bool newFeature = newTable ? true : !pgUtil.exists(feature);
        QString columnValuesCommaSeparated = sqlHelper.columnValuesCommaSeparated(feature);
        QString sqlStmt;
        if (newFeature) {
            sqlStmt = "INSERT INTO ";
            sqlStmt.append(qtablename);


            sqlStmt.append(" ( ");
            sqlStmt.append(columnNamesCommaSeparated);

            foreach (MetaGeometryColumn geomMeta, metaGeomColumns) {
                sqlStmt.append(", ");
                sqlStmt.append(geomMeta.geomColumn);
            }

            sqlStmt.append(" ) ");
            sqlStmt.append(" VALUES ( ");
            sqlStmt.append(columnValuesCommaSeparated);
            sqlStmt.append(", ");

            // add geometry values

            /*
            // TODO storing level n data only makes sense
            //      if subfeatures are stored in separate
            //      tables related via foreign keys, but
            //      separate tables aren't supported now

            ITabel levelData = fcoverage->attributeTable(level);
            PostgresqlTableConnector connector(_resource,false);
            tableOk = connector.store(levelData.ptr(), options);
            */

            QString rootGeomColumn = fcoverage->attributeDefinitionsRef().index((quint32)0);
            // have to iterate the order of given geom columns
            foreach (MetaGeometryColumn geomMeta, metaGeomColumns) {
                QString wkt = "NULL";
                QString geomColumn = geomMeta.geomColumn;
                if (rootGeomColumn == geomColumn || rootGeomColumn == sUNDEF) {
                    if (feature->geometry() != nullptr){
                        wkt = QString("'%1'").arg(GeometryHelper::toWKT(feature->geometry().get()));
                    }
                } else if (feature[geomColumn]->geometry() != nullptr){
                    // access subfeature geometries
                    SPFeatureI subfeature = feature[geomColumn];
                    wkt = QString("'%1'").arg(GeometryHelper::toWKT(subfeature->geometry().get()));
                }
                sqlStmt.append("st_geomfromtext(").append(wkt);
                sqlStmt.append(", ").append(srid).append(")");
                sqlStmt.append(",");

                // skip subfeature's geometries, as accessed already via domain
                ++featureIter;
            }

            sqlStmt = sqlHelper.trimAndRemoveLastCharacter(sqlStmt);


            sqlStmt.append(" ); ");
        } else {
Exemplo n.º 15
0
void FeatureCoverageTest::testVariants() {
    Ilwis::IFeatureCoverage featureCoverage;
       featureCoverage.prepare();

       Ilwis::ICoordinateSystem csy("code=proj4:+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");

       Ilwis::Envelope env(Ilwis::LatLon(50,-30), Ilwis::LatLon(-50, 60));
       featureCoverage->coordinateSystem(csy);
       featureCoverage->envelope(env);

       Ilwis::ITimeDomain dom;
       dom.prepare();
       dom->range(new Ilwis::TimeInterval("20090101", "20110101"));
       std::vector<QString> times = {"20090101","20090131","20090602","20090703", "20100109","20110101"};

       featureCoverage->attributeDefinitionsRef().addColumn("population", "value");
       featureCoverage->attributeDefinitionsRef().setSubDefinition(dom,times);

       Ilwis::SPFeatureI feature1 = featureCoverage->newFeature(0);

       geos::geom::Geometry *geom = Ilwis::GeometryHelper::fromWKT("Linestring(40 20, 10 12, -20 -10)", featureCoverage->coordinateSystem().ptr());
       feature1->geometry(geom);
       feature1("population", 200);

       geom = Ilwis::GeometryHelper::fromWKT("Linestring(40 30, 10 12, -20 -10)", featureCoverage->coordinateSystem().ptr());
       Ilwis::SPFeatureI feature2 = featureCoverage->newFeature(geom);
       feature2("population", 300);

       geom = Ilwis::GeometryHelper::fromWKT("Linestring(40 30, 12 12, -20 -10)", featureCoverage->coordinateSystem().ptr());
       Ilwis::SPFeatureI feature3 = featureCoverage->newFeature(geom);
       feature3("population", 100);
    feature1->createSubFeature("20090101", geom);
    feature1->createSubFeature("20090703", geom);

       geom = Ilwis::GeometryHelper::fromWKT("Linestring(30 10, 10 15, -23 -12)",featureCoverage->coordinateSystem().ptr());

       feature1->createSubFeature("20090101", geom);

       geom = Ilwis::GeometryHelper::fromWKT("Linestring(20 10, 10 15, -23 -12)",featureCoverage->coordinateSystem().ptr());

       feature2->createSubFeature("20090101", geom);

       auto *attrdef = new Ilwis::FeatureAttributeDefinition();
       attrdef->addColumn("temperature", "value");
       featureCoverage->attributeDefinitionsRef().featureAttributeDefinition(attrdef);

       feature1["20090101"]("temperature", 26.5);
       feature2["20090101"]("temperature", 19.5);


    FeatureIterator featureIter(featureCoverage);
    featureIter.flow(FeatureIterator::fDEPTHFIRST);

    while(featureIter != featureIter.end()) {
        SPFeatureI f = (*featureIter);
        f->geometry(); // test if geometry is null
        DOTEST(true, "geometry of (sub)feature not null.");
        featureIter++; // move to next
    }






}