Blog from May, 2008

See also presentation prepared by Unknown User (alex) for this meeting on the following page: Cross-Section.

People present on the meeting:

Fedor Baart
Unknown User (logch_o)
Unknown User (alex)
Marc van Dijk
Micha Werner
Govert.Verhoeven@deltares.nl

Notes from the last internal user meeting related to Cross Section editor:

Cross section data

A user should be able to edit cross section data in the following ways:

  • typing in Width vs Height W(H)
  • typing in Height vs offset Y(Z)
  • choosing a default shape and modifying its dimension (urban / rural)

Markers

For now 3 markers will be added used for bottom, left and right bank with a possibility to extend. In the Eurotask project "Integrated catchment modeling toolkit" people defined a standard format for cross section data (similar to the Mike11 approach). This format defines the following markers:

  • main channel
  • bank (small part in between channel and floodplain)
  • floodplain
  • extremity
  • limit of conveyance and storage (look at sobek RE)

current Sobek uses "flow width" and "total width" to distinguish between the water bearing part of the river and the part that only serves as storage area.

Map centered approach should be optional

Typical data in a project may consist of cross sections and chainage data. References to geographic coordinates may be missing altogether. (chainage is the cross section offset along a branch). The user should therefore not be required to always provide a map and work with that. The Isis model provides an approach to work with this type of data only. Mark has a working version of Isis.

Logical and physical coordinate of the cross-sections along the branch. If map is defined - logical will be used as a fist estimate.

Library of roughness classes

Using a library of common roughness types is appreciated. Bottomtype should not be used as name but instead "roughness class" or "friction class". A cross section should initially have two roughness classes along its length:

Manning's n

Name

0.001

stream

0.01

floodplain

When the user updates the friction/roughness for a class it should change in all profiles where it's in use. The scope of this change should be contained within the project.

Cross section metadata

Cross sections may change over time (they are periodically updated with new measurements). Therefore it is important to have some "versioning system" for cross sections. At least a cross section used in a calculation should have some meta data attached to it so you know who made it and when it was made.

Tasks

(plus) Micha will provide some tables with common friction parameters for river modeling.
(minus) Onno will ask Paul for an example of a typical dataset for river modeling (Adige river)
(minus) Gena and Fedor will organize "pizza cursussen" to enable colleagues to provide input for the new user interface

Scetch of the cross-sectional profile editor
Playing with WMS in SharpMap

WMS Layer works but it is a bit slow, looks like it tries to render everything too frequently. In general it should be possible to optimize it in the future.
If speed will be acceptable - it will be nice to create some list of the available WMS servers with fast connection and embed it into application as list of default maps.

... and some update:

Proportional editing

While editing a network or a grid you usually want to follow a smooth curve from the coastline or a river bed. In 3D modeling tools you also want to make smooth surfaces, for example a face or a landscape. The 3D modeling software blender uses what they call proportional editing to make editing of smooth curvatures more easy.

Proportional editing in blender

Interview Jan Jaap Brinkman about Cross-Sections

Attendees: Alex Koster
Date: 8 may 2008

To find out wat users expect from a new UI for Sobek a colleagues working at Deltares was interviewed last thursday. The experiences derived from interviews like these will be used to implement functionality that will satisfy most users (80/20 rule).

Jan Jaap is bursting with ideas. So, it would be very handy to catch up frequently and to share the ideas.

Here a brief summery:

Use of Cross-Section

  • To navigate through cross-sections should be easier.
  • The overview/maintenance of the cross-sections should be much better.
  • Possibility to make sets (collections) of cross-sections to switch on/of (possibly with the same locations).

Issues

  • Beside y-z info also geographic coordinates.
  • Adjustment of lowest point / connection point river polygon.
  • Detailed info of Cross-Section higher then grid level not used for calculations (ex. dike information): a waste of important information.
  • Problem if an (little) island is part of the cross-section.

Views

  • Length placements of cross-section on the map (a perpendicular across the river)
  • Londiguanal section of the system for waterlevels (output)
  • Multiple cross-sections in one view

