Goal
When the user makes changes to the data, those modifications often need to be saved, to disk or to a database for example.
The goal of this tutorial is to illustrate how you can save the contents of an ILcdModel
.
For this purpose, the tutorial uses the custom data format introduced in the
Support a custom vector format tutorial.
The LuciadLightspeed API uses the concept of an ILcdModelEncoder
to save
the contents of an ILcdModel
.
An ILcdModelEncoder
offers two methods for encoding an ILcdModel
:
-
save
: this method encodes theILcdModel
to its native format.For example, if you use this method for SHP data with an encoder capable of writing SHP files, it will write the model contents to a SHP file.
The models for which an encoder is available in the LuciadLightspeed API typically expose their native encoder through the
ILcdModel.getModelEncoder()
method.For example:
TLcdCompositeModelDecoder decoder = new TLcdCompositeModelDecoder( TLcdServiceLoader.getInstance(ILcdModelDecoder.class) ); ILcdModel model = decoder.decode(pathToGeoJsonFile); // Make some changes to the model // For example add, remove or update elements // Save those changes back to disk ILcdModelEncoder modelEncoder = model.getModelEncoder(); if (modelEncoder != null && modelEncoder.canSave(model)) { //This will override the file located at 'pathToGeoJsonFile' modelEncoder.save(model); } else { // this kind of model can't be saved back }
-
export
: this method encodes theILcdModel
to a specific destination. This is similar to the "Save As" function in many applications.The
export
method can also accept models with a different native format, similar to the "Export" function in many applications.For example, it can be used to write SHP data to a GeoJSON file:
//Decode a .shp file TLcdCompositeModelDecoder decoder = new TLcdCompositeModelDecoder( TLcdServiceLoader.getInstance(ILcdModelDecoder.class) ); ILcdModel model = decoder.decode(pathToSHPFile); TLcdGeoJsonModelEncoder geoJsonModelEncoder = new TLcdGeoJsonModelEncoder(); if (geoJsonModelEncoder.canExport(model, geoJsonOutputPath)) { //This will create a .geojson file at 'geoJsonOutputPath' geoJsonModelEncoder.export(model, geoJsonOutputPath); } else { // can happen, but probably not in this case }
In this tutorial, vector data is used, but the same concepts apply to raster data, and you can use the same API. |
Creating the model encoder
The model encoder we create in this tutorial has the following properties:
-
It supports waypoint models only.
-
It does not support saving waypoint models to another data format.
We start by creating a new class that implements the ILcdModelEncoder
interface:
public class WayPointsModelEncoder implements ILcdModelEncoder {
}
Similar to an ILcdModelDecoder
, a model encoder also has a display name.
The display name is a human-readable name for the format that the model encoder can encode:
@Override
public String getDisplayName() {
return "Way Points";
}
We start by writing the export
functionality of the model encoder.
Each model encoder has a canExport
method that indicates whether the encoder can handle the ILcdModel
.
In this case, we only support waypoint models that are stored to a waypoint file:
@Override
public boolean canExport(ILcdModel aModel, String aDestinationName) {
return isWaypointModel(aModel) && isValidDestinationName(aDestinationName);
}
private boolean isWaypointModel(ILcdModel aModel) {
return "CWP".equals(aModel.getModelDescriptor().getTypeName());
}
private boolean isValidDestinationName(String aTargetName) {
return aTargetName != null && aTargetName.endsWith(".cwp");
}
The export
method loops over each element, and writes them to the file:
@Override
public void export(ILcdModel aModel, String aDestinationName) throws IllegalArgumentException, IOException {
if (!canExport(aModel, aDestinationName)) {
throw new IllegalArgumentException(String.format("Cannot export %s to %s", aModel.getModelDescriptor().getDisplayName(), aDestinationName));
}
//Loop over all elements, and write them to the file one by one
TLcdFileOutputStreamFactory outputStreamFactory = new TLcdFileOutputStreamFactory();
try (OutputStream os = outputStreamFactory.createOutputStream(aDestinationName);
PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os)))) {
try (TLcdLockUtil.Lock autoUnlock = TLcdLockUtil.readLock(aModel)) {
Enumeration elements = aModel.elements();
while (elements.hasMoreElements()) {
ILcdDataObject wayPoint = (ILcdDataObject) elements.nextElement();
writeRecord(wayPoint, writer);
}
}
}
//Update the source name on the model to reflect the latest location
try (TLcdLockUtil.Lock autoUnlock = TLcdLockUtil.writeLock(aModel)) {
TLcdModelDescriptor waypointModelDescriptor = (TLcdModelDescriptor) aModel.getModelDescriptor();
waypointModelDescriptor.setSourceName(aDestinationName);
}
}
private void writeRecord(ILcdDataObject aWayPoint, PrintWriter aWriter) throws IOException {
aWriter.println(aWayPoint.getValue("identifier"));
TLcdLonLatHeightPoint point =
(TLcdLonLatHeightPoint) aWayPoint.getValue("location");
double lon = point.getX();
double lat = point.getY();
double height = point.getZ();
aWriter.print(lon);
aWriter.print(" ");
aWriter.print(lat);
aWriter.print(" ");
aWriter.print(height);
aWriter.println();
}
See the Support a custom vector format for a description of the waypoint format. |
If you save a model, instead of exporting it, you can re-use this code. You can consider saving a model as exporting it to its original location, overwriting the original file:
@Override
public boolean canSave(ILcdModel aModel) {
return canExport(aModel, aModel.getModelDescriptor().getSourceName());
}
@Override
public void save(ILcdModel aModel) throws IllegalArgumentException, IOException {
export(aModel, aModel.getModelDescriptor().getSourceName());
}
Full code
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Enumeration;
import com.luciad.datamodel.ILcdDataObject;
import com.luciad.io.TLcdFileOutputStreamFactory;
import com.luciad.model.ILcdModel;
import com.luciad.model.ILcdModelEncoder;
import com.luciad.model.TLcdModelDescriptor;
import com.luciad.shape.shape3D.TLcdLonLatHeightPoint;
import com.luciad.util.concurrent.TLcdLockUtil;
public class WayPointsModelEncoder implements ILcdModelEncoder {
@Override
public String getDisplayName() {
return "Way Points";
}
@Override
public boolean canSave(ILcdModel aModel) {
return canExport(aModel, aModel.getModelDescriptor().getSourceName());
}
@Override
public void save(ILcdModel aModel) throws IllegalArgumentException, IOException {
export(aModel, aModel.getModelDescriptor().getSourceName());
}
@Override
public boolean canExport(ILcdModel aModel, String aDestinationName) {
return isWaypointModel(aModel) && isValidDestinationName(aDestinationName);
}
private boolean isWaypointModel(ILcdModel aModel) {
return "CWP".equals(aModel.getModelDescriptor().getTypeName());
}
private boolean isValidDestinationName(String aTargetName) {
return aTargetName != null && aTargetName.endsWith(".cwp");
}
@Override
public void export(ILcdModel aModel, String aDestinationName) throws IllegalArgumentException, IOException {
if (!canExport(aModel, aDestinationName)) {
throw new IllegalArgumentException(String.format("Cannot export %s to %s", aModel.getModelDescriptor().getDisplayName(), aDestinationName));
}
//Loop over all elements, and write them to the file one by one
TLcdFileOutputStreamFactory outputStreamFactory = new TLcdFileOutputStreamFactory();
try (OutputStream os = outputStreamFactory.createOutputStream(aDestinationName);
PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os)))) {
try (TLcdLockUtil.Lock autoUnlock = TLcdLockUtil.readLock(aModel)) {
Enumeration elements = aModel.elements();
while (elements.hasMoreElements()) {
ILcdDataObject wayPoint = (ILcdDataObject) elements.nextElement();
writeRecord(wayPoint, writer);
}
}
}
//Update the source name on the model to reflect the latest location
try (TLcdLockUtil.Lock autoUnlock = TLcdLockUtil.writeLock(aModel)) {
TLcdModelDescriptor waypointModelDescriptor = (TLcdModelDescriptor) aModel.getModelDescriptor();
waypointModelDescriptor.setSourceName(aDestinationName);
}
}
private void writeRecord(ILcdDataObject aWayPoint, PrintWriter aWriter) throws IOException {
aWriter.println(aWayPoint.getValue("identifier"));
TLcdLonLatHeightPoint point =
(TLcdLonLatHeightPoint) aWayPoint.getValue("location");
double lon = point.getX();
double lat = point.getY();
double height = point.getZ();
aWriter.print(lon);
aWriter.print(" ");
aWriter.print(lat);
aWriter.print(" ");
aWriter.print(height);
aWriter.println();
}
}