Writing a New Reader
In this section, you will find an example of an IlvMapFeatureIterator that you can use to read polylines that were saved in an ASCII file.
Note
The classes that implement the IlvMapFeatureIterator abstract class are not necessarily file readers. They can also iterate, for example, the result of a query to a map server. |
The File to Be Read
The ASCII file to be read has been created especially for this example. Its format is very simple and its specifications are as follows:
-
It has a header specifying its format.
-
There is one pair of coordinates (latitude and longitude) per line. These coordinates are expressed in degrees.
-
Lines can contain comments. These comments, when they exist, are merged to form an attribute.
-
Polylines are separated by a blank line.
-
The file has the .pol extension.
The ASCII file is shown below:
#ascii polylines
-1.0 40.0 A 1x1 degree rectangle centered on the
1.0 40.0 (0,39) point
1.0 38.0
-1.0 38.0
-1.0 40.0
0.0 90.0 A meridian extending from the North pole to the South pole
0.0 -90.0
The Reader
This section shows how you can use the reader to read this polyline file.
The complete source code for this example can be found in the following file:
<installdir>/samples/maps/userman/src/simplePolylineReader.cpp
Note
Only the portions of code that require explanation are here. |
As shown below, the SimplePolylineReader implements the IlvMapFeatureIterator abstract class:
#ifdef IL_STD
#include <fstream>
using namespace std;
#else
#include <fstream.h>
#endif
#include <ilviews/maps/mapfeature.h>
#include <ilviews/maps/format/mapinput.h>
#include <ilviews/maps/rendering/curverdr.h>
class ILVMAPSEXPORTED SimplePolylineReader
:public IlvMapFeatureIterator
{
public:
SimplePolylineReader(IlvDisplay* display,
const char* filename);
virtual ~SimplePolylineReader();
virtual IlvMapsError getInitStatus() const;
virtual const IlvMapFeature* getNextFeature(IlvMapsError& status);
virtual IlvBoolean getLowerRightCorner(IlvCoordinate& coordinate) const;
virtual IlvBoolean getUpperLeftCorner(IlvCoordinate&) const;
IlvFeatureRenderer* getDefaultFeatureRenderer(IlvDisplay *);
const IlvProjection* getProjection() const;
IlvBoolean emptyLine(const char* line);
IlvBoolean readHeader(const char* line);
int parseLine(const char* line,
IlvDouble* lng,
IlvDouble* lat,
char* comment);
IlvFeatureAttributeProperty* attributes(IlString& buffer,
IlvMapsError& status);
IlvMapFeatureIteratorDeclareClassInfo();
private:
static IlvMapsError _formatError;
ifstream _stream;
IlvMapLineString* _geometry;
IlvMapFeature* _feature;
IlvProjection* _projection;
IlvDisplay* _display;
IlvMapsError _status;
static IlvMapsError FormatError();
static void Init();
const IlvMapFeature* readPolyline(IlvMapsError& status);
};
The IlvClassInfo Registration
The IlvClassInfo registration is needed to determine the class of an IlvMapFeatureIterator (like the one returned by the makeFeatureIterator method). To register a new reader class, use the predefined macro:
IlvMapFeatureIteratorDefineClassInfo(className,superClassName);
where className is the name of the new reader class (SimplePolygonReader in the present example) and superClassName is the name of the superclass (IlvMapFeatureIterator the present example).
The IlvMapFeatureIterator class can then be checked in this way:
IlvMapFeatureIterator* reader;
......
IlvClassInfo* readerClass = reader->getClassInfo();
if (readerClass->isSubtypeOf(SimplePolygonReader::ClassInfo())) {
...
}
The Georeferencing Methods
Since the latitude and the longitude in the polyline file are expressed in degrees, we know that the projection is geographic. This is why the getProjection method returns IlvGeographicProjection. The method getProjection would return null if the projection of the file to be read was unknown.
SimplePolylineReader::SimplePolylineReader(IlvDisplay* display, const char* filename) :_display(display), _status(IlvMaps::NoError()), _stream(filename), _geometry(0), _feature(0), _projection(new IlvGeographicProjection()) { char line[1024]; _stream.getline(line, 1024); if(readHeader(line) == IlvFalse) _status = _formatError; } const IlvProjection* SimplePolylineReader::getProjection() const { return _projection; } |
Bounding Box Methods
Because of the data format, we cannot retrieve the bounding box of the polyline until the whole data has been read. Here, the methods getUpperLeftCorner and getLowerRightCorner return IlvFalse to indicate that these points are not known. Another option we could have taken is to read the whole data, place it in an array, and then compute the bounding box.
IlvBoolean SimplePolylineReader::getLowerRightCorner(IlvCoordinate& c) const { return IlvFalse; } IlvBoolean SimplePolylineReader::getUpperLeftCorner(IlvCoordinate& c) const { return IlvFalse; } |
Rendering Methods
The getDefaultFeatureRenderer method must return a renderer able to transform into graphic objects all the map features read by this feature iterator. The IlvDefaultCurveRenderer can process map features whose geometry is of type IlvMapLineString.
IlvFeatureRenderer* SimplePolylineReader::getDefaultFeatureRenderer(IlvDisplay *display) { return new IlvDefaultCurveRenderer(display); } |
If the geometries of the returned map features are not predefined, but instead, are instances of a derived class, or, if the map feature attributes store drawing parameters to be used in rendering operations such as color or line width, it is necessary to provide renderers whose rendering styles that can process these attributes or derived geometries. See the section Creating a Colored Line Renderer.
The getNextFeature Method
The IlvMapFeatureIterator::getNextFeature method reads the geometry of a map feature and creates an IlvMapFeature object that will hold all the information required to process the geometry. The geometry read in the code example that follows is an IlvMapLineString, which is the class to define polyline geometries.
const IlvMapFeature* SimplePolylineReader::getNextFeature(IlvMapsError& status) { return readPolyline(status); } |
The polyline points are read by the private method readPolyline. This method reads each line in the file to extract the coordinates of the points and the related comments, if any.
It is broken up as follows:
A geometry of the type IlvMapLineString is created or reset, which will be associated with the map feature. Notice that, to improve performance, the reader always returns the same instance of IlvMapFeature The geometry will also be allocated only once—when these points are read for the first time, and emptied at each call of getNextFeaure. The map feature returned by the getNextFeature method is volatile, meaning that its geometry and attributes must be used before the method is called again. All the readers provided in the Views Maps library that implement the IlvMapFeatureIterator interface work this way.
const IlvMapFeature* SimplePolylineReader::readPolyline(IlvMapsError& status) { if(_stream.eof()) return 0; if(!_geometry) _geometry = new IlvMapLineString(); else _geometry->removeAll(); if(!_feature) { _feature = new IlvMapFeature(); _feature->setGeometry(_geometry); const IlvProjection* proj = getProjection(); if(proj) _feature->setProjection(proj->copy()); } |
The line then is read. If end of file is reached, or if an empty line is reached, we return the last feature read.
char line[1024]; int first = 1; IlString buffer; IlvCoordinate c; while (1) { _stream.getline(line, 1024); if(_stream.eof() || emptyLine(line)) { status = IlvMaps::NoError(); IlvFeatureAttributeProperty* prop = attributes(buffer, status); if(status != IlvMaps::NoError()) IlvWarning(IlvMaps::GetErrorMessage(status, _display)); _feature->setAttributes(prop); buffer = 0; return _feature; } |
In the following code, the longitude and latitude points are read with the parseLine method. If a comment is found, it is added to the comment buffer. This buffer is processed by the attributes method described later.
double x, y; char comment[1024]; int i = parseLine(line, &x, &y, comment); if(i < 2) { status = _formatError; return 0; } c.x(x); c.y(y); if(i == 3) buffer += comment; |
Each point read from the file is added to the line string geometry.
if(first) { first = 0; _geometry->setStartPoint(c); } else { _geometry->lineTo(c); } } } |
The comments are extracted to form attributes, which are associated with the map feature. This is the attributes method.
IlvFeatureAttributeProperty* SimplePolylineReader::attributes(IlString& buffer, IlvMapsError& status) { if(buffer.getLength() == 0) return 0; IlvUInt count = 1; const char* name = "Comment"; const IlvMapClassInfo* attClass = IlvStringAttribute::ClassInfo(); const IlvBoolean nullable = IlvTrue; IlvFeatureAttributeInfo* info = new IlvFeatureAttributeInfo( count, &name, &attClass, &nullable); const char* val = buffer.getValue(); IlvStringAttribute* att = new IlvStringAttribute(); att->setValue(val); IlvFeatureAttributeProperty* prop = new IlvFeatureAttributeProperty(info, (IlvFeatureAttribute**)&att, status); return prop; } |