This tutorial explains the basics of working with LuciadCPillar:

• Creating a map

The sample_firstapp sample supports this tutorial. It is provided for C++ as well as C#.

## Configuring the library through global initialization

Before you can use the LuciadCPillar library, you need to define a global configuration. You can configure global settings for the library by initializing an `Environment`. While you are interacting with the library, you must hold a reference to that `Environment` object. The lifetime of this object determines when the functionality of the library is available.

### Creating the Environment

You use a builder pattern to create the `Environment`, through `Environment::createInitializer()`. Typically you need to configure the following:

• the contents of the license file

• the location to the LuciadCPillar resources. You can find those in the `cpp/resources` folder of the release.

For example:

Program (C++): Initialize an environment
``````std::string licenseText = readLicenseText();
std::shared_ptr<Environment>
Program (C#): Initialize an environment
``````private Environment env;

env = Environment.CreateInitializer()
.WithResourcePath("resources")
.Initialize();``````

### Setting global configuration options

The initializer allows you to set global configuration options for the library, so that you can replace some default behavior:

• You can plug in a logging framework. See Logging for more information.

• You can decide whether to initialize and tear down the curl library for network requests.

### Disposing of the environment

When the application is closed, you must dispose of the created environment. In C++, the destructor is automatically invoked when the environment object goes out of scope. Please make sure that a reference to the environment is kept while the application uses the LuciadCPillar API.

Program (C#): Dispose of an environment
``env.Dispose();``

## Creating a map and visualizing geographical data

To visualize geographic data, you need to create a map.

Next, you typically need to integrate the map into an UI framework.

• For C++: the LuciadCPllar samples integrate the map with QtQuick by default. A specific Qt Widgets integration sample is provided.

• For C#: the LuciadCPillar samples integrate with WPF by default.

### Creating the map

When creating the map, you must pass a spatial reference that will be used to display the data. All data will be transformed to this reference on-the-fly. For a 3D view, you must specify a geocentric reference, for example EPSG:4978. For a 2D view, you can pick another, non-geodetic reference. The map is created using the builder pattern.

Program (C++): Creating the map
``````luciad::expected<std::shared_ptr<CoordinateReference>, ErrorInfo> reference = CoordinateReferenceProvider::create("EPSG:4978");
if (!reference) {
std::cerr << "Could not create the coordinate reference: " << reference.error() << std::endl;
std::exit(EXIT_FAILURE);
}
std::shared_ptr<Map> map = Map::newBuilder().reference(*reference).build();``````
Program (C#): Creating the map
``````// Create a Map with a 3D Map reference
CoordinateReference mapReference = CoordinateReferenceProvider.Create("EPSG:4978");
Debug.Assert(mapReference != null, "Could not create the coordinate reference.");
Map map = Map.NewBuilder().Reference(mapReference).Build();``````

### Visualizing data

To visualize data, you must take these steps:

1. Create a model for the data

2. Create a layer for that model

3. Add the layer to the map

You typically create a model by decoding a data source. LuciadCPillar offers out-of-the-box functionality to create models from data in certain formats, such as the `GeoPackageModelDecoder` for the GeoPackage format. If necessary, you can add support for other formats yourself.

#### Decoding and visualizing raster data

Raster data consists of matrices of pixels, or grid cells. Models containing raster data implement the `IRasterModel` interface.

To visualize a raster model, you must create a `RasterLayer`. You can use a builder pattern to create the layer.

Finally, you can show the layer on the map by adding it to the layer list of the map.

Program (C++): Adding a raster layer
``````void addRasterLayer(const std::shared_ptr<Map>& map, const std::string& fileName) {
if (!model) {
ErrorInfo& errorInfo = model.error();
std::cerr << "Cannot load GeoPackage file (raster) " + fileName + ": " + errorInfo.getMessage() << std::endl;
return;
}

std::shared_ptr<IRasterModel> rasterModel = std::dynamic_pointer_cast<IRasterModel>(*model);
if (!rasterModel) {
std::cerr << "Not a raster model" << std::endl;
return;
}
std::shared_ptr<RasterLayer> rasterLayer = RasterLayer::newBuilder().model(rasterModel).build();
}``````
Program (C#): Adding a raster layer
``````private void AddRasterLayer(String fileName, Map map)
{
try
{
Model model = GeoPackageModelDecoder.Decode(fileName);
RasterLayer rasterLayer = RasterLayer.NewBuilder().Model(model as IRasterModel).Build();
}
catch (Exception exception)
{
string errorMessage = $"Cannot load data from file '{fileName}': {exception.Message}"; Console.WriteLine(errorMessage); } }`````` #### Decoding and visualizing vector data Vector data consists of objects that have a geometry and typically some extra object information in the attributes, such as name, type and so on. Those types of objects are called features, and are represented in LuciadCPillar by the `Feature` class. Models containing features implement the `IFeatureModel` interface. To visualize a feature model, you need a `FeatureLayer`. When you pass the model to the layer, you must also provide an instance of `IFeaturePainter`. This painter determines how the features will be visualized. In the samples, a simple implementation, called the `RiverPainter`, is used to visualize river features. Again, you can show the layer on the map by adding it to the layer list of the map. Program (C++): Adding a feature layer ``````void addFeatureLayer(const std::shared_ptr<Map>& map, const std::string& fileName) { luciad::expected<std::shared_ptr<Model>, ErrorInfo> model = GeoPackageModelDecoder::decode(fileName); if (!model) { ErrorInfo& errorInfo = model.error(); std::cerr << "Cannot load GeoPackage file (vector) " + fileName + ": " + errorInfo.getMessage() << std::endl; return; } std::shared_ptr<IFeatureModel> featureModel = std::dynamic_pointer_cast<IFeatureModel>(*model); if (!featureModel) { std::cerr << "Not a feature model" << std::endl; return; } std::shared_ptr<FeatureLayer> featureLayer = FeatureLayer::newBuilder().model(featureModel).painter(std::make_shared<RiverPainter>()).build(); map->getLayerList()->add(featureLayer); }`````` Program (C#): Adding a feature layer ``````private void AddFeatureLayer(String fileName, Map map) { try { Model model = GeoPackageModelDecoder.Decode(fileName); IFeaturePainter featurePainter = new RiverPainter(); FeatureLayer featureLayer = FeatureLayer.NewBuilder().Model(model as IFeatureModel) .Painter(featurePainter) .Build(); map.LayerList.Add(featureLayer); } catch (Exception exception) { string errorMessage =$"Cannot load data from file '{fileName}': {exception.Message}";
Console.WriteLine(errorMessage);
}
}``````

### Fitting on the data

When you are adding data to the map, it can be useful to automatically move the viewport of the map to the data position, so that it becomes visible to the map user. This is referred to as fitting on the data in LuciadCPillar.

To fit on map data:

1. Create the bounds that you want to fit on, or take the bounds of the data model.

2. Create a map navigator for the map. The map navigator is an object that allows you to navigate in the map programmatically.

3. Use the map navigator to fit on the bounds.

Program (C++): Fitting the map
``````luciad::expected<std::shared_ptr<CoordinateReference>, ErrorInfo> crs = CoordinateReferenceProvider::create("EPSG:4326");
if (!crs) {
return;
}
Coordinate coordinate{7.5, 45.9, 0};
Bounds bounds{crs.value(), coordinate, 2.5, 1.0, 0};
std::shared_ptr<MapNavigator> mapNavigator = MapNavigator::create(map);
mapNavigator->newFitAction().bounds(bounds).fit();``````
Program (C#): Fitting the map
``````CoordinateReference fitReference = CoordinateReferenceProvider.Create("EPSG:4326");
Bounds bounds = GeometryFactory.CreateBounds(fitReference, new Coordinate(7.5, 45.9), 2.5, 1.0, 0);
MapNavigator navigator = MapNavigator.Create(map);
navigator.NewFitAction().Bounds(bounds).Fit();``````