Out-of-the-box, you can use our PanoramaConverter
sample, or LuciadFusion, to process panoramic images.
The output consists of:
-
Panorama "feature" information such as positions, image tiling structure, and data properties. You can find those in a GeoJson file named
cubemap.json
. -
Panorama image tiles. Those are stored in a folder named
images
.
The LuciadFusion PANORAMICS service serves these files and LuciadRIA can load them. This works for most datasets.
For some use cases though, you want to merge many datasets into one, for example if you have scans made on different days. You may also have many panoramas across a large area. Storing such a number in a single GeoJSON file has its limits.
Our TLcdLuciadPanoramaModelEncoder
supports many other spatial formats as output format, besides GeoJSON. You can:
-
Store the panorama feature information in a spatial database such as PostGIS, Amazon RDS, Oracle, or Microsoft SQL Server, and serve it over the OGC WFS protocol with LuciadFusion.
-
Store the panorama images on any storage system, including Amazon S3, and serve them through a regular HTTP file server, or a CDN such as Amazon CloudFront.
In this article, we’re going to show you how to store the panorama feature information in a spatial database. Next, we expose the result through LuciadFusion, and load the panoramas in LuciadRIA.
Decoding the input data
In this example, we’re using a Pegasus Mobile Mapping track, but other panorama data also works, including your own custom format.
First, we’re going to decode the source panorama data. We can use a specific model decoder, or find the right one using TLcdCompositeModelDecoder
.
ILcdModelDecoder modelDecoder = new TLcdCompositeModelDecoder(TLcdServiceLoader.getInstance(ILcdModelDecoder.class));
ILcdModel inputModel = modelDecoder.decode("/path/to/pegasus/Track_ASphere/External Orientation.csv");
Creating or opening the spatial table
Next, we must create the table in the spatial database that’s going to hold the feature information. You can create the table
using the SQL CREATE TABLE
statement with the right columns, but we’re going to let our TLcdPostGISModelEncoder
create it.
The TLcdPostGISModelEncoder
and TLcdPostGISModelDecoder
need an entry file with the database connection and table details. Make a file named cubemap.pgs
with the content below — adapted to your database of course. For details on the properties in the file, see the PostGIS documentation.
You need at least the spatial column and the properties id, type, version, levelCount, tileWidth, and tileHeight. You can add other columns, like filename in this example. It stores the value of the filename data property in the input model. |
driver = org.postgresql.Driver
url = jdbc:postgresql://localhost:5432/luciad
user = luciad
password = luciad
table = cubemap
spatialColumn = location
featureNames.0 = id
featureNames.1 = version
featureNames.2 = type
featureNames.3 = levelCount
featureNames.4 = tileWidth
featureNames.5 = tileHeight
featureNames.6 = filename # this property is taken over from the input model
primaryFeatureIndex = 0
primaryFeatureAutoGenerated = false
# The properties below are only used creating a new table
dropBeforeCreatingTable = false # enable this to remove an existing table, use with caution
SRID = 4326
geometryType = POINT
dimensions = 3
featureDataTypes.0 = VARCHAR(40)
featureDataTypes.1 = VARCHAR(10)
featureDataTypes.2 = VARCHAR(10)
featureDataTypes.3 = INTEGER
featureDataTypes.4 = INTEGER
featureDataTypes.5 = INTEGER
featureDataTypes.6 = VARCHAR(40)
We need to create the table once. To create the table, you just export an empty model.
new TLcdPostGISModelEncoder().export(new TLcdVectorModel(), "/path/to/cubemap.pgs");
Let’s verify that the table exists. Note that it has a spatial index on the location column, and a primary key index on the id column.
![panorama postgis table](images/panorama_postgis_table.png)
Processing the panoramas
Now that we have the input panoramas in a model, and have created the SQL table, we can start processing.
We can use the standard TLcdLuciadPanoramaModelEncoder
, but pass a delegate encoder and decoder that it uses for storing feature information.
TLcdLuciadPanoramaModelEncoder modelEncoder = new TLcdLuciadPanoramaModelEncoder(new TLcdPostGISModelDecoder(), new TLcdPostGISModelEncoder());
We’re good to go. We can kick off the export.
if (modelEncoder.canExport(inputModel, "/path/to/cubemap.pgs")) {
modelEncoder.export(inputModel, "/path/to/cubemap.pgs");
}
We can have a look inside the table using a SQL query.
SELECT *, ST_ASTEXT(location) FROM cubemap;
![panorama postgis list](images/panorama_postgis_list.png)
The actual panorama image tiles must be in a folder named images/ , right next to the cubemap.pgs file.
|
Adding more panoramas to the database
You can add more datasets to this table. Every following export
adds rows in the database, unless a row with the same id already exists.
By default, the id property is a simple incrementing number though, restarting from 1
for each dataset. To distinguish panoramas of distinct datasets, you must give each a unique id. This can be a UUID or another automatically generated ID, but preferably it’s something deterministic, based on a property
of your object for example. This way, if you export the same dataset twice, to resume after aborting for example, objects
get the same id as before. Plug in your id scheme with setFeatureIDRetriever
. In the example below, we’re using the filename property as id.
modelEncoder.setFeatureIDRetriever(o -> (String) ((ILcdDataObject) o).getValue("filename"));
Serving the database table with LuciadFusion WFS
The LuciadFusion PANORAMICS service exposes only panoramas stored as GeoJson, but you can simply serve your database with a WFS service instead. Let LuciadFusion crawl the cubemap.pgs
file through a Data root, upload the cubemap.pgs
file, or add a data entry for it directly using Add data. Then, create a service of type WFS.
![panorama postgis wfs](images/panorama_postgis_wfs.png)
You can serve the images/ folder with any HTTP server or CDN.
|
Loading the panoramas with LuciadRIA
In LuciadRIA, you create a regular WFS model for this service. You attach a FusionPanoramaModel
to it with the path to the images. Then, create a FeatureLayer
with a PanoramaFeaturePainter
as usual. This layer uses spatial WFS queries to load panorama positions relevant to the visible part of the map.
WFSCapabilities.fromURL("http://localhost:8081/ogc/wfs/cubemap")
.then(function(capabilities) {
const featureType = capabilities.featureTypes[0];
const store = WFSFeatureStore.createFromCapabilities(capabilities, featureType.name);
const model = new FeatureModel(store);
const panoModel = new FusionPanoramaModel("https://d341tnsdj5u5dv.cloudfront.net/data/panoramics/");
return new FeatureLayer(model, {
panoramaModel: panoModel,
selectable: false,
painter: new PanoramaFeaturePainter({
iconHeightOffset: 2.5
}),
label: "Panoramas",
});
})
LuciadRIA now displays the panorama.
![panorama postgis ria](images/panorama_postgis_ria.jpg)