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

Introduction

Manual updating of a chart is a way to edit the chart without modifying its content. A manual update is a separate chart linked to its owner. Manual updating affects the chart access and as a result it also affects the chart display, anti-grounding task, and all other tasks related to the access to the chart content.

The front-end of the chart manual updating is the ManualUpdates component that implements the IManualUpdates interface.

  • The ManualUpdates component id is defined in the /datalayer/inc/manual_update/component_ids.h file.
  • The IManualUpdates interface is defined in the /datalayer/inc/manual_update/manual_updates_interface.h file.

To initialize manual updating the application has to pass the path to a manual updates database in the workspace configuration at the time when it opens a SENC geodatabase. See an example below (namespaces are omitted):

  IWorkspaceConfigurationSP config;
  if (SDK_FAILED(wks_factory->CreateWorkspaceConfiguration(&config)))
    return;
  if (SDK_FAILED(config->SetConfigurationParameter(
    kWorkspaceConfigurationParameter_RootPath, ScopedAny(path))))
    return;
  if (SDK_OK(config->SetConfigurationParameter(
    kWorkspaceConfigurationParameter_ManualUpdateRootPath, 
    ScopedAny(manual_update_path))))
    return;
  IWokspaceSP wks;
  wks_factory->Open(config, &wks);
  

For the manual updates path we recommend to use the same last path component as in the SENC geodatabase path, e.g. geodatabases/PRIMAR and for the manual updates manual_update/PRIMAR.

Basic Operations

To perform manual updating operations you can use the following methods of the IManualUpdates interface:

  • To create a new feature, use CreateFeature:
    virtual SDKResult SDK_CALLTYPE CreateFeature(const IFeatureDatasetSP& dataset, sdk::geometry::GeometryType type, 
    sdk::geometry::PointDimension coord_dim, IFeatureEditSP& feature_editor) throw() = 0; 
    
  • To edit a feature, use EditFeature:
    virtual SDKResult SDK_CALLTYPE EditFeature(const IFeatureSP& feature, IFeatureEditSP& feature_editor) throw() = 0; 
    
  • To delete a feature, use DeleteFeature:
    virtual SDKResult SDK_CALLTYPE DeleteFeature(const IFeatureSP& feature, DeleteMode mode) throw() = 0;
    

With the DeleteFeature you can physically delete a manual update record if you pass a feature from the manual update dataset and DeleteMode_DestroyRecord as a mode.

  • To clear all updates from a specified dataset, use ClearUpdates:
    virtual SDKResult SDK_CALLTYPE ClearUpdates(const SDK_GDB_NAMRSPACE::IFeatureDatasetSP & dataset) throw() = 0; 
    

Other Operations

Hiding manual updates

If you want to make a manual update invisible you can use the IObjectEdit::SetFlags method. Pass the senc::FEATS_TRP_FLAGS.m_IS_INVISIBLE. See the sample code below:

senc::FEATS_TRP_FLAGS feature_flags;
feature_flags.m_IS_INVISIBLE = 1;
IObjectEditSP object_edit;
object_edit->SetFlags(feature_flags.m_RAWF);

For the visibility flag to come into effect, use the kGetFeaturesFlags_MU_SkipInvisibleManualUpdates flag in the kGetFeaturesParameter_Flags of the IFeatureDataset::GetFeatures call.

Removing old updates

To remove manual updates created 3 months back from the operations, use the kGetFeaturesParameter_MU_DeltaTimeLimitToSkipManualUpdates for the IFeatureDataset::GetFeatures method. The parameter is set in milliseconds.

For the GetFeatures parameters to come into effect for the chart display, the application can pass them via the visualization manager IDataSourceView::SetFeatureQueryFilter.

Dataset and feature enumeration

There are several flags and enumeration parameters that can be used to control the dataset and/or feature enumeration.

Feature/manual update relationship

To get the relationship between a feature and one of its manual updates, use kRelationshipClass_ManualUpdates in IObject.GetRelationships.

Manual update/feature relationship

To get the relationship between a manual update and its feature, use the IObject.GetRelationships method of the manual update feature with the kRelationshipClass_ManualUpdates class.

Editing manual update date/time attributes

To encode date/time attributes of manual updates, use the ANY_TIME_PACKED structure.

See a sample for reference (click to open)

