Developer Reference V1.5.0.63 Release Notes

Version 1.5.x.x

A new version of ENCX is now available. Moving to this new version will break existing applications that use ENCX. Changes have been made to make the API more consistent with the behaviour of .NET languages where some workarounds were necessary previously. This article will run through the areas of change and the implications although in most cases the old workarounds will still work fine.

A breaking change

To use the new version of the library you will have to remove and re-add references to the ENCX COM library in your projects. This is to prevent people accidentally upgrading to the new version and then their applications getting caught out by changes at runtime. It also means that you can have an older version of your software (working with an old version of the library) running side by side with new software (using the new version of the library).

After you point your projects at the new library the likelihood is that it will not compile. This article is to explain why not and how to fix it up.

Properties that return GeoPix

Previously the GeoPix property of the S57Draw class returned a new GeoPix every time. This code shows the behaviour...

S57Draw draw =  new S57Draw();
... // initialize everything
GeoPix geopix1 = draw.GeoPix;
GeoPix geopix2 = draw.GeoPix;
bool b = ReferenceEquals(geopix1, geopix2);

Now, when you ask for a property in .NET you expect to get a reference to something that is part of the parent object. So you expect to get the same thing if you ask for it twice. The code above does that and then asks the framework whether the two geopixes are actually pointing at the same instance. Previously the answer was b == false.

Due to the underlying implementation this was only a problem when checking the PointInView property of the GeoPix (PointInView is true if the last call to Point related to a point on the view). The following code needed a workaround...

PixelPoint pp = draw.GeoPix.Point(gp);
if(draw.GeoPix.PointInView)
{
    // this code does not get executed (ever)
}

The above was really dealing with two GeoPixes but the programmer expected only one. The workaround makes sure only one comes into play...

GeoPix geopix = draw.GeoPix;
PixelPoint pp = geopix.Point(gp);
if(geopix.PointInView)
    // this code gets executed if gp is in the view
}

All fixed now.

Also, a theoretically possible crash has been removed. It was possible to provoke a crash by destroying the parent drawing object and then using a reference to a GeoPix obtained from the destroyed parent - e.g. in .NET by using System.Runtime.InteropServices.Marshal.ReleaseComObject(encDraw).

GeoPix returned from the RasDraw

HCRF charts (raster charts issued by UKHO, Australian HO and the New Zealand HO) can be made up of several panels each of which can have a different scale, orientation and so on. For this reason any given view of a raster chart can have several GeoPixes pertaining to different parts of it. When the view rectangle is moved to look at another portion of the chart the collection of relevant GeoPixes can change as the panels that are visible change.

Highlight area shows a view with bit of two panels

The highlighted area shows a view comprising two panels, a small inset and the main panel. Two GeoPixes would be in the VisibleGeoPix results for this view. If the view were moved across to the far left of the chart four panels would be in the view.

There are two ways of getting GeoPixes from the RasDraw class - either ask for the GeoPix relating to a given pixel position (use the GeoPix method) or ask for all the GeoPixes in the view (the VisibleGeoPix property). Both of these return a snapshot of the situation at the time when you ask, results are valid until something changes the view. So use them quickly for what you need (drawing or hit-testing normally) and don't keep hold of GeoPix references. (Although the results returned by dodgy old GeoPix references may be out of whack it is still safe to call them - your application won't crash it'll just be wrong!).

Collections of GeoPix returned now have reference behaviour (if you ask for the nth element repeatedly you always get the same object). The collection however is a snapshot of the situation at the time of calling (if you call VisibleGeoPix repeatedly you always get different collection objects).

Where have S57Draw.Size and RasDraw.Rect gone?

A lot of people got caught out writing this sort of code:

S57Draw draw = new S57Draw;
... // do various init
draw.Size.Width = 200; // fail to set the width!

It didn't work because the Size property returned a copy of the size of the S57Draw object rather than a reference to its internal state. The copy got its width set to 200. The width of the S57Draw object stayed the same and some time down the line that copy which never even had a name got garbage collected.

