NAVTOR SDK  v.3.0
Software Development Kit for fast and efficient development of ECDIS, ECS and generic marine applications
How to Guides

Table of Contents

This section includes answers to frequently asked questions.

SDK distribution, installation, supported operating systems and graphic platforms

Which operating systems does SDK support?

The NAVTOR SDK currently supports Windows 32-bit and 64-bit operating systems, starting with Windows 7, can be compiled for Windows XP by request; Linux 64-bits, Ubuntu/Debian operating system starting with Ubuntu 12.04 and Debian 6.0; Android armeabi-v7a, arm64-v8a, x86_64.

Which graphic platforms does SDK support?

The SDK supports CPU-based software rendering back-end and GPU-accelerated OpenGL and OpenGL ES rendering back-end.

How to redistribute and install SDK runtime?

Windows

For Windows: The SDK runtime is compiled into a single MSI file named sdk_x32.msi for 32-bit platforms and sdk_x64.msi for 64-bit platforms. Use MSI API or MsiExec for redistribution.

Linux

For Linux: The SDK runtime is delivered as a Debian package named navtor-sdk_3.0.0.xxxx-xxx..xxx_amd64.deb

Android

For Android: The NAVTOR SDK is delivered as a set of directories with compiled libraries for 3 platform architectures, SDK environment and samples demonstrating how to compile and deploy an Android application with the NAVTOR SDK on a target device.

Where does the SDK runtime reside on a target machine?

Windows

SDK runtime resides in 2 directories:

  • The program directory (read only) resides by default in the "Program Files\NAVOR\NAVTOR SDK x64 Runtime" for 64-bit version and in the "Program Files (x86)\NAVOR\NAVTOR SDK x32 Runtime" for 32-bit version.
  • The program data directory (read/write) resides in the "ProgramData\NAVTOR\NAVTOR SDK x64 Runtime" for 64-bit version and in the "ProgramData\NAVTOR\NAVTOR SDK x32 Runtime" for 32-bit version.

SDK Runtime adds the path to the SDK program directory into the System environment variable Path. SDK Runtime does not use the Windows Registry.

Linux

SDK runtime resides in 3 directories:

  • Executables and libraries reside in the "/usr/bin/" and "/usr/lib" directories.
  • Other data reside in the "/usr/share/navtor-sdk" directory.

What geodatabases does the SDK runtime installation include?

The SDK installation includes the following databases:

  • IHO Test Dataset For ECDIS compiled to a SENC geodatabase
  • Chart1 datasets compiled to a SENC geodatabase
  • Overview charts in a SENC geodatabase

What does geodatabase WKN location stand for?

The geodatabase WKN location means "Well Known Location of Geodatabases". The geodatabase WKN location is the path that is written in the SDK configuration file [program]/bin/sdk_config.xml or /usr/share/navtor-sdk/sdk_config.xml under the "geodatabase/root_path" section. C++ developers can use base/sdk_config_const.h file for referencing the configuration constants. The WorkspaceFactory object has a service to enumerate geodatabases located in the "Geodatabase WKN Location".

Access to the SENC Geodatabase

How to convert S-57/S-63 data to the SENC?

See the S-57/S-63 data ingestion article.

How to enumerate SENC geodatabases residing in the WKN location?

The WorkspaceFactory does this work. Create a WorkspaceFactory object and call the GetGeodatabasesNamesFromWKNLocation method.

See the code snippet below:

C++

  sdk::ISDKComponentSP obj;
  SDKResult res = SDKCreateComponentInstance(NULL, sdk::gdb::kSencGdbWorkspaceFactoryCID, &obj);
  if (SDK_OK(res)) {
    sdk::gdb::IWorkspaceFactorySP wks_factory;
    res = obj->GetInterface(sdk::gdb::IWorkspaceFactory::IID(), reinterpret_cast< void** >(wks_factory);
    if (SDK_OK(res)) {
      sdk::IEnumSDKStringSP geodatabases_names;
      if (SDK_OK(wks_factory->GetGeodatabasesNamesFromWKNLocation(&geodatabases_names, NULL))) {
        for (sdk::ScopedString name; SDK_OK(geodatabases_names->Next(&name)); name.Clear()) {
        /// Do your stuff...
        }
      }
    }
  }
  

How to retrieve properties of a geodatabase?

Geodatabase properties are stored in a separate table of the geodatabase root catalog. To retrieve a geodatabase property, you should use the sdk::gdb::IRootCatalog::GetGeodatabaseProperty method.

See the code snippet below:

C++

  // Open the root catalog from the file
  sdk::gdb::IRootCatalogSP root_catalog;
  wks_factory->OpenRootCatalog(sdk::ScopedString(catalog_file_path), &root_catalog);
    sdk::gdb::ICatalogSP root_catalog;
  // or take it from the opened workspace
  workspace->GetCatalog(sdk::gdb::kCatType_RootCatalog, &root_catalog);

  // Take property using the general method
  sdk::ScopedAny geodatabase_name;
  root_catalog->GetGeodatabaseProperty(
    sdk::gdb::kRootCatGeodatabaseProperty_GeodatabaseName, geodatabase_name);
  // or the template one
  SDKUInt32 encryption = 0;
  root_catalog->GetGeodatabasePropertyT(
    sdk::gdb::kRootCatGeodatabaseProperty_Encryption, encryption);
  

How to open a SENC workspace?

Use the WorkspaceFactory::Open method. This method requires configuration parameters listed below:

  • Full path to the SENC geodatabase root.cat
  • HW_ID of the system if the SENC geodatabase is encrypted
  • Path to the PERMITS file if the SENC geodatabase is encrypted

See the code snippet below: C++

  Create the workspace factory object sdk::gdb::IWorkspaceFactorySP wks_factory ...
  SDKResult res; sdk::gdb::IWorkspaceConfigurationSP wks_config;
  if (SDK_FAILED(res = wks_factory->CreateWorkspaceConfiguration(&wks_config)))
    return res; wks_config->SetConfigurationParameter(
      sdk::gdb::kWorkspaceConfigurationParameter_RootPath, 
      sdk::ScopedAny(root_catalog_path));
  if (is_senc_encrypted) {
    sdk::gdb::IEncryptionParametersSP encryption_parameters;
    if (SDK_FAILED(res = wks_factory->CreateEncryptionParameters(&encryption_parameters)))
      return res;
    encryption_parameters->SetParameter(sdk::gdb::kEncryptionParameter_S63_HWID, sdk::ScopedAny(hw_id_str));    
    encryption_parameters->SetParameter(sdk::gdb::kEncryptionParameter_S63_PermitsPath, sdk::ScopedAny(permits_file_path_str));
    wks_config->SetConfigurationParameter( 
      sdk::gdb::kWorkspaceConfigurationParameter_EncryptionParameters, 
      sdk::ScopedAny(encryption_parameters));
  }
  sdk::gdb::IWorkspaceSP wks;
  if (SDK_OK(res = wks_factory->Open(wks_config, &wks))) {
    // Do your stuff...
  }

How to enumerate datasets included in a SENC workspace?

Use the IWorkspace::GetDatasets method. To control the enumerator you can use a geometric filter and a parameters filter. The dataset enumerator currently supports only rectangular geometric filters. For the enumerator parametrization you can use the dataset parameters defined in the DatasetParameter enumeration.

How to enumerate features included in a feature dataset?

Use the IFeatureDataset::GetFeatures method. To control the feature enumerator you can optionally use a geometric filter and/or a parameters filters. The feature enumerator currently supports only rectangular geometric filters. Use the GetFeaturesParameter enumeration for the query parametrization.

See the code snippet below:

C++

  sdk::geometry::IGeometry geometric_filter;
  wks_factory->CreateRectGeometryFilter(
    sdk::GeoIntFromDeg(-10.0), sdk::GeoIntFromDeg(-10.0), 
    sdk::GeoIntFromDeg(10.0), sdk::GeoIntFromDeg(10.0), &geometric_filter);
  sdk::gdb::IEnumFeatureSP enum_feature;
  feature_dataset->GetFeatures(geometric_filter, NULL, &enum_feature);
    

How to get a feature's attributes?

Use the sdk::gdb::IObject::GetAttributes, sdk::gdb::IAttributeCollection::GetAttribute and sdk::gdb::IAttribute::GetValue methods.

See the code snippet below:

C++

   void GetAttributes(const sdk::gdb::IFeatureSP& feature) {
    sdk::gdb::IObjectSP object;
    feature->GetInterface(sdk::gdb::IObject::IID(), reinterpret_cast< void** >(&object));
    SDKResult res;
    sdk::gdb::IAttributeCollectionSP attributes;
    if (SDK_FAILED(res = object->GetAttributes(&attributes))) {
      if (Err_NotExist == res)
        return res // the object does not have attributes
        return res; // other errors occurred
  }
    SDKUInt32 count = 0;
    attributes->GetAttributeCount(count);
    for (SDKUInt32 i = 0; i < count; ++i) {
      sdk::gdb::IAttributeSP attr;
      if (SDK_OK(attributes->GetAttribute(i, &attr))) {
        sdk::ScopedAny val;
        if (SDK_OK(attr->GetValue(val))) {
        // Do your stuff...
        }
      }
    }
  }

How to get the default shape of a feature?

Use the sdk::gdb::IFeature::GetShape method to return the default shape of a specified feature.

How to get feature shape attributes?

The feature shape is a rightful geodatabase object. It implements the sdk::gdb::IGDBGeometry interface inherited from the sdk::gdb::IObject interface that should be used to get shape attributes. See the How to get a feature's attributes section.

How to get an image or a text referenced in a feature?

Here are the steps involved in getting an image or a text:

  • Get the file name of an image or a text from the corresponding feature attribute
  • Find the root catalog entry using the file name of an image or a text as a root catalog entry key
  • Get the DatasetID from the root catalog entry found in the previous step
  • Get the dataset using the sdk::gdb::IWorkspace::GetDataset method
  • Get the image or the text data using the sdk::gdb::IDataset::GetData method

How to get a feature's relationships?

Use the sdk::gdb::IObject::GetRelationships method.

See the code snippet below:

C++

   SDKResult InspectRelationships(const sdk::gdb::IObjectSP object) {
    sdk::gdb::IRelationshipCollectionSP rel_collection;
    SDKResult res;
    if (SDK_FAILED(res = object->GetRelationships(sdk::gdb::kRelationshipClass_Any,
    sdk::gdb::kRelationshipRole_Any, &rel_collection))) {
      if (Err_NotExist == res)
        return res; // No relationships
      return res;
    }
    SDKUInt32 count = 0;
    rel_collection->GetRelationshipCount(count);
    for (SDKUInt32 i = 0; i < count; ++i) {
      sdk::gdb::IObject related_object;
      if (SDK_OK(rel_collection->GetRelationship(i, &related_object, NULL, NULL))) {
      // Do your stuff...
      }
    }
  }
    

How to copy SENC from NavBox

To read a SENC from a NavBox, use the NavBoxRESTClient SDK component and its INavBoxRESTClient interface described in inc/datalayer/navbox_client/navbox_client_interface.h

To create a NavBoxRESTClient component, use the SDKCreateComponentInstance call with the kNavBoxRESTClientCID class id defined in inc/datalayer/navbox_client/component_ids.h

To get access to the NavBox, you need to initialize the component with a number of required parameters. In particular, you have to set a number of properties of the ISDKProperties interface that implements the NavBoxRESTClient component using its SetProperty method. The required properties are the following:

  • kNavBoxRESTClientProperty_Host - specifies a valid host address of the NavBox.
  • kNavBoxRESTClientProperty_SoftwareName - specifies the application name; the property is only used for logging purposes by the NavBox service, but it must be provided.
  • kNavBoxRESTClientProperty_SoftwareVersion - specifies the application version; the property is only used for logging purposes by the NavBox service, but must be provided.
  • kNavBoxRESTClientProperty_ServerToken - the property value is a string that identifies the NavBox, indicating to the NavBox that the application is aware of the security scheme and knows the NavBox details. The NavBox token string is placed in the NavStick during synchronization with the NAVTOR back office. It can be found on the NavStick path //INFO/NAVTOR/Setup/NavBox/APICredentials .
    Once obtained, the string could be saved in a secure way to a local drive for later use without requesting the NavStick. This can be done during the installation phase of the application. If for some reason the NavStick is not available and the NavBox token has not been obtained, the only way to get it is by calling NAVTOR support.
  • kNavBoxRESTClientProperty_OEMToken - the property value is a string that is released by NAVTOR for an OEM. It has to be passed to the NavBox during the initialization phase. It is the responsibility of the OEM to store the OEM token in its system in a way that prevents unauthorized use. The NavBox connected to the system must be aware of the OEM token to recognize and grant access to data. The OEM token is uploaded from the NAVTOR Back Office during initialization for a specific system.

Once you get connected to the NavBox, you may start using NavBox services.

Use the GetSENCFiles method of the INavBoxRESTClient interface to get the list of SENC files that need to be copied to a local drive.

Use the GetSENCFileInfo method of the INavBoxRESTClient interface to get complete information for a file from the list.

As the last step, use the OpenSENCFileStream method of the INavBoxRESTClient interface to get the file stream to copy the file content.

Visualization, portrayal, scene, layers

Software rendering backend

How to initialize the scene manager for your application chart window?

The scene manager initialization involves the following steps:

See the code snippet below:

C++

   sdk::ISDKComponentSP obj;
   SDKCreateComponentInstance(NULL, sdk::vis::kSceneManagerCID, &obj);

   sdk::vis::ISceneManagerSP scene_manager;
   obj->GetInterface(sdk::gdb::ISceneManager::IID(), reinterpret_cast< void** >(scene_manager);
   
   sdk::vis::ISceneManagerInitialParametersSP initial_parameters;
   if (is_window) {
      sdk::gfx::OSWindow window;
      window.hwnd = hwnd;

      sdk::vis::SceneManagerInitParametersHelper::CreateSceneManagerInitialParameters(
        &window, sdk::SizeF(wnd_rect.width, wnd_rect.height), 1/resolution, 
        sdk::vis::SceneManagerFlag_OutputTechnologyType_Auto | 
          sdk::vis::kSceneManagerFlag_MultiThreadedRendering | 
          sdk::vis::kSceneManagerFlag_OutputType_Window,
        initial_parameters);
   } else {
      sdk::vis::SceneManagerInitParametersHelper::CreateSceneManagerInitialParameters(
        sdk::SizeF(tex_rect.width, tex_rect.height), 1/resolution, 
        sdk::vis::SceneManagerFlag_OutputTechnologyType_Auto | 
          sdk::vis::kSceneManagerFlag_MultiThreadedRendering | 
          sdk::vis::kSceneManagerFlag_OutputType_Texture,
        initial_parameters);
   }

   scene_manager->Initialize(initial_parameters);

How to add a SENC geodatabase workspace into the scene?

Use the sdk::vis::ISceneManager::AddDataSourceView method.

See the code snippet below:

C++

  SDKResult AddWorkspace(const sdk::vis::ISceneManager& scene_manager,
  const sdk::gdb::IWorkspaceSP& wks) {
    sdk::vis::IDataSourceViewSP datasource_view;
    SDKResult res = scene_manager->AddDataSourceView(sdk::ScopedAny(wks), &datasource_view);
      return res;
  }

What layers does the scene manager create?

The scene manager creates the following predefined layers:

  1. chart_decoration
  2. tg1
  3. chart
  4. over_radar
  5. under_radar
  6. nodata

Predefined layer names are defined in the layers_manager_interface.h file.

How to control geodata portraying and visualization using portrayal parameters?

The scene manager encapsulates portrayal parameters. It uses them to control the visualization of the geographic data. You can get the portrayal parameters from the scene manager, modify them and set them back to the scene manager. The currently published list of portrayal parameters can be found here.

See the code snippet below for details:

C++

  SDKResult SetDisplayBase(const sdk::vis::ISceneManagerSP& scene_manager) {
    sdk::vis::IPortrayalParametersSP portrayal_parameters;
    SDKResult res = scene_manager->GetPortrayalParameters(&portrayal_parameters);
    if (SDK_FAILED(res))
      return res; // An error occurred
    portrayal_parameters->SetParameter(sdk::vis::kPP_SafetyContour, sdk::ScopedAny(30.0));
    return scene_manager->SetPortrayalParameters(portrayal_parameters);
  } 

How to manage the scene projection?

The scene manager creates a Mercator projection during initialization. It is also possible to replace it with a projection created by the application. See the sdk::vis::ISceneManager::SetProjection method.

To change projection parameters you should get the pointer to the sdk::crs::IProjection interface from the scene manager, then you should get the sdk::crs::IProjectionParameters from the projection, modify them and set them back.

See the code snippet below for details:

C++

  SDKResult SetProjectionResolution(const sdk::crs::IProjectionSP& projection, double dpm) {
    sdk::crs::IProjectionParameters projection_parameters;
    SDKResult res = projection->GetProjectionParameters(&projection_parameters);
    if (SDK_FAILED(res))
      return res; // Failed to get projection parameters
    projection_parameters->SetParameterValueByID(sdk::crs::kProjPar_CoordinateUnit, 1/dpm);
    return projection->SetProjectionParameters(projection_parameters);  
  }

There is another simpler way if you just need to change position and/or scale: use the ISceneControl::SetSceneParameters method.

How to manage the scene view using the scene viewport?

A viewport is the rectangular viewing region of the scene. The scene viewport has the size, position and three predefined matrices for translate, rotate and scale.

See the code snippet below for details:

C++

  void SetZoomRatio(float zoom_ratio, const sdk::vis::ISceneManagerSP scene_manager) {
    sdk::vis::ISceneManagerControlSP scene_control = 
      sdk::GetInterfaceT<sdk::vis::ISceneControl>(scene_manager);
    if (!scene_control)
      return; // Failed to get the sdk::vis::ISceneControl interface
    sdk::vis::scene::IScene2DViewportBaseSP viewport_base;
    if (SDK_FAILED(m_scene_control->GetViewport(viewport_base)))
      return; // Failed to get a viewport
    sdk::vis::scene::IScene2DViewportSimpleSP viewport = 
      sdk::GetInterfaceT<sdk::vis::scene::IScene2DViewportSimple>(viewport_base);
    viewport->SetScale(zoom_ratio, NULL);
  }

How to use the display groups manager to control the chart display content?

See the Display Groups article.

How to simplify implementation of the Follow Vessel Mode?

To simplify implementation of the Follow Vessel Mode, the SDK provides a special boolean parameter kSceneManagerPropertyID_FollowVesselMode (to be set through the ISceneManager::SetProperty method).

With this mode on, Projection and Viewport parameters are interpreted in a special way:

  1. The projection centre is the position of the vessel, rather than the screen centre (as it usually is).
  2. Shift parameters for the Viewport indicate the desired position of the vessel on the screen in the Viewport's coordinate system (the centre is the screen centre, x to the right, y up).
  3. Other Viewport and Projection parameters are interpreted in a regular way.

This mode also can use another parameter: kSceneManagerPropertyID_NoChartViewportRotate. When set to True, the chart image turns only after the turned chart has been redrawn; as opposed to the regular mode when the chart turns instantly using the "matrix" turn of the previous chart image. NOTE that this property can only be used together with the kSceneManagerPropertyID_FollowVesselMode property.

How to implement Owner Draw?

With the SDK, you can implement the owner draw function to plot your own graphical objects in the chart.

First of all, to implement the owner draw, you should implement your own renderer: the sdk::vis::scene::IRenderer interface.

The drawing is done using interfaces of the graphics library.

To enable the renderer, pass it to the layer you are creating in visman. Then when it’s turn of the layer to be drawn, the renderer is called.

The renderer has two main methods: SetContext and Render.

visman calls the SetContext method when initializing the layer and passes the object to draw in (canvas or texture) and the graphical context with drawing tools with the method. Store the drawing tools. The object to draw in is called a resource. Resources are described in enum ResourceType. Currently, only the kResourceType_RenderTarget option is available.

After that, every time the application draws, visman calls the Render method with the current scene parameters.

To get the scene parameters, use IRenderContext.

Parameters are described in visualizationlayer\inc\visman\layers_manager_interface.h - enum SceneRendererParameters.

Drawing, that is creating and plotting primitives, is done with the graphical context (graphics).

Make sure you take the current rendering parameters from IRenderContext when you draw a scene, otherwise the drawing will not match the chart. The most important rendering parameters are: projection with position and scale, angle, dpm, portrayal parameters (IPortrayalParameters).

If you need S52 colors, you can get them with the ISymbolSetCatalogSP interface that provides symbolization resources. Keep track of the palette (provided by IRenderContext) and recreate resources with colors.

How to visualize grounding check using software backend?

At initialization step, add a special grounding check scene layer:

C++

  sdk::vis::ISceneLayersManagerSP scene_layers_manager;
  res = scene_manager->GetSceneLayersManager(scene_layers_manager);

  sdk::vis::ISceneLayerSP gc_layer;
  res = scene_layers_manager->CreateLayer(sdk::ScopedString(gc_layer_name), 
          gc_layer_priority_above_chart,
          sdk::PointF2D(), sdk::SizeF(), layer_flags, sdk::ScopedString(""),
          sdk::ScopedString(""), sdk::ScopedAny(sdk::vis::kSceneLayerRenderer_GroundingCheck),
          gc_layer, nullptr);

  res = scene_layers_manager->AddLayer(gc_layer, sdk::vis::scene::kSceneLayerID_Root);

Later, after each grounding check is performed, pass the grounding check result to the layer:

C++

  sdk::vis::ISceneLayersManagerSP scene_layers_manager;
  res = scene_manager->GetSceneLayersManager(scene_layers_manager);

  sdk::vis::ISceneLayerSP gc_layer;
  res = scene_layers_manager->FindLayerByName(
    sdk::ScopedString(gc_layer_name), gc_layer);

  gc_layer->SetProperty(sdk::vis::kSceneLayerProperty_Data, sdk::ScopedAny(gc_result));
  gc_layer->SetDirty(true);

Then push rendering.

How to visualize radar overlay using software backend?

First, you need to implement a specific radar external renderer (see a sample in ecdis_demo_1). This renderer should prepare a raster image of the radar data and draw it when asked.

At initialization step, create a radar renderer and add a scene layer:

C++

  sdk::vis::ISceneLayersManagerSP scene_layers_manager;
  res = scene_manager->GetSceneLayersManager(scene_layers_manager);

  sdk::ISDKComponentSP ref_to_radar_renderer = new RadarRenderer(); 

  sdk::vis::ISceneLayerSP radar_layer;
  res = scene_layers_manager->CreateLayer(sdk::ScopedString(radr_layer_name), 
          sdk::vis::kSceneLayerPriority_Radar,
          sdk::PointF2D(), sdk::SizeF(), layer_flags, sdk::ScopedString(""),
          sdk::ScopedString(""), sdk::ScopedAny(ref_to_radar_renderer),
          radar_layer, nullptr);

  res = scene_layers_manager->AddLayer(radar_layer, sdk::vis::scene::kSceneLayerID_Root);

Make sure to switch on the radar layer-set for correct splitting of chart data (as some chart objects should be drawn below the radar image and some above it):

C++

  scene_layers_manager->AddLayerSet(sdk::ScopedString(sdk::vis::kSceneLayerSet_Radar));

Later, when the vessel changes its position (and the radar position as well) or new radar data comes, or the palette is changed, you should push rendering.

During scene rendering, the RadarRenderer::Render() method of the radar external renderer will be called. It is expected that it will prepare a radar image and will draw it at an appropriate position.

To prepare an image, you can use the RadarConversion component (same code in the ecdis_demo_1):

C++

  // Create once at renderer creation
 sdk::ecdislayer::radar::IRadarConversionSP radar_conversion;
 GetComponentInstanceT<sdk::ecdislayer::radar::IRadarConversion>(sdk::ecdislayer::radar::kRadarConversionCID, &radar_conversion);

  // Set radar data into 'radar_conversion' component
  {
    sdk::ecdislayer::radar::RADAR_SCAN_DATA radar_scan_data;
    radar_scan_data.m_data = rays_data.data();
    radar_scan_data.m_data_size = static_cast<SDKUInt32>(rays_data.size());
    radar_scan_data.m_bits_per_pixel = 8;
    radar_scan_data.m_alignment = 1;
    radar_scan_data.m_sweep_header_size = 0;
    radar_scan_data.m_sweep_size = static_cast<SDKUInt32>(pixels_per_ray);
    radar_scan_data.m_pixel_count = static_cast<SDKUInt32>(pixels_per_ray);
    radar_scan_data.m_sweep_count = static_cast<SDKUInt32>(total_rays_num);

    res = radar_conversion->SetRadarScanData(radar_scan_data);
    res = radar_conversion->SetRadarPosition(radar_pos.lat, radar_pos.lon);
    res = radar_conversion->SetRadarParameters(range_nm, 0.0, radar_heading);
  }

  // Prepare transformation and projection
  sdk::CMatrix3X2 transform;
  sdk::crs::IProjectionSP radar_projection;
  {
    // Get image rotate matrix
    sdk::CMatrix3X2 rotate;
    rotate.Rotate(static_cast<float>(-scene_angle), sdk::PointF2D(0, 0));
    // Get image transform matrix
    sdk::CMatrix3X2 translate;
    translate.Translate(static_cast<float>(viewport_size.width) / 2,
      static_cast<float>(viewport_size.height) / 2);
    // Get final transformation
    transform = sdk::CMatrix3X2::Multiply(rotate, translate);

    // Prepare projection
    res = projection->Clone(&radar_projection);
    sdk::crs::IProjectionParametersSP parameters;
    res = radar_projection->GetProjectionParameters(&parameters);
    parameters->SetParameterValueByID(sdk::crs::kProjPar_Azimuth, 0);
    res = radar_projection->SetProjectionParameters(parameters);
  }

  // Generate image in 'radar_conversion' component
  res = radar_conversion->GenerateRadarImage(
    viewport_size.width, viewport_size.height, radar_projection, transform);
  
  // Get image data from 'radar_conversion' component
  sdk::ecdislayer::radar::IMAGE_PARAMETERS image_data;
  {
    SDKGeoPointRect geo_frame = {};
    image_data.m_data = nullptr;
    image_data.m_data_size = 0;
    image_data.m_bits_per_pixel = 32;
    image_data.m_alignment = 4;
    image_data.m_width = viewport_size.width;
    image_data.m_height = viewport_size.height;

    // First call - get image data size
    radar_conversion->GetRadarImage(
      image_data, nullptr, nullptr, &geo_frame);
    if (0 == image_data.m_data_size)
      return Ok;

    data_buffer.resize(image_data.m_data_size);
    image_data.m_data = data_buffer.data();

    // Second call - get image data
    res = radar_conversion->GetRadarImage(
      image_data, nullptr, nullptr, &geo_frame);

    // Update image data to make it green (see function in the ecdis_demo_1)
    static const SDKUInt32 kImgColor = 0xFF00FF00; // green
    ProcessRawImage(image_data, sdk::ColorF(kImgColor));
  }

  // Create bitmap
  SDKSize size = { image_data.m_width, image_data.m_height };
  res = render_target->CreateBitmapFromRawData(size, 
    image_data.m_line_size, gfx::kPixelFormat_BGRA_8888, 
    image_data.m_data, bitmap);

To draw the prepared bitmap, call the DrawBitmap method of RenderTarget:

C++

  render_target->RemoveTransformationMatrixes();

  // Draw image
  SDKSize bitmap_size = {};
  res = bitmap->GetSize(bitmap_size);

  SDKRectF2D src_rect = SDKRectF2D();
  src_rect.x = 0;
  src_rect.y = 0;
  src_rect.width = static_cast<float>(bitmap_size.width);
  src_rect.height = static_cast<float>(bitmap_size.height);
  SDKRectF2D dst_rect = SDKRectF2D();
  dst_rect.x = -static_cast<float>(bitmap_size.width) / 2;
  dst_rect.y = -static_cast<float>(bitmap_size.height) / 2;
  dst_rect.width = static_cast<float>(bitmap_size.width);
  dst_rect.height = static_cast<float>(bitmap_size.height);
  res = render_target->DrawBitmap(bitmap, src_rect, dst_rect);

OpenGL rendering backend

How to initialize the SDK OpenGL rendering backend?

Create an sdk::visgl::kSceneManagerGLCID component and get the sdk::visgl::ISceneManagerGL interface. See inc/visualizationlayer/inc/vismangl/component_ids.h and inc/visualizationlayer/inc/vismangl/scene_manager_gl_interface.h where required constants are defined.

Create initialization parameters using the sdk::visgl::CreateSceneManagerGLInitialParameters helper found in inc/visualizationlayer/inc/vismangl/scene_manager_gl_helper.h as follows:

C++

         
  ISDKParametersSP init_params;
  sdk::visgl::CreateSceneManagerGLInitialParameters(
    L"scene_manager",      // Any name.
    gl_type,               // OpenGL subset or auto detected, see SceneManagerGLType
                           // in scene_manager_gl_interface.h for reference.
    context_to_share_with, // Pointer to ISDKParameters that contains
                           // a valid platform-specific OpenGL context to share with.
                           // It can be nullptr in case if you are not going to
                           // or cannot use context sharing.
    ext_created_ctx,       // Pointer to sdk::gl2d::IGL2DExtCreatedGlCtx that contains
                           // OpenGL context created by the application. It can be nullptr.
    scene_manager_gl,      // Pointer to ISceneManagerGL which is the previously
                           // instantiated OpenGL scene manager instance. 
                           // It is intended to optimize the use of OpenGL resources, 
                           // e.g. if the application is not a single view. It can be nullptr.
    sdk::gl2d::kGL2DTextureFormat_RGBA, // Format of the scene texture.
    static_cast<SDKUInt32>(scene_size.width),  // Scene width.
    static_cast<SDKUInt32>(scene_size.height), // Scene height.
    dpm,                                       // Scene resolution in DPM.
    0,                                         // Flags. Reserved, 0 for now.
    init_params); // The output.

Initialize the scene manager with the created parameters. Check if it succeeded. In case of failure, check the returned error code and reports from the error logs located in the ($sdk_program_data)/logging.

Get the scene control:

C++

  sdk::visgl::ISceneControlGLSP scene_control = scene_manager.GetInterface<sdk::visgl::ISceneControlGL>();

Connect the scene control to listen events from the scene manager.

C++

  sdk::visgl::ISceneManagerGLCallbackSP callback(
    new SceneManagerEventsListener(this));
  SDKUInt32 scene_listener_cookie = static_cast<SDKUInt32>(-1);
  res = AdviseToConnectionPoint(scene_manager,
    sdk::visgl::ISceneManagerGLCallback::IID(), callback,
    scene_listener_cookie);

Where SceneManagerEventsListener is an implementation of sdk::visgl::ISceneManagerGLCallback.

Below is the list of recommended settings for the scene manager.

  • Enable OpenGL data cache.

    Pros: speeds up data loading when using it not for the first time.

    Cons: occupies local disk space. From KBytes to over 10 MBytes per cell, depending on the cell size.

    C++

      m_scene_manager->SetProperty(
        sdk::visgl::kSceneManagerGLProperty_GLDataCache_On,
        sdk::ScopedAny(true));
    
  • Limit the amount of memory that the graphic cache is allowed to occupy in the application memory space. In MB.

    The graphics cache is a collection of images of data sets in a form that allows them to be rendered without additional preparation. It's stored in the application memory space.

    C++

                    
      m_scene_manager->SetProperty(
        sdk::visgl::kSceneManagerGLProperty_GraphicCache_MaxAppMemoryMb,
        sdk::ScopedAny(1300)); // ~1.3GB, you can define more or less depending on your application preference.
    
  • How to add an ENC data source to a scene?

    The scene is split into layers. The data source must be bound with the layer. The layer for ENC is predefined and created by the scene manager, its name is defined by kSceneLayerName_Chart (defined in //inc/visualizationlayer/inc/vismangl/scene_layers_manager_gl_interface.h).

    Use IDataSourcesManager to add an ENC data source to the scene.

    1. Get the data source manager from the scene manager.

      C++

        sdk::visgl::IDataSourcesManagerSP datasources_manager;
        scene_manager->GetDataSourceManager(datasources_manager);
      
    2. Add data source to data source manager.

      C++

        ISDKParametersSP params(new ParametersVector());
        // Possible values for the flag are kAddDataSourceFlag_None,
        // kAddDataSourceFlag_ChartDatabase, kAddDataSourceFlag_WeatherDatabase and kAddDataSourceFlag_RoutesDatabase.
        // These flags are used to recognize the group in group operations. These flags are not used by the SDK.
        // The flag value can be kAddDataSourceFlag_None if you don't need to recognize the data source by flag,
        // you can also add your own flag.
        params->SetParameter(sdk::visgl::kDataSourceParameter_Flags, sdk::ScopedAny(flags));
      
        sdk::visgl::IDataSourceBaseSP datasource;
        datasources_manager->AddDataSource(
          sdk::ScopedString(data_source_name), // Any unique name.
          sdk::ScopedAny(wks), // Pointer to IWorkspace of an opened workspace.
          params, // Pointer to the parameters that holds the flags to be assigned to the added data source. 
          datasource);
      
    3. Get the layer intended for ENC data sources.

      C++

        sdk::visgl::ISceneLayerGLSP scene_layer_chart;
        res = scene_layers_manager->FindLayer(sdk::ScopedString(sdk::visgl::kSceneLayerName_Chart), scene_layer_chart);
      
    4. Bind the data source by name.

      C++

        scene_layer_chart->BindDataSource(sdk::ScopedString(wks_name));
      

    How to add Overview to the scene?

    Use the following procedure to add Overview to the scene:

    1. Open Overview workspace.
    2. Add data source to the data source manager.
      C++
                
        sdk::visgl::IDataSourcesManagerSP datasources_manager;
        scene_manager->GetDataSourceManager(datasources_manager);
      
        sdk::ISDKParametersSP params(new sdk::ParametersVector());
        params->SetParameter(sdk::visgl::kDataSourceParameter_Flags, sdk::ScopedAny(sdk::visgl::kAddDataSourceFlag_None));
      
        sdk::visgl::IDataSourceBaseSP datasource;
        datasources_manager->AddDataSource(
          sdk::ScopedString(overview_ds_name), // The Overview data source name.
          sdk::ScopedAny(overview_wks), // Pointer to IWorkspace of Overview workspace.
          params, // Pointer to the parameters that holds the flags to be assigned to the added data source. 
          datasource);
      
    3. Create a layer for Overview.
      C++
                    
        const SDKUInt64 ov_flags =
          sdk::visgl::kSceneLayerGLFlag_Overview |           // Indicates the Overview layer.
          sdk::visgl::kSceneLayerGLFlag_PortrayalDependent | // Automatically re-render if portrayal changed.
          sdk::visgl::kSceneLayerGLFlag_UsePortrayal;        // Use internal portrayal procedures.
      
        sdk::visgl::ISceneLayerGLSP scene_layer_overview;
        res = scene_layers_manager->AddLayer(
          sdk::ScopedString(ov_layer_name),                   // Any unique name.
          sdk::visgl::kSceneLayerGLType_Default,              // Default layer type, there are few specific layer types
                                                              // i.e. kSceneLayerGLType_Radar and kSceneLayerGLType_GroundingCheck.
          sdk::visgl::kSceneLayerGLPriority_Overview,         // Layer priority specifically for the Overview,
                                                              // defined in //inc/visualizationlayer/inc/vismangl/scene_layers_manager_gl_interface.h
          ov_flags,                                           // Scene layer flags.
          sdk::ScopedAny(sdk::visgl::kSceneRenderer_Default), // Currently only one type of scene layer renderer type is defined.
          sdk::ISDKParametersSP(),                            // No additional parameters.
          &scene_layer_overview);
      
    4. Bind the data source.
      C++
        scene_layer_overview->BindDataSource(sdk::ScopedString(overview_wks_name));
      

    How to visualize grounding check using OpenGL backend?

    At initialization step, add a special grounding check scene layer:

    C++

      sdk::visgl::ISceneLayersManagerGLSP scene_layers_manager;
      res = scene_manager->GetSceneLayersManager(scene_layers_manager);
    
      sdk::visgl::ISceneLayerGLSP gc_layer;
      res = scene_layers_manager->AddLayer(
        sdk::ScopedString(gc_layer_name),             // Grounding check layer name.
        sdk::visgl::kSceneLayerGLType_GroundingCheck, // Predifined specific layer type.
        gc_layer_priority_above_chart,                // Layer priority.
        sdk::visgl::kSceneLayerGLFlag_NoFlags,        // Scene layer flags.
        sdk::ScopedAny(),                             // No renderer info is required.
        sdk::ISDKParametersSP(),                      // No additional parameters.
        &gc_layer);
    

    A special grounding check data source is automatically created when you create a layer with type kSceneLayerGLType_GroundingCheck. Use this data source to pass grounding check data to the visualization subsystem. Take the reference to the data source from the layer:

    C++

      sdk::ScopedAny data_source_gc_any;
      scene_layer_gc->GetProperty(sdk::visgl::kSceneLayerGLProperty_DataSources, data_source_gc_any);
    
      sdk::visgl::IDataSourceGroundingCheckSP data_source_gc = 
        data_source_gc_any.GetInterface<sdk::visgl::IDataSourceGroundingCheck>();
    

    Later, after each grounding check is performed, pass the grounding check result to the data source:

    C++

      data_source_gc->SetGroundingCheckResult(gc_result, sdk::visgl::kGroundingCheckResultSetType_Direct, nullptr);
    
      gc_layer->SetDirty(true);
    

    Then push rendering.

    How to visualize radar overlay using OpenGL backend?

    Main difference from the software version is that you don't need to implement an external renderer or prepare a bitmap. Just pass raw radar data to the visualization engine.

    At initialization step, add a special radar scene layer:

    C++

      sdk::visgl::ISceneLayersManagerGLSP scene_layers_manager;
      res = scene_manager->GetSceneLayersManager(scene_layers_manager);
    
      sdk::visgl::ISceneLayerGLSP radar_layer;
      res = scene_layers_manager->AddLayer(
        sdk::ScopedString(radar_layer_name),      // Radar layer name.
        sdk::visgl::kSceneLayerGLType_Radar,      // Predefined specific layer type.
        sdk::visgl::kSceneLayerGLPriority_Radar,  // Layer priority specifically for the Radar,
                                                  // defined in //inc/visualizationlayer/inc/vismangl/scene_layers_manager_gl_interface.h
        sdk::visgl::kSceneLayerGLFlag_NoFlags,    // Scene layer flags.
        sdk::ScopedAny(),                         // No renderer info is required.
        sdk::ISDKParametersSP(),                  // No additional parameters.
        &radar_layer);
    

    A special radar data source is automatically created when you create a layer with type kSceneLayerGLType_Radar. Use this data source to pass radar data to the visualization subsystem. Take the reference to the data source from the data source manager:

    C++

      sdk::visgl::IDataSourcesManagerSP datasources_manager;
      res = m_scene_manager->GetDataSourceManager(datasources_manager);
    
      sdk::visgl::IDataSourceBaseSP radar_data_source_base;
      res = datasources_manager->FindDataSource(
        sdk::ScopedString(sdk::visgl::kDataSourcePredefinedName_Radar), radar_data_source_base);
    
      sdk::visgl::IDataSourceRadarSP radar_data_source =
        radar_data_source_base.GetInterface<sdk::visgl::IDataSourceRadar>();
    

    To set radar overlay parameters, use the SetRadarImageParameters method:

    C++

      const SDKUInt32 total_rays_num = 1024;
      const SDKUInt32 pixels_per_ray = 1024;
      const SDKUInt32 range_nm = 4;
      res = radar_data_source->SetRadarImageParameters(
        total_rays_num, pixels_per_ray, range_nm, sdk::ISDKParametersSP());
    

    When radar overlay is on, switch the kPP_UseOverRadarFlag parameter to split chart data (as some chart objects should be drawn below the radar image and some above it):

    C++

      portrayal_manager->GetPortrayalParameters(portrayal_params);
      portrayal_params->SetParameter(sdk::vis::kPP_UseOverRadarFlag, sdk::ScopedAny(true));
      portrayal_manager->SetPortrayalParameters(portrayal_params);
    

    Later, when the vessel changes its position (and radar position as well) or new radar data come, update the data source:

    C++

      res = radar_data_source->UpdateRadarImageData(lon, lat, angle,
        0UL, static_cast<SDKUInt32>(total_rays_num), 
        &rays_data.front(), static_cast<SDKUInt32>(rays_data.size()));
    

    You can pass any portion of the radar rays. Only specified part of the overlay will be updated.

    Coordinate systems, projections, datums, geodetic functions

    What projections does SDK support?

    The SDK currently implements only Mercator projection. Implementation of the Transverse Mercator and the Lambert Conformal Conic projection is in our plans.

    How to create a projection?

    To create a projection, perform the following steps:

    1. Create an sdk::crs::kCoordinateSystemFactoryCID component
    2. Get the sdk::crs::ICoordinateSystemFactory interface
    3. Invoke the sdk::crs::ICoordinateSystemFactory::CreateProjection method

    How to change projection parameters?

    To change projection parameters perform the following steps:

    1. Get the sdk::crs::IProjectionParameters itnerface from the sdk::crs::IProjection interface
    2. Set the new parameters using the sdk::crs::IProjectionParameters::SetParameterValueByID method
    3. Set the modified projection parameters back to the projection using the sdk::crs::IProjection::SetProjectionParameters method

    See the code snippet below for details:

    C++

      SDKResult SetProjectionResolution(const sdk::crs::IProjectionSP& projection, double dpm) {
        sdk::crs::IProjectionParameters projection_parameters;
        SDKResult res = projection->GetProjectionParameters(&projection_parameters);
        if (SDK_FAILED(res))
          return res; // Failed to get projection parameters
        projection_parameters->SetParameterValueByID(sdk::crs::kProjPar_CoordinateUnit, 1/dpm);
        return projection->SetProjectionParameters(projection_parameters);  
      }