// --gtest_filter=mu_feature_editor_tests.add_date_attribute
TEST_F(mu_feature_editor_tests, add_date_attribute)
{
  std::wstring dataset_name = kTdsDatasets[0];
  std::wstring senc_dataset_name = kTdsDatasetNames[0];
 
  DestroySlaveDatabase();
  bool b_exists = FileExists(dataset_name);
  ASSERT_TRUE( false == b_exists );
 
  SDKResult res;
  ClassCode code = s101::kFCC_BCNCAR;
  SDKUInt32 attr_code = s101::kFAC_BOYSHP;
  SDKUInt16 attr_val = 1;
  FOID foid_added = { 0 };
 
  ScopedAny dt_full(0);
  ANY_TYPE(&dt_full) = kSDKAnyType_SDKTimePacked;
  ANY_TIME_PACKED_YEAR(&dt_full) = 1941;
  ANY_TIME_PACKED_MONTH(&dt_full) = 6;
  ANY_TIME_PACKED_MDAY(&dt_full) = 22;
  ANY_TIME_PACKED_HOUR(&dt_full) = 4;
  const ClassCode dt_full_code = s101::kFAC_DATSTA;
  const std::wstring dt_full_str = L"19410622T040000";
 
  ScopedAny dt_md(0);
  ANY_TYPE(&dt_md) = kSDKAnyType_SDKTimePacked;
  ANY_TIME_PACKED_MONTH(&dt_md) = 6;
  ANY_TIME_PACKED_MDAY(&dt_md) = 22;
  ANY_TIME_PACKED_HOUR(&dt_md) = kSDK_PACKED_TIME_INVALID_HOUR;
  const ClassCode dt_md_code = s101::kFAC_PERSTA;
  const std::wstring dt_md_str = L"--0622";
 
  ScopedAny dt_m(0);
  ANY_TYPE(&dt_m) = kSDKAnyType_SDKTimePacked;
  ANY_TIME_PACKED_MONTH(&dt_m) = 6;
  ANY_TIME_PACKED_HOUR(&dt_m) = kSDK_PACKED_TIME_INVALID_HOUR;
  const ClassCode dt_m_code = s101::kFAC_PEREND;
  const std::wstring dt_m_str = L"--06";
 
  ScopedAny dt_invalid(0);
  ANY_TYPE(&dt_invalid) = kSDKAnyType_SDKTimePacked;
  ANY_TIME_PACKED_YEAR(&dt_invalid) = kSDK_PACKED_TIME_INVALID_YEAR;
  ANY_TIME_PACKED_MONTH(&dt_invalid) = kSDK_PACKED_TIME_INVALID_MONTH;
  ANY_TIME_PACKED_MDAY(&dt_invalid) = kSDK_PACKED_TIME_INVALID_MDAY;
  ANY_TIME_PACKED_HOUR(&dt_invalid) = kSDK_PACKED_TIME_INVALID_HOUR;
  ANY_TIME_PACKED_MINUTE(&dt_invalid) = kSDK_PACKED_TIME_INVALID_MINUTE;
  ANY_TIME_PACKED_SECOND(&dt_invalid) = kSDK_PACKED_TIME_INVALID_SECOND;
  const ClassCode dt_invalid_code = s101::kFAC_DATEND;
 
  ScopedAny dt_only_time_valid(0);
  ANY_TYPE(&dt_only_time_valid) = kSDKAnyType_SDKTimePacked;
  ANY_TIME_PACKED_YEAR(&dt_only_time_valid) = kSDK_PACKED_TIME_INVALID_YEAR;
  ANY_TIME_PACKED_MONTH(&dt_only_time_valid) = kSDK_PACKED_TIME_INVALID_MONTH;
  ANY_TIME_PACKED_MDAY(&dt_only_time_valid) = kSDK_PACKED_TIME_INVALID_MDAY;
  ANY_TIME_PACKED_HOUR(&dt_only_time_valid) = 01;
  ANY_TIME_PACKED_MINUTE(&dt_only_time_valid) = 02;
  ANY_TIME_PACKED_SECOND(&dt_only_time_valid) = 03;
  const ClassCode dt_only_time_valid_code = s101::kFAC_SURSTA;
 
  ScopedAny dt_only_date_valid(0);
  ANY_TYPE(&dt_only_date_valid) = kSDKAnyType_SDKTimePacked;
  ANY_TIME_PACKED_YEAR(&dt_only_date_valid) = 2014;
  ANY_TIME_PACKED_MONTH(&dt_only_date_valid) = 05;
  ANY_TIME_PACKED_MDAY(&dt_only_date_valid) = 14;
  ANY_TIME_PACKED_HOUR(&dt_only_date_valid) = kSDK_PACKED_TIME_INVALID_HOUR;
  ANY_TIME_PACKED_MINUTE(&dt_only_date_valid) = kSDK_PACKED_TIME_INVALID_MINUTE;
  ANY_TIME_PACKED_SECOND(&dt_only_date_valid) = kSDK_PACKED_TIME_INVALID_SECOND;
  const ClassCode dt_only_date_valid_code = s101::kFAC_SUREND;
 
 
  GeoPoint gp;
  {
    IFeatureDatasetSP dataset_master;
    OpenMasterDataset(dataset_master, m_wks_factory, m_config, 
      senc_dataset_name);
    ASSERT_TRUE(dataset_master != NULL);
 
    geometry::IEnvelopeSP envelope;
    res = dataset_master->GetBounds(&envelope);
    ASSERT_EQ(kSDKResult_Succeeded, res);
    ASSERT_TRUE(NULL != envelope);
 
    double x_min = 360, y_min = 360, x_max = 360, y_max = 360;
    res = envelope->GetCoordinates(kSDKAnyType_Double,
      &x_min, &y_min, &x_max, &y_max);
    ASSERT_EQ(kSDKResult_Succeeded, res);
    gp = GeoPoint(x_min + (x_max - x_min) / 2.,y_min + (y_max - y_min) / 2.);
 
    IFeatureEditSP feature_editor;
    res = m_mu->CreateFeature(dataset_master, 
      kGMT_Point, kGMPD_2, feature_editor);
    ASSERT_EQ(kSDKResult_Succeeded, res);
    ASSERT_TRUE(NULL != feature_editor);
 
    res = feature_editor->StartEditing();
    ASSERT_EQ(kSDKResult_Succeeded, res);
 
    res = feature_editor->SetObjectClassCode(code);
    ASSERT_EQ(kSDKResult_Succeeded, res);
    {
    ScopedFeatureEdit fe(feature_editor);
      fe.SetAttribute(attr_code, attr_val);
 
      fe.SetAttributeValue(dt_full_code, dt_full);
      fe.SetAttributeValue(dt_md_code, dt_md);
      fe.SetAttributeValue(dt_m_code, dt_m);
      fe.SetAttributeValue(dt_invalid_code, dt_invalid);
      fe.SetAttributeValue(dt_only_time_valid_code, dt_only_time_valid);
      fe.SetAttributeValue(dt_only_date_valid_code, dt_only_date_valid);
    }
    IGeometryEditSP geom_edit = 
      feature_editor.GetInterface();
    ASSERT_TRUE(NULL != geom_edit);
    res = geom_edit->AddPoints(geometry::kGMPT_GeoPoint, &gp, 1, -1, 0);
    ASSERT_EQ(kSDKResult_Succeeded, res);
 
    res = feature_editor->StopEditing(true, NULL);
    ASSERT_EQ(kSDKResult_Succeeded, res);
    IFeatureSP f = feature_editor.GetInterface();
    ASSERT_TRUE(NULL != f);
    ScopedAny foid_any;
    res = f->GetFOID(foid_any);
    ASSERT_EQ(kSDKResult_Succeeded, res);
 
    foid_added.foid = GetAnyValue(foid_any);
    ASSERT_TRUE(0 != foid_added.foid);
  }
  {
    IFeatureDatasetSP dataset_master;
    OpenMasterDataset(dataset_master, m_wks_factory, m_config, 
      senc_dataset_name);
    ASSERT_TRUE(NULL != dataset_master);
 
    IEnumFeatureSP enum_fea;
    res = dataset_master->GetFeatures(NULL, NULL, NULL, &enum_fea);
    ASSERT_EQ(kSDKResult_Succeeded, res);
    ASSERT_TRUE(NULL != enum_fea);
    bool is_found = false;
    for (IFeatureSP fea; SDK_OK(enum_fea->Next(&fea)); fea.Release())
    {
      ScopedAny foid_any;
      res = fea->GetFOID(foid_any);
      if (SDK_OK(res) && GetAnyValue(foid_any) == foid_added.foid)
      {
        SDKUInt64 flags;
        ASSERT_TRUE(SDK_OK(fea->GetFlags(flags)));
        senc::FEATS_TRP_FLAGS feature_flags;
        feature_flags.m_RAWF = static_cast(flags);
        ASSERT_TRUE(feature_flags.m_UPDATE_TYPE == 
          senc::kUpdateType_NonIntegratedUpdate);
        std::wstring dt_str;
        {
          IAttributeCollectionSP attributes;
          ASSERT_TRUE(SDK_OK(fea->GetAttributes(&attributes)));
 
          ScopedAny dt_full_returned;
          ASSERT_TRUE(SDK_OK(attributes->GetAttributeValue(dt_full_code, 
            dt_full_returned)));
          ASSERT_TRUE(dt_full == dt_full_returned);
 
          ScopedAny dt_md_returned;
          ASSERT_TRUE(SDK_OK(attributes->GetAttributeValue(dt_md_code, 
            dt_md_returned)));
          ASSERT_TRUE(dt_md == dt_md_returned);
 
          ScopedAny dt_m_returned;
          ASSERT_TRUE(SDK_OK(attributes->GetAttributeValue(dt_m_code, 
            dt_m_returned)));
          ASSERT_TRUE(dt_m == dt_m_returned);
 
          dt_m_returned.Clear();
          ASSERT_TRUE(SDK_OK(attributes->GetAttributeValue(dt_invalid_code, 
            dt_m_returned)));
          {
          IAttributeSP attr;
          ASSERT_TRUE(SDK_OK(attributes->FindAttribute(dt_invalid_code, 
            &attr)));
          ScopedAny returned_value;
          ASSERT_TRUE(SDK_OK(attr->GetValue(returned_value)));
          SDKUInt64 flags = 0;
          ASSERT_TRUE(SDK_OK(attr->GetFlags(flags)));
          ASSERT_TRUE(ANY_IS_TIME(&returned_value));
          ScopedAny time_packed;
          ASSERT_TRUE(SDK_OK(SDKTimeToSDKTimePacked(ANY_TIME(&returned_value), 
            flags, time_packed)));
          ASSERT_TRUE(dt_invalid == time_packed);
          }
          {
          IAttributeSP attr;
          ASSERT_TRUE(SDK_OK(attributes->FindAttribute(dt_only_time_valid_code, 
            &attr)));
          ScopedAny returned_value;
          ASSERT_TRUE(SDK_OK(attr->GetValue(returned_value)));
          SDKUInt64 flags = 0;
          ASSERT_TRUE(SDK_OK(attr->GetFlags(flags)));
          ASSERT_TRUE(ANY_IS_TIME(&returned_value));
          ScopedAny time_packed;
          ASSERT_TRUE(SDK_OK(SDKTimeToSDKTimePacked(ANY_TIME(&returned_value), 
            flags, time_packed)));
          ASSERT_TRUE(dt_only_time_valid == time_packed);
          }
          {
          IAttributeSP attr;
          ASSERT_TRUE(SDK_OK(attributes->FindAttribute(dt_only_date_valid_code, 
            &attr)));
          ScopedAny returned_value;
          ASSERT_TRUE(SDK_OK(attr->GetValue(returned_value)));
          SDKUInt64 flags = 0;
          ASSERT_TRUE(SDK_OK(attr->GetFlags(flags)));
          ASSERT_TRUE(ANY_IS_TIME(&returned_value));
          ScopedAny time_packed;
          ASSERT_TRUE(SDK_OK(SDKTimeToSDKTimePacked(ANY_TIME(&returned_value), 
            flags, time_packed)));
          ASSERT_TRUE(dt_only_date_valid == time_packed);
          }
        }
        is_found = true;
        break;
      }
    }
    ASSERT_TRUE(is_found == true);
  }
}

  

Manual Updates Catalogue

The SDK provides a manual update catalogue to help developers to create a manual update editor. The catalogue is a simple XML file consisting of a list of features grouped in a hierarchical structure where root features are themes. There is no additional service on top of the catalogue. Applications can use it as it is.