Now this functionality is no longer a property; instead we have a pair of methods GetSize and SetSize. Because it is a pair of methods we hope it is a bit clearer that the PixelSize objects involved are not really parts of the S57Draw but just ways of passing the information around.

GetSize returns a PixelSize object containing a copy of the height and width of the S57Draw object.

SetSize sets the S57Draw object's height and width according to the PixelSize object that it is given.

For the same reason we have replaced the Rect property of the RasDraw class with GetRect and SetRect.

S57DisplaySettings

It is no longer possible to create an instance of the S57DisplaySettings class from scratch. We think that this class will normally be used as a property of an S57Draw object rather than a standalone object. But if you wish to detach one from an S57Draw object you can make a copy of an S57DisplaySettings object in two ways.

  • Use standard ICloneable .NET pattern (not typesafe)
  • Use the typesafe Copy method.

This extract from our test code illustrates...

// S57DisplaySettings settings = new S57DisplaySettings(); //not allowed!
S57Draw draw = new S57Draw();
S57DisplaySettings settings = draw.DisplaySettings;
S57DisplaySettings copied = settings.Copy();
// using ICloneable...
ICloneable able = (ICloneable) settings;
S57DisplaySettings cloned = (S57DisplaySettings) able.Clone();
bool bPass = !ReferenceEquals(settings, copied)
             && !ReferenceEquals(settings, cloned)
             && !ReferenceEquals(cloned, copied);

bPass == true as the original and the two copies are all not the same thing.

S57ManagerInitialisationData

This class allows you to control how the S57Manager class is initialized, it is a good way to point your program at the S-57 presentation library file rather than keeping it in the same folder as your executable.

(For the people in the US we spell it colour (with a u) not color over here in the UK. We have to put up with Microsoft spelling it wrong ;-) in their APIs.)

There have been a few changes here, again to make its behaviour a little more sensible from the point of view of developers targeting ENCX from .NET. The changes involve the colour values functionality which allows you to customize the colours that ENCX displays charts in.

If you ever want to use this functionality it will now not catch you out.

If you do use the functionality right now then it's worth reviewing your code - it will probably still work ok without any modification but you might be able to simplify it now if you want.

The ColourValues property now returns a reference to the same collection every time and the S57ColourValue.Colour property does likewise. Basically this allows you to change stuff together like this...

S57ManagerInitialisationData init = new S57ManagerInitialisationData();
... // do various stuff
// do something with the Colour property of the 1st item in the ColourValues collection, a property of the S57ManagerInitialisationData class
init.ColourValues[1].Colour.Construct("CHRED");

Great. You can do what you would expect to be able to do. Only previously it wouldn't have worked.

Changes to Chart Installation and Management Functionality

As chart suppliers are coming more into line with a standard interpretation of the S-63 standard it has been possible to simplify chart and licensing management in ENCX.

The S57Provider class, collection and enum has been removed. It is no longer necessary to specify a provider (using the enum) when installing charts or permits programmatically. In fact all functions which required providers have been removed e.g. S57Manager's Providers property is no more.

It is no longer necessary to install certificates into a SENC. They are now built in to the library.

Functions affected:

  • InstallCell: provider enum need not be specified any more.
  • InstallTransmittal: provider enum need not be specified any more.
  • InstallZippedTransmittal: provider enum need not be specified any more.
  • InstallCertificate: removed completely.
  • InstallPermitFile:  provider enum need not be specified any more.
  • Providers property of S57Manager removed.

Starting the Chart Management Applets

ManageENC.exe and ManageHCRF.exe can now be simply run from the command line. You will prompted for the SENC or RENC as appropriate. You can provide various command line parameters in order to avoid getting prompted.

Previously you had to use calls from the ENCX API to start the chart management applications. You had to first tell ENCX where the chart management applications were with a call to SetInstallationParams and then call something like InstallCharts_ENC. We felt this was a bit cumbersome so now you can just run the applications.