Comparisons

  • There's a strong desire for a good comparison of cross-sections (and londiguanal sections) for different situations: two measures based on cross-sections, sediment changes in cross-section, waterlevel(s) etc.

Features

  • Program computes mud volume for dredgers (two different sets of cross-sections: this situation and optimal dredged situation)
  • Program computes an optimal cross-section (depth, not width?)

Map Layer Legend Styles and Thematics

Currently map layer style in the map control can only be set using property grid and not shown in the map legend tree view. The idea is to implement the following styles:

In case if we apply gradient theme and also need to define style for values bigger or smaller than specific value - we can add one more style on top and/or bottom of the layer legend > 0.04 or < 0.01

Coding standards

fxCop

We use FxCop to check wether DelftShell sources follow correct coding standards such as

  • correct CapITalizaTION
  • exception throwing
  • naming

The reports are currently generated for core functionality only.

An fxcop report can be generated manually by running the fxcop project on Teamcity. To inspect the results we can use FxCopGraph and FxCopReport.

Common mistakes

Often I find people use System.Exception instead of a more specific one like ArgumentException or ReadOnlyException etc.
Assemblies are often missing the ClsCompliant attribute and SecurityPermission Attribute.

Example

[assembly: CLSCompliant(true)] //fxcop prerequisite
[assembly: SecurityPermission(SecurityAction.RequestMinimum, Execution = true)] //fxcop prerequisite

When using a format statement or a ToString() function, the CultureInfo is usually missing. At least specify CultureInfo.InvariantCulture so the conversion always yields the same result when intended:

String comparison:

  • String comparison for equality – Specify StringComparison.Ordinal or OrdinalIgnoreCase. This is true when comparing against string resources, and particularly important when comparing file names.
  • String comparison for sorting - Specify StringComparison.CurrentCulture when sorting lists
  • String.ToUpper - Use ToUpper rather than ToLower, and specify InvariantCulture in order to pick up OS casing rules
  • Determining BiDi languages, for example to load correct icons - Continue to specify CurrentUICulture for these cases.
  • String formatting including numbers, and int.ToString - Specify CurrentCulture
  • String formatting of strings only (no numbers) - the Culture arg here is a no-op. Use CurrentCulture.

examples:

myInt.ToString(CultureInfo.InvariantCulture)
string.Format(CultureInfo.InvariantCulture, "Tomorrow I will be {0} years old", 37)

Implementation of IDisposable is often incomplete. For classes that are not sealed you have to implement the Dispose Pattern. A codeproject article provides some details on how to implement IDisposable.

The public void Dispose() method should be left final (in other words, don't make this a virtual method) and should always look like this

public void Dispose()
{
    Dispose(true);
    GC.SupressFinalize(this);
}

All of your resource cleanup should be contained in the Dispose(bool disposing) method. If necessary, you should protect the cleanup by testing the disposing parameter. This should happen for both managed and unmanaged resources. The Dispose(bool disposing) runs in two distinct scenarios:

  • If disposing equals true, the method has been called directly or indirectly by a user's code. Managed and unmanaged resources can be disposed.
  • If disposing equals false, the method has been called by the runtime from inside the finalizer and you should not reference other objects. Only unmanaged resources can be disposed.
protected virtual void Dispose(bool disposing)
{
    if (!disposed)
    {
        if (disposing)
        {
            // Dispose managed resources.
        }

        // There are no unmanaged resources to release, but
        // if we add them, they need to be released here.
    }
    disposed = true;

    // If it is available, make the call to the
    // base class's Dispose(Boolean) method
    base.Dispose(disposing);
}

Often people cast an object multiple times, leading to small performance degradation

if (myobject is IDataItem) //first cast
{
   node.Tag = ((IDataItem)myObject).Value; // second cast.
}

This can be done a bit more efficient in the following way:

IDataItem item
if ((item = myObject as IDataItem) != null) //first cast.
{
   node.Tag = item.Value;
}

I found the following links on internet about coding standards while fixing some issues:
document on coding standards
Exception throwing
Exception handling
FxCop, locale installatie
Using CultureInfo.InvariantCulture
Writing your own fxcop rules
When and how to dispose and finalize in c#
Why StringComparison.Ordinal is usually the right choice
New Recommendations for Using Strings in Microsoft .NET 2.0

DelftTools and DelftShell

A few days ago we had a very long discussion with Onno related to organization of classes in DelftTools Framework and DelftShell Gui, the result of discussion was that current organization of some classes / interfaces is a bit too coupled. Especially in Service interfaces which actually were introduced more as mix of Service and Controller in DelftTools.Gui and implemented in the DelftShell.Gui. The following was concluded:

  • Services were not Services but Contollers mixed with Service
  • IApplication has no Services and required to do all operations via IGui.AbcService - strange (warning)
  • Implementation of IApplication and IGui is mixed in one class - DelftShell and as result does not allow to use application intependently from gui, neither it allows to implement gui logic independently from application.
  • Gui and Application is mixed in DelftShell project.

Interfaces were organized in the following way:

  • DelftTools.Core project
    • IApplication
  • DelftTools.Gui project
    • IGui
    • Services/
      • IProjectGuiService
      • ITaskGuiService
      • IModelGuiService
      • IDataItemGuiService

... and before that Bert asked me a few quesitons related to organization of classes in the DelftShell which also puzzled me a little. So I had no other choice than to refactor it immediatelly (smile).

Also I had a short look on DDD book (Domain-Driven Design Quickly by Erik Evans) and some applications like MonoDevelop - they use Service principle, Resharper and a few others. Main conclusion from DDD book was:

...there are some actions in the domain, some verbs, which do not seem to belong to any object. They represent an important behavior of the domain, so they cannot be neglected or simply incorporated into some of the Entities or Value Objects. Adding such behavior to an object would spoil the object, making it stand for functionality which does not belong to it. Nonetheless, using an object-oriented language, we have to use an object for this purpose. We can't just have a separate function on its own. It has to be attached to some object. Often this kind of behavior functions across several objects, perhaps of different classes. For example, to transfer money from one account to another; should that function be in the sending account or the receiving account? It feels just as misplaced in either.
When such a behavior is recognized in the domain, the best practice is to declare it as a Service. Such an object does not have an internal state, and its purpose is to simply provide functionality for the domain. ...

  • Services are stateless - in our case means they won't remember objects given as parameters to their functions.
  • Services will help to work with our Entities, such as Project, Model, Task, etc.
  • It was logical to make these services independent from Gui - will provide better maintenability and faster response for changes.

As a result the following reorganisation of classes emerged:

As you can notice some of the service methods which have something to do with Gui, mainly those are parameter-less methods, were moved into GuiCommandHandler.

Interfaces shown on a class diagram contained in the following projects:

  • DelftTools.Core project
    • IApplication
    • Services/
      • IProjectService
      • ITaskService
      • IModelService
      • IDataItemService
  • DelftTools.Gui project
    • IGui
    • IGuiCommandHandler

After such a change it was logical to re-organize DelftShell project as well in one shot into DelftShell.Core and DelftShell.Gui:

And as result the same refactoring was applied to all AbcGuiService classes.

Interesting thing is that during this refactoring many non-logical pieces of code were found and refactored or removed so it is not only "moving code around" step but also overall code cleanup.

PS: there is still some crappy code left, mainly in GuiCommandHandler, but it will be much easier to clean it by moving non-gui related functionality into Service classes in DelftShell.Core.

SplashScreen

Another relatively small implementation I made is related to the SplashScreen, I noticed that it started to ... kind of ... hanging on 50% (warning). It was expected since first version implemented for Habitat, Verkenner used a bit get-it-work-fast approach. In short - approach has been changed and splash screen is now more inteligent, it has simple AI. It uses log4net to catch all log messages sent with a log level >= Info and also updates progress bar in a smart way. Algorithm implemented is based on the log messages and timing information from the previous application start-up which is stored in the: "C:\Documents and Settings\<user>\Local Settings\Application Data\Deltares\DelftShell\splash-screen-log-history.xml" file. Information is collected during the first application run. After that splash screen is able to update progress bar very precisely during all next runs of the application. Idea was suggested by Andre - thanks!

Design was also a little bit changed - feel free to criticize and suggest a better one (wink).

Update: 29-05-2008