Using LuciadFusion, you can set up high-performance, OGC-certified services for your data. Next to the file formats and databases that are supported out-of-the-box, you can also plug in your own data formats.
In this article, we focus on plugging in another WMS, WMTS, WCS, or WFS service as the source of the data, so that we can serve that data from a LuciadFusion OGC service. This can be useful to:
-
Boost the performance of slow WMS services, because LuciadFusion WMS services automatically use a built-in tile caching mechanism that speeds up WMS data serving.
-
Re-project WMS and WMTS maps to geographic references that the original service doesn’t support. A LuciadFusion WMS service comes with on-the-fly re-projection support to any reference system, such as EPSG, AUTO, AUTO2, or WKT.
-
Customize the styling of WMS and WMTS maps, to add transparency, for example.
-
Render WFS data with an SLD style of your choice and serve the resulting maps using a LuciadFusion WMS or WMTS service.
-
Access WCS coverage data value using WM(T)S GetFeatureInfo requests
How to do it?
Adding support to connect to a WMS service
Create a file entry point with the URL and layer of the WMS service
LuciadFusion can use files as entry points for a data source. This means that we need a custom file format in which we can specify the URL and the layer of the WMS service. For example, we can define a Java Properties file with those two properties:
-
The URL of the WMS service
-
The name of the WMS layer
To load the Black Marble layer served by https://sampleservices.luciad.com/wms
, for example, we specify these properties in the entry point file:
url=http://sampleservices.luciad.com/wms
layer=92c09725-a9c5-46fb-bffd-d9e23b4abbf2
Create a model decoder for the WMS file entry point
We must now plug in a decoder that can create a model for the WMS data specified in the Properties file:
WMSModelDecoder.java
package samples.fusion.wms;
import java.io.IOException;
import java.util.Properties;
import com.luciad.io.TLcdInputStreamFactory;
import com.luciad.model.ILcdModel;
import com.luciad.model.ILcdModelDecoder;
import com.luciad.model.TLcdModelMetadata;
import com.luciad.util.service.LcdService;
import com.luciad.wms.client.model.TLcdOGCWMSProxyModelDecoder;
import com.luciad.wms.client.model.TLcdWMSDataSource;
/**
* Implementation of {@code ILcdModelDecoder} that decodes a WMS model from a Java Properties file.
* The file needs to have the extension .wms and specify two properties:
* <html:ol>
* <html:li>url: the URL of the WMS service,</html:li>
* <html:li>url: the name of the WMS layer to be loaded.</html:li>
* </html:ol>
*/
@LcdService(service = ILcdModelDecoder.class, priority = LcdService.HIGH_PRIORITY)
public class WMSModelDecoder implements ILcdModelDecoder {
private TLcdInputStreamFactory fInputStreamFactory = new TLcdInputStreamFactory();
// We leverage the existing TLcdOGCWMSProxyModelDecoder to create a WMS model.
private TLcdOGCWMSProxyModelDecoder fWMSModelDecoder = new TLcdOGCWMSProxyModelDecoder();
@Override
public String getDisplayName() {
return "WMSProxyModelDecoder";
}
@Override
public boolean canDecodeSource(String aSourceName) {
return aSourceName.endsWith(".wms");
}
@Override
public ILcdModel decode(String aSourceName) throws IOException {
// Load Properties file.
Properties properties = new Properties();
properties.load(fInputStreamFactory.createInputStream(aSourceName));
// Retrieve the required WMS connection parameters.
String wmsUrl = properties.getProperty("url");
String wmsLayer = properties.getProperty("layer");
// Decode a model for the WMS URL & layer when correctly specified.
if (wmsUrl != null && wmsLayer != null) {
return fWMSModelDecoder.decodeSource(TLcdWMSDataSource.newBuilder().uri(wmsUrl).addLayer(wmsLayer).build());
} else {
throw new IOException("Could not find WMS URL and layer in Properties file " + aSourceName);
}
}
@Override
public TLcdModelMetadata decodeModelMetadata(String aSourceName) throws IOException {
ILcdModel model = decode(aSourceName);
return TLcdModelMetadata.newBuilder()
.fromModel(model)
.entryPoint(new TLcdModelMetadata.Source(aSourceName, null))
.addDataCategory(TLcdModelMetadata.DataCategory.RASTER)
.build();
}
}
Create a layer factory for the created models
Next, we plug in a customized layer factory to create a GXY layer for a WMS model:
WMSLayerFactory.java
package samples.fusion.wms;
import com.luciad.model.ILcdModel;
import com.luciad.util.service.LcdService;
import com.luciad.view.gxy.ILcdGXYLayer;
import com.luciad.wms.client.gxy.TLcdWMSProxyGXYLayerFactory;
import com.luciad.wms.client.model.TLcdWMSProxyModelDescriptor;
import com.luciad.wms.server.ILcdWMSGXYLayerFactory;
import com.luciad.wms.server.TLcdWMSRequestContext;
import com.luciad.wms.server.model.ALcdWMSLayer;
/**
* Implementation of {@code ILcdWMSGXYLayerFactory} that can generate a GXY layer for a model
* representing data from a WMS service.
*/
@LcdService(service = ILcdWMSGXYLayerFactory.class, priority = LcdService.HIGH_PRIORITY)
public class WMSLayerFactory implements ILcdWMSGXYLayerFactory {
// We leverage the existing TLcdWMSProxyGXYLayerFactory to create a GXY layer for a WMS model.
// Through the @LcdService annotation, we make it available to LuciadFusion.
private TLcdWMSProxyGXYLayerFactory fWMSLayerFactory = new TLcdWMSProxyGXYLayerFactory();
public WMSLayerFactory() {
fWMSLayerFactory.setTiled(false);
}
@Override
public ILcdGXYLayer createGXYLayer(ILcdModel aModel, ALcdWMSLayer aWMSLayer, String aStyleID, TLcdWMSRequestContext aContext) {
if (aModel.getModelDescriptor() instanceof TLcdWMSProxyModelDescriptor) {
return fWMSLayerFactory.createGXYLayer(aModel);
} else {
return null;
}
}
}
Adding support to connect to a WMTS service
Create a file entry point with the URL and layer of the WMTS service
Similar to the use case of plugging in a WMS service, we can define a Java Properties file that contains this information:
-
The URL of the WMTS service
-
The name of the WMTS layer
To load the global imagery layer served by https://sampleservices.luciad.com/wmts
, for example, we specify these properties in the entry point file:
url=https://sampleservices.luciad.com/wmts
layer=4ceea49c-3e7c-4e2d-973d-c608fb2fb07e
Create a model decoder for the WMTS file entry point
We must now plug in a decoder that can create a model for the WMTS data specified in the Properties file:
WMTSModelDecoder.java
package samples.fusion.wmts;
import java.io.IOException;
import java.util.Properties;
import com.luciad.io.TLcdInputStreamFactory;
import com.luciad.model.ILcdModel;
import com.luciad.model.ILcdModelDecoder;
import com.luciad.model.TLcdModelMetadata;
import com.luciad.ogc.wmts.client.TLcdWMTSDataSource;
import com.luciad.ogc.wmts.client.TLcdWMTSModelDecoder;
import com.luciad.util.service.LcdService;
/**
* Implementation of {@code ILcdModelDecoder} that decodes a WMTS model from a Java Properties file.
* The file needs to have the extension .wmts and specify two properties:
* <html:ol>
* <html:li>url: the URL of the WMTS service,</html:li>
* <html:li>url: the name of the WMTS layer to be loaded.</html:li>
* </html:ol>
*/
@LcdService(service = ILcdModelDecoder.class, priority = LcdService.HIGH_PRIORITY)
public class WMTSModelDecoder implements ILcdModelDecoder {
private TLcdInputStreamFactory fInputStreamFactory = new TLcdInputStreamFactory();
// We leverage the existing TLcdWMTSModelDecoder to create a WMTS model.
private TLcdWMTSModelDecoder fWMTSModelDecoder = new TLcdWMTSModelDecoder();
@Override
public String getDisplayName() {
return "WMTSModelDecoder";
}
@Override
public boolean canDecodeSource(String aSourceName) {
return aSourceName.endsWith(".wmts");
}
@Override
public ILcdModel decode(String aSourceName) throws IOException {
// Load Properties file.
Properties properties = new Properties();
properties.load(fInputStreamFactory.createInputStream(aSourceName));
// Retrieve the required WMS connection parameters.
String wmtsUrl = properties.getProperty("url");
String wmtsLayer = properties.getProperty("layer");
// Decode a model for the WMS URL & layer when correctly specified.
if (wmtsUrl != null && wmtsLayer != null) {
return fWMTSModelDecoder.decodeSource(TLcdWMTSDataSource.newBuilder().uri(wmtsUrl).layer(wmtsLayer).build());
} else {
throw new IOException("Could not find WMTS URL and layer in Properties file " + aSourceName);
}
}
@Override
public TLcdModelMetadata decodeModelMetadata(String aSourceName) throws IOException {
ILcdModel model = decode(aSourceName);
return TLcdModelMetadata.newBuilder()
.fromModel(model)
.entryPoint(new TLcdModelMetadata.Source(aSourceName, null))
.addDataCategory(TLcdModelMetadata.DataCategory.RASTER)
.build();
}
}
For WMTS data, we don’t need to plug in a custom layer factory: the data is modeled using the ALcdImage
API, which is supported out-the-box by the default layer factories available in LuciadFusion.
Adding support to connect to a WCS service
Create a file entry point with the URL and coverage of the WCS service
Similar to the use case of plugging in a WMS or WMTS service, we can define a Java Properties file that contains this information:
-
The URL of the WCS service
-
The name of the WCS coverage
To load the global elevation layer served by https://sampleservices.luciad.com/wcs
, for example, we specify these properties in the entry point file:
url=https://sampleservices.luciad.com/wcs
coverage=e8f28a35-0e8c-4210-b2e8-e5d4333824ec
Create a model decoder for the WCS file entry point
We must now plug in a decoder that can create a model for the WCS data specified in the Properties file:
WCSModelDecoder.java
package samples.fusion.wcs;
import java.io.IOException;
import java.util.Properties;
import com.luciad.io.TLcdInputStreamFactory;
import com.luciad.model.ILcdModel;
import com.luciad.model.ILcdModelDecoder;
import com.luciad.model.TLcdModelMetadata;
import com.luciad.ogc.wcs.client.TLcdWCSCoverageModelDecoder;
import com.luciad.ogc.wcs.client.TLcdWCSDataSource;
import com.luciad.util.service.LcdService;
/**
* Implementation of {@code ILcdModelDecoder} that decodes a WCS model from a Java Properties file.
* The file needs to have the extension .wcs and specify two properties:
* <html:ol>
* <html:li>url: the URL of the WCS service,</html:li>
* <html:li>url: the name of the WCS coverage to be loaded.</html:li>
* </html:ol>
*/
@LcdService(service = ILcdModelDecoder.class, priority = LcdService.HIGH_PRIORITY)
public class WCSModelDecoder implements ILcdModelDecoder {
private TLcdInputStreamFactory fInputStreamFactory = new TLcdInputStreamFactory();
// We leverage the existing TLcdWCSCoverageModelDecoder to create a WCS model.
private TLcdWCSCoverageModelDecoder fWCSModelDecoder = new TLcdWCSCoverageModelDecoder();
@Override
public String getDisplayName() {
return "WCSModelDecoder";
}
@Override
public boolean canDecodeSource(String aSourceName) {
return aSourceName.endsWith(".wcs");
}
@Override
public ILcdModel decode(String aSourceName) throws IOException {
// Load Properties file.
Properties properties = new Properties();
properties.load(fInputStreamFactory.createInputStream(aSourceName));
// Retrieve the required WCS connection parameters.
String wcsUrl = properties.getProperty("url");
String wcsCoverage = properties.getProperty("coverage");
// Decode a model for the WCS URL & coverage when correctly specified.
if (wcsUrl != null && wcsCoverage != null) {
return fWCSModelDecoder.decodeSource(TLcdWCSDataSource.newBuilder().uri(wcsUrl).coverageName(wcsCoverage).build());
} else {
throw new IOException("Could not find WCS URL and coverage in Properties file " + aSourceName);
}
}
@Override
public TLcdModelMetadata decodeModelMetadata(String aSourceName) throws IOException {
ILcdModel model = decode(aSourceName);
return TLcdModelMetadata.newBuilder()
.fromModel(model)
.entryPoint(new TLcdModelMetadata.Source(aSourceName, null))
.addDataCategory(TLcdModelMetadata.DataCategory.RASTER)
.build();
}
}
For WCS data, we don’t need to plug in a custom layer factory: the data is modeled using the ALcdImage
API, which is supported out-the-box by the default layer factories available in LuciadFusion.
Adding support to connect to a WFS service
Create a file entry point with the URL and feature type of the WFS service
Similar to the use case of plugging in a WMS, WMTS or WCS service, we can define a Java Properties file that contains the following information:
-
The URL of the WFS service
-
The name of the WFS feature type
To load the US cities feature type served by https://sampleservices.luciad.com/wfs
, for example, we specify these properties in the entry point file:
url=https://sampleservices.luciad.com/wfs
featureType=cities
Create a model decoder for the WFS file entry point
We must now plug in a decoder that can create a model for the WFS data specified in the Properties file:
WFSModelDecoder.java
package samples.fusion.wfs;
import java.io.IOException;
import java.util.Properties;
import com.luciad.io.TLcdInputStreamFactory;
import com.luciad.model.ILcdModel;
import com.luciad.model.ILcdModelDecoder;
import com.luciad.model.TLcdModelMetadata;
import com.luciad.ogc.wfs.client.TLcdWFSDataSource;
import com.luciad.ogc.wfs.client.TLcdWFSModelDecoderDecorator;
import com.luciad.util.service.LcdService;
/**
* Implementation of {@code ILcdModelDecoder} that decodes a WFS model from a Java Properties file.
* The file needs to have the extension .wfs and specify two properties:
* <html:ol>
* <html:li>url: the URL of the WFS service,</html:li>
* <html:li>url: the name of the WFS feature type to be loaded.</html:li>
* </html:ol>
*/
@LcdService(service = ILcdModelDecoder.class, priority = LcdService.HIGH_PRIORITY)
public class WFSModelDecoder implements ILcdModelDecoder {
private TLcdInputStreamFactory fInputStreamFactory = new TLcdInputStreamFactory();
// We leverage the existing TLcdWFSModelDecoderDecorator to create a WFS model.
private TLcdWFSModelDecoderDecorator fWFSModelDecoder = new TLcdWFSModelDecoderDecorator();
@Override
public String getDisplayName() {
return "WFSProxyModelDecoder";
}
@Override
public boolean canDecodeSource(String aSourceName) {
return aSourceName.endsWith(".wfs");
}
@Override
public ILcdModel decode(String aSourceName) throws IOException {
// Load Properties file.
Properties properties = new Properties();
properties.load(fInputStreamFactory.createInputStream(aSourceName));
// Retrieve the required WFS connection parameters.
String wfsUrl = properties.getProperty("url");
String wfsFeatureType = properties.getProperty("featureType");
// Decode a model for the WFS URL & feature type when correctly specified.
if (wfsUrl != null && wfsFeatureType != null) {
return fWFSModelDecoder.decodeSource(TLcdWFSDataSource.newBuilder().uri(wfsUrl).featureTypeName(wfsFeatureType).build());
} else {
throw new IOException("Could not find WFS URL and feature type in Properties file " + aSourceName);
}
}
@Override
public TLcdModelMetadata decodeModelMetadata(String aSourceName) throws IOException {
ILcdModel model = decode(aSourceName);
return TLcdModelMetadata.newBuilder()
.fromModel(model)
.entryPoint(new TLcdModelMetadata.Source(aSourceName, null))
.addDataCategory(TLcdModelMetadata.DataCategory.VECTOR)
.build();
}
}
For WFS data, we don’t need to plug in a custom layer factory: the data is modeled using the ILcdShape
API, which is supported out-the-box by the default layer factories available in LuciadFusion.
Using Studio to connect to OGC services
The annotations in both the decoder and the WMS layer factory make sure that LuciadFusion picks them up, and recognizes .wms
, .wmts
, .wcs
and .wfs
files. You can create products and services for WMS, WMTS, WCS and WFS data, like you can for data from any other source.
Users of LuciadFusion Studio can add WMS, WMTS, WCS and WFS data directly by providing the file path to a .wms
, .wmts
, .wcs
or .wfs
file when they click the Add data button.
Users of the Studio REST API can also specify a file path to a .wms
, .wmts
, .wcs
or .wfs
file to add the data directly.