ManageENC.exe

If you run this application without a command line parameter it will prompt you for a path to a SENC. Alternatively you can supply the SENC path as a command line parameter.

ManageEnc d:\mySenc

The application has been improved and now starts with a list of the cells you have installed:

ManageENC.exe

You can install transmittals and permits from here. Permits are only necessary if you are installing commercial (encrypted) ENCs.From this list you can delete cells.

You can still run ManageENC via the ENCX library - by calling RunInstallApplication on your S57Manager object (supplying the path to ManageENC.exe). This will automatically pass the SENC folder path to ManageENC.exe.

If you have special requirements it is possible to drive ManageENC.exe programmatically.

ManageHCRF.exe

This application replaces ManageARCS.exe. It can install UKHO ARCS charts, Australian raster Charts and NZ raster Charts. All of which use basically the same system as the UKHO ARCS charts.

The main command line option is to specify the RENC folder where the charts are to be installed/updated:

managehcrf -r D:\charts\RENC

Note that unlike the ManageENC.exe application you need to use the -r switch here.

Switches that can be used:

  • -r to specify the RENC (place where charts are installed to).
  • -h to specify a help file.
  • -I to specify the ID in the help file.

(And there are some other obscure licensing options that we would tell you about if you needed to know).

"Snapshots" You Should Know About

A lot of properties on ENCX classes return "snapshots" of the situation at the time you look at the property. If you get a list of installed ENC cells from the S57Manager it will not stay up to date if more cells are added (that would actually be confusing if new cells were appearing while you were trying to enumerate the collection). You can sometimes modify collections returned by ENCX, if it is a snapshot then that change will not affect the state of the library. Here is a short list of properties and methods that return information that will not stay up to date.

The S57Draw.GeoPix property is not a snapshot. It will continue to provide sensible answers if the display scale is changed. But, most values returned are snapshots.

Some snapshots to be aware of...

  • S57Draw: PositionOfCentre and GeoExtent properties – return value is correct when you get the property but it will be not stay up to date.
  • S57FeatureLine.Points – you can modify the contents of this collection without having any affect on ENCX.
  • S57Face.Points – you can modify the contents of this collection without having any affect on ENCX.
  • S57Manager.UnusedCells, InstalledProducts, Cells. These are all snapshots relating to what ENC data is installed. They will not stay up to date. If you do need to stay up to date you should monitor the SENC OnBusy(..) events.

Why?

Whilst proofing this article it was suggested that not everybody would care about some of the technical explanations. These bits have been moved from other sections and can be ignored. They are included here for people who do care.

References and Class Behaviour

The ENCX library is written in C++ for speed (to draw charts fast). C++ is a very flexible language and it is normal to both copy objects (memberwise copy) and handle references to objects (pointers and references). .NET languages like C# and VB.NET are far more regular about when copying semantics are used (built in types like int) and when reference semantics are used (with instances of classes). With C++ there is more choice - a blessing and a curse.

To interoperate with other languages we wrap our C++ up as a COM library. C++, Delphi and VB 6 all target COM directly. C# and VB.NET (and other .NET languages) target ENCX through Microsoft's COM Interop which does an excellent job of making ENCX classes behave as it they were written in C#. But they are not. However, we do have a chance to make things easier to you the OEM by writing them so they behave more like .NET classes. This release addresses some of these situations.

The problem we had with S57Draw.Size

The problem for us was that S57Draw is a wrapper for a C++ class which knows nothing about ENCX.PixelSize objects. It does not use a PixelSize object to hold this information internally. So it was impossible for us to return a reference to the size of the object that you could modify and have that change modify the S57Draw.

We might have made something that could "pretend" in certain cases but we thought this was getting complicated and the wheels would probably fall off our abstraction in the end. Then we would have to explain the limitations of what we had done.

That's why we introduced the GetSize and SetSize methods discussed above.