WHISTLE APPLICATIONS

Part 08: Urban Modeling with OpenStreetMap

  • Frequently-Asked Questions
  • Example 1: A Simplified OSM Data File
  • Example 2: Visualizing OSM Data for BWI Airport
  • Example 3: Buildings and Postal Codes in Chicago
  • Example 4: NYC Subway System and Coastline

Frequently-Asked Questions

  • What is OpenStreetMap? And why is it part of Whistle?

    OpenStreetMap (OSM) is a collaborative project to create a free editable map of the world with open data.

    And since one of our goals for Whistle is to provide end-users with an easy-to-use framework for integrating data into modeling and decision making procedures for urban systems, OSM is a good fit.

  • How do geometries in OpenStreetMap work?

    Geometries in OSM are described with three different elements: nodes, ways and relations. Nodes are used to represent any kind of point type feature or just to designate a name for a point of interest in the vicinity. A way is an ordered list of nodes. They are used to represent linear features such as roads and rivers. A relation provide a means to logically organize things into groups that naturally belong together. Attributes are described as tags that can be part of a node, a way or a relation.

  • Are OpenStreetMap data files large?

    Yes and no. The OSM data files used to create the pictures below are of the order 20,000 -- 100,000 lines of XML. The corresponding data model will easily fit on a modern laptop. But the complete OSM data file for New York is in excess of 32 million lines, which is well beyond a laptop's capabilities. This necessitates use of filter programs such as OSMFilter to extract only those elements -- nodes, ways and relations -- of a model that are relevant to a specific viewpoint.

  • What are the challenges in working with data from OpenStreetMap?

    When OSM data files are large, DOM parsers, which create a syntax tree, will probably not work. Whistle avoids this problem by using a SAX parser to read and process OSM files. Second, ways tend to represent fragments of something end-users care about (e.g., roads, subway lines, coastlines), but OSM makes no attempt to stitch these fragments together.

  • Can I use OpenStreetMap to describe urban behavior?

    No. In OSM everything is static -- there is no notion of behavior.

  • Where can I find the OpenStreetMap code in Whistle?

    Look under: src/whistle/util/osm/ ....


Example 1: A Simplified OSM Data File

OpenStreetMap (OSM) uses five tags -- <osm>, <bounds>, <node>, <way> and <relation> -- and their attributes to describe urban/geographical systems. These tags are organized into a hierarchy.

For example, the simplified OSM data file:

<?xml version="1.0" encoding="UTF-8"?>
<osm>
   <bounds minlat="39.1605000" minlon="-76.6814000" maxlat="39.1898000" maxlon="-76.6503000"/>
   <node id="33051350" lat="39.1623308" lon="-76.6726108"/>
   <node id="33051337" lat="39.1590716" lon="-76.6893368"/>
   <node id="33051331" lat="39.1584560" lon="-76.6958300"/>
   <way id="04" >
       <node id="33051350" >
       <node id="33051337" >
       <node id="33051331" >
   </way>
</osm>

defines a bounding box for the map, then three nodes and one way which references the three nodes. Together nodes and ways describe geometry and topology (connectedness) of the urban domain. Relation tags (i.e., <relation>) provide a means to logically organize things -- they can contain nodes, ways and other relations.


Example 2: Visualizing OSM Data for BWI Airport

The fragment of Whistle code:

// ======================================================================
// Input view02: Use Viewer2D to display OSM of BWI Airport ...
//
// Written by: Mark Austin                                      July 2015
// ======================================================================

import whistle.gui.Viewer2D;
import whistle.model.geometry.GridModel;
import whistle.util.osm.model.OpenStreetMapModel;

program ("Exercise Viewer2D") {

   // Create GridModel ....

   grid = GridModel();
   grid.setRange( 0 m, 0 m, 3000 m, 3000 m);
   grid.setGridSize( 200 m );
   grid.buildGrid();

   // Create OpenStreeMapModel ....

   osm = OpenStreetMapModel();
   osm.shrinkFactor(  1.2 );
   osm.smallDotSize( 20.0 );
   osm.getData( "data/bwiairport.osm" );

   // Create grid layer workspace ...

   gridlayer      = grid.getWorkspace();

   // Extract composite hierarchy workspaces from OSM data model ...

   amenitylayer        = osm.getWorkspace( "amenity" );
   apronlayer          = osm.getWorkspace( "apron" );
   buildinglayer       = osm.getWorkspace( "building" );
   footwaylayer        = osm.getWorkspace( "footway" );
   highwaylayer        = osm.getWorkspace( "highway" );
   hpositionlayer      = osm.getWorkspace( "holdingposition" );
   streetlayer         = osm.getWorkspace( "residential" );
   railwaystationlayer = osm.getWorkspace( "railwaystation" );
   runwaylayer         = osm.getWorkspace( "runway" );
   serviceroadlayer    = osm.getWorkspace( "service" );
   taxiwaylayer        = osm.getWorkspace( "taxiway" );
   terminallayer       = osm.getWorkspace( "terminal" );

   // Set colors for application layers ...

   amenitylayer.setColor("black");
   apronlayer.setColor("purple");
   buildinglayer.setColor("black");
   footwaylayer.setColor("green");
   highwaylayer.setColor("blue" );
   hpositionlayer.setColor("blue" );
   railwaystationlayer.setColor("purple");
   streetlayer.setColor("green");
   runwaylayer.setColor("black");
   taxiwaylayer.setColor("red");
   serviceroadlayer.setColor("purple");
   terminallayer.setColor("black");

   // Create 2D viewer ...

   jfx = Viewer2D();

   // Add layers to the viewer ...

   jfx.addLayer(                "Grid", gridlayer );
   jfx.addLayer(     "Terminal Aprons", apronlayer );
   jfx.addLayer(           "Amenities", amenitylayer );
   jfx.addLayer(           "Buildings", buildinglayer );
   jfx.addLayer(            "Footways", footwaylayer );
   jfx.addLayer(            "Highways", highwaylayer );
   jfx.addLayer(   "Holding Positions", hpositionlayer );
   jfx.addLayer(       "Service Roads", serviceroadlayer );
   jfx.addLayer(     "Railway Station", railwaystationlayer );
   jfx.addLayer( "Residential Streets", streetlayer );
   jfx.addLayer(             "Runways", runwaylayer );
   jfx.addLayer(            "Taxiways", taxiwaylayer );
   jfx.addLayer(  "Terminal Buildings", terminallayer );

   // Setup and instantiate display ....

   jfx.sleep( 1000 );
   jfx.setSize( 1000, 1000 );
   jfx.setXRange( 0, 3000 );
   jfx.setYRange( 0, 3000 );
   jfx.display();
   jfx.sleep( 1000 );

   print "=====================================================";
   print "Done !!";
}

generates the following picture:

This OSM application/data file is one of the first we explored, and at the time we were surprised to see the level of domain-specific information it contained. To be more specific:

  • The heavy black regions are the runways.
  • The red contour lines are the taxiways for the planes.
  • The blue dots are the control points for planes.
  • The purple lines are pathways within the parking lots.
  • Terminal buildings are represented by black polygons.
  • The blue and green lines are fragments of highway and residential street.

Composite hierarchy workspaces are extracted from the OSM data model with commands of the type:

    amenitylayer = osm.getWorkspace( "amenity" );

and then added to the viewer with

   jfx = Viewer2D();
   jfx.addLayer( "Amenities", amenitylayer );

Thus, all that the Viewer2D sees is layers of composite hierarchy models. It knows nothing about OpenStreetMap.


Example 3: Postal Codes and Buildings in Chicago

The abbreviated script:

// ======================================================================
// Viewer2D display of buildings, postal codes and lakeshore in Chicago.
//
// Written by: Mark Austin                                   January 2019
// ======================================================================

import whistle.gui.Viewer2D;
import whistle.util.osm.model.OpenStreetMapModel;

program ("Exercise Viewer2D") { 

   // Create OSM data model for Lake Michigan coastline ...

   osmc = OpenStreetMapModel();
   osmc.shrinkFactor( 15.0 );
   osmc.smallDotSize( 10.0 );
   osmc.getData( "data/chicago-lake-michigan.osm" );

   // Create OSM data model for zip (postal) codes ...

   osmp = OpenStreetMapModel();
   osmp.shrinkFactor( 15.0 );
   osmp.smallDotSize( 10.0 );
   osmp.getData( "data/chicago-postal-code.osm" );

   // Create OSM data model for energy data entities ...

   osme = OpenStreetMapModel();
   osme.shrinkFactor( 15.0 );
   osme.smallDotSize( 10.0 );
   osme.getData( "data/chicago-energy-data.osm" );

   print osme.toString();

   // Details of OSM models ...

   .... details of nodes, ways, and relations for each model removed ...

   // Create 2D viewer ...

   jfx = Viewer2D();

   // Add grid layer workspace to Viewer2D model ...

   coastlinelayer  = osmc.getWorkspace( "coastline" );
   postalcodelayer = osmp.getWorkspace( "postalcode" );
   buildinglayer   = osme.getWorkspace( "building" );

   coastlinelayer.setColor("blue");
   postalcodelayer.setColor("red");
   buildinglayer.setColor("black");

   jfx.addLayer(      "Building", buildinglayer );
   jfx.addLayer( "Lake Michigan", coastlinelayer );
   jfx.addLayer(  "Postal Codes", postalcodelayer );

   // Customize graphics for the Viewer2D model ...

   ... details of graphics customization removed ...

   print "--- ============================================= .... ";
   print "--- Done !! ... ";
}

shows the essential details for modeling the spatial distribution of buildings across postal codes in the Chicago Metropolitan Area and then displaying the results with Viewer2D.

This application is interesting because data in multiple formats and from multiple sources is transformed and assembled into a single integrated view. Points to note:

  • The coastline (or lakeshore) layer comes from OSM.
  • The building layer comes from public data (CSV format) on building types and their gas/electricity usage.
  • The postal code boundaries are in a SHP (shape) format.

Small Java programs were written to convert data in CSV and SHP formats to OSM.


Example 4: NYC Subway System and Coastline

The abbreviated script:

// ======================================================================
// Input osm-view05: Viewer2D display of New York Subway System ...
//
// Written by: Mark Austin                                      July 2015
// ======================================================================

import whistle.gui.Viewer2D;
import whistle.util.osm.model.OpenStreetMapModel;

program ("Exercise Viewer2D") { 

   // Create OpenStreeMapModel ....

   osms = OpenStreetMapModel();
   osms.shrinkFactor( 20.0 );
   osms.smallDotSize( 10.0 );
   osms.getData( "data/nyc-subway02.osm" );

   osmc = OpenStreetMapModel();
   osmc.shrinkFactor( 20.0 );
   osmc.smallDotSize( 16.0 );
   osmc.getData( "data/nyc-coastline-simple02.osm" );

   // Details of OSM subway and coastline models ...

   print "*** ";
   print "*** OSM New York City Subway Model ... ";
   print "*** ================================= ... ";
   print "*** ";

   osms.print();
   osms.printWayNames();
   osms.printRelationNames();

   print "*** ";
   print "*** OSM New York Coastline Model ... ";
   print "*** ================================= ... ";
   print "*** ";

   osmc.print();
   osmc.printWayNames();
   osmc.printRelationNames();

   // Create 2D viewer ...

   jfx = Viewer2D();

   // Add grid layer workspace to Viewer2D model ...

   coastlinelayer      = osmc.getWorkspace( "coastline" );
   subwaylayer         = osms.getWorkspace( "subway" );
   railwaystationlayer = osms.getWorkspace( "railwaystation" );

   // coastlinelayer.setColor("green");

   subwaylayer.setColor("red");
   railwaystationlayer.setColor("blue");

   jfx.addLayer(        "Coastline", coastlinelayer );
   jfx.addLayer( "NYC Subway Lines", subwaylayer );
   jfx.addLayer(  "Subway Stations", railwaystationlayer );

   // Customize graphics for the Viewer2D model ...

   jfx.setSize( 1000, 1000 );
   jfx.setXRange( 0, 1000 );
   jfx.setYRange( 0, 1000 );
   jfx.display();
   jfx.sleep( 500 );

   print "--- ============================================= .... ";
   print "--- Done !! ... ";
}

generates a detailed figure of the NYC subway system and coastline.

The starting point for this example is the full OSM model for NYC, comprising (approx) 8.5 million nodes, 1.4 million ways, and 7,300 relations in 32 million lines of XML.

OSMFilter (a C program) traverses the XML file and systematically extracts only those elements of the file that match a set of keywords. For this example, the result is domain-specific files for the subway system and coastline layers:

  • Subway system model: 13,036 nodes, 1774 ways, and 31 relations.
  • Coastline model: 54,691 nodes and 526 ways.

Not tiny, but nowhere near the original file size.

Part 09: System Data Model (SDM) Components and Networks
  • Working with the System Data Model
  • Evaluation of Component Specifications
  • Example 1: Data Model for Rooms and Sensors
  • Example 2: Data Model for a Drone Component
  • Example 3: Data Model for a Simple Water Supply Network
  • Example 4: Simple Schematic for HVAC/Fan/Room Temperature Control
  • Example 5: Layout of Networks for HVAC/Air Distribution

Working with the System Data Model (SDM)

The system data model (SDM) is an exploratory study where we:

  • Build upon OpenStreetMap with sets of tags to describe components and networks, their attributes and parameters, specifications and statechart behaviors.

  • Explore the use of JAXB (as opposed to DOM or SAX in OpenStreetMap) for parsing and processing component and network data models.

We expect that:

  • System data model files will contain descriptions of components that can be imported into Whistle and used in the assembly of system architectures.

  • These files will be of a modest size (i.e., not millions of lines of XML code).

Thus, a complicated system architecture might be assembled from components of various types that have been imported from a variety of sources.


Getting Started: Nodes, Ways and Relations

The system data model borrows the node, way, and relation tagset from OpenStreetMap, and adds support for descriptions and attributes.

Consider, for example, the abbeviated script of nodes, ways and relations used to specify the geometry of a small building floorplan.

<?xml version="1.0" encoding="UTF-8"?>
<SystemDataModel author="Mark Austin" date="2018-01" source="UMD">

  <!-- Corner point nodes -->

  <node ID="001" x = " 0.0" y = "0.0" type="Point" />
  <node ID="002" x = " 8.5" y = "0.0" type="Point" />
  <node ID="003" x = " 9.5" y = "0.0" type="Point" />

  ... etc, etc, ....

  <!-- Way for a window portal ... -->

  <way ID="011" type="Portal">
      <description text="Window portal" />
      <attribute key = "type" value = "WindowOpening"/>
      <node ID="009" />
      <node ID="010" />
  </way>

  <!-- Relation for a composite boundary ... -->

  <relation ID="001" type="CompositeBoundary">
      <description text="Room Boundary" />
      <attribute key = "function" value = "Office"/>
      <way ID="001" />

      ... ways removed ...

      <way ID="014" />
      <shape type = "MultiPolygon">
          <attribute key = "opacity" value = "0.3"/>
          <attribute key =   "color" value = "LightBlue"/>
      </shape>
  </relation>

</SystemDataModel>

Points to note:

  • The three basic tags are nodes, ways and relations:

    • Nodes are used to represent any kind of point type feature or just to designate a name for a point of interest.

    • Ways are an ordered lists of nodes, and are used to represent linear features such as boundaries.

    • A relation provide a means to logically organize things into groups that naturally belong together.

  • Nodes, ways and relations are identified by their ID (a String). ID names must be unique within each group.

  • As a starting point, defined node types (i.e., see NodeType.java) are: Point, Boundary, and ComponentPoint.

    Defined way types are: Trajectory, Pathway, Boundary, Portal and Marker.

    And defined relation types are: Boundary, CompositeBoundary, Room, Space2D, Group and Marker.

    Users can extend these lists by simply editing NodeType.java, WayType.java and RelationType.java.

  • We expect that the contents of a system data model will be displayed and/or used in geometric computations. The <shape> tag provides a means to specify the type appropriate modeling formalism (e.g., MultiPolygon) and/or parameters to be used in visualization (e.g., opacity, color).

  • Attributes allow for the specification of supplementary information relevant to a specific application and can be part of any type of tag (e.g., node, way, relation, component, constraint, etc).

    They are stored as [ key, value ] pairs in hash maps.


Adding Simple and Compound Shapes

The system data model provides support for the representation of single entity shapes, e.g.,

    <shape ID = "ShapeID" type = "ShapeType" >
       <attribute key = "key1" value = "value1" > 
       <attribute key = "key2" value = "value2" > 

       <node ID="node01" x =   "0.0" y = "0.0" >
       <node ID="node02" x = "100.0" y = "0.0" >

       ... etc ...

    <shape>

The shape attributes specify parameters for sizing (e.g., width, height) and displaying the shape (e.g., opacity, color, depth level).

The currently defined shape types are: Circle, Square, Rectangle, LineString, Polygon, MultiPolygon, Cylinder

A compound shape is simply a list of simple shapes enclosed within a compoundshape tag, i.e.,

    <compoundshape ID = "CompoundShape01" >
        <shape>
           ... specification for shape one ...
        </shape>

        <shape>
           ... specification for shape two ...
        </shape>
    </compoundshape>


Adding Components

Components modeling abstractions that simplify system modeling by bundling groups of elements into cohesive units that have a well defined boundary, and inputs and outputs that flow through a port.

The following fragment of XML is a partial description for an electronic component:

<?xml version="1.0" encoding="UTF-8"?>
<SystemDataModel author="Mark Austin" date="2018-01" source="UMD">

  <!-- =================================== -->
  <!-- Part 01: Electronic component       -->
  <!-- =================================== -->

  <component ID="C002" type="Electronic">
      <description text="Sony TV" />
      <attribute key =  "width" value = "2.0" units = "m" />
      <attribute key = "height" value = "1.0" units = "m" />
      <attribute key = "weight" value =  "25" units = "lb"/>

      <port ID = "IP01" type = "Input" connection = "Power" >
          <description text="Power connection" />
          <attribute key =  "voltage" value =   "120" units = "Volts" />
          <attribute key = "occupied" value = "false" />
      </port>
      <port ID = "IP02" type = "Input" connection = "5WayBindingPost">
          <attribute key =    "audio" value = "120" units = "Volts" />
          <attribute key = "occupied" value = "false" />
      </port>
      <port ID = "IP03" type = "Input">
          <attribute key = "occupied" value = "false" />
          <connection plug = "HDMI"/>
      </port>
      <shape type = "Rectangle"
          <attribute key = "opacity" value = "1.0"/>
          <attribute key =   "color" value = "Green"/>
          <attribute key =   "width" value = "200" />
          <attribute key =  "height" value = "100" />
      </shape>

  </component>
</SystemDataModel>

The corresponding textual description is:

Component: ID = C002, type = Electronic
--------------------------------------------------------
Description = [ Sony TV ]
--------------------------------------------------------
No nodes          = [ 0.0 ]
No ways           = [ 0.0 ]
No attributes     = [ 3.0 ]
No specifications = [ 0.0 ]
No ports          = [ 4.0 ]
--------------------------------------------------------
Attribute  1:  [ key, value, units ] = [ "width", "2.0", "m" ]
Attribute  2:  [ key, value, units ] = [ "height", "1.0", "m" ]
Attribute  3:  [ key, value, units ] = [ "weight", "25", "lb" ]
--------------------------------------------------------
Port  1:  ID = IP01, type = Input
       : [ description ] = [ Power connection ]
       :  attribute ( key, value ) = [ "voltage", "120" ]
       :  attribute ( key, value ) = [ "occupied", "false" ]
--------------------------------------------------------
Port  2:  ID = IP02, type = Input
       :  attribute ( key, value ) = [ "audio", "120" ]
       :  attribute ( key, value ) = [ "occupied", "false" ]
--------------------------------------------------------
Port  3:  ID = IP03, type = Input
       :  attribute ( key, value ) = [ "occupied", "false" ]
       :  connection ( plug, value ) = [ "HDMI", "" ]
--------------------------------------------------------
Port  4:  ID = IP04, type = Input
       :  attribute ( key, value ) = [ "occupied", "false" ]
       :  connection ( plug, value ) = [ "Audio", "L" ]
       :  connection ( plug, value ) = [ "Audio", "R" ]
--------------------------------------------------------
JTS Geometry string = null! ...
--------------------------------------------------------
Shape: type = Rectangle
--------------------------------------------------------
No attributes = [ 4.0 ]
No nodes      = [ 0.0 ]
--------------------------------------------------------
Attribute 1:  [key,value] = [ "opacity", "1.0" ]
Attribute 2:  [key,value] = [ "color", "Green" ]
Attribute 3:  [key,value] = [ "width", "200" ]
Attribute 4:  [key,value] = [ "height", "100" ]
--------------------------------------------------------

Points to note:

  • Attributes (and soon parameters) can be defined at the component and port levels.

  • Whistle provides support for the two-dimensional representation of component geometries. This example is very simple -- geometry of the electronic component is represented by a rectangle.


Mathematical Constraints

The input file:

<?xml version="1.0" encoding="UTF-8"?>
<SystemDataModel author="Mark Austin" date="2018-01" source="UMD">

  <!-- =============================================== -->
  <!-- Part 01: System-level attributes and parameters -->
  <!-- =============================================== -->

  <attribute key = "units" value = "m"/>

  <parameter name = "a" value = "0"     type = "Integer"/>
  <parameter name = "b" value = "1"     type = "Integer"/>

  <parameter name = "c" value = "true"  type = "Boolean"/>
  <parameter name = "d" value = "false" type = "Boolean"/>

  <parameter name = "v" value = "Math.PI" type = "Real"/>
  <parameter name = "x" value = "1.0"     type = "Real" />
  <parameter name = "y" value = "2.0"     type = "Real" />
  <parameter name = "z" value = "3.0"     type = "Real"/>

  <!-- ======================================== -->
  <!-- Part 02: Sample constraints              -->
  <!-- ======================================== -->

  <constraint ID="C-001" type="Linear" satisfaction="Soft">
     <attribute key = "expression" value = "x + 2*y + 3*z" />
     <parameter name = "#x" />
     <parameter name = "#y" />
     <parameter name = "#z" />
  </constraint>

  <constraint ID="C-002" type="Nonlinear" satisfaction="Soft">
     <attribute key = "expression" value = "Math.sin(Math.PI/2) < 0.0" />
  </constraint>

  <constraint ID="C-003" type="Inequality" satisfaction="Hard">
     <attribute key = "expression" value = "Math.sin(v/2) > 0.0" />
     <parameter name = "#v" />
  </constraint>

  <constraint ID="C-004" type="Equality">
     <description text="Inequality expression ..." />
     <attribute key = "expression" value = "x + 2*y + 3*z == 12" />
     <parameter name = "x" value = "1.0" />
     <parameter name = "y" value = "2.0" />
     <parameter name = "z" value = "3.0" />
  </constraint>

  <constraint ID="C-005" type="Inequality">
     <description text="Inequality expression ..." />
     <attribute key = "expression" value = "x + 2*y + 3*z > 12" />
     <parameter name = "x" value = "1.0" />
     <parameter name = "y" value = "2.0" />
     <parameter name = "z" value = "3.0" />
  </constraint>

  <constraint ID="C-006" type="Inequality">
     <description text="Inequality expression ..." />
     <attribute key = "expression" value = "3*(a + 3*c) < 4*(10 - b)" />
     <parameter name = "a" value = "1.0" />
     <parameter name = "b" value = "2.0" />
     <parameter name = "c" value = "3.0" />
  </constraint>

  <constraint ID="C-007" type="Equality">
     <description text="Inequality expression ..." />
     <attribute key = "expression" value = "x + 2*y + 3*z != 15" />
     <parameter name = "x" value = "1.0" />
     <parameter name = "y" value = "2.0" />
     <parameter name = "z" value = "3.0" />
  </constraint>

  <constraint ID="C-008" type="Logical">
     <description text="Inequality expression ..." />
     <attribute key = "expression" value = "x + 2*y + 3*z == 15 || x < 3 || y <= 2" />
     <parameter name = "x" value = "1.0" />
     <parameter name = "y" value = "2.0" />
     <parameter name = "z" value = "3.0" />
  </constraint>

  <constraint ID="C-009" type="Logical">
     <description text="Logical expression ..." />
     <attribute key = "expression" value = "x + 2*y + 3*z == 14 && x < 0 || y <= 2" />
     <parameter name = "x" value = "1.0" />
     <parameter name = "y" value = "2.0" />
     <parameter name = "z" value = "3.0" />
  </constraint>

  <constraint ID="C-010" type="Logical">
     <description text="Logical expression ..." />
     <attribute key = "expression" value = "x + 2*y + 3*z == 14 && x < 0 && y <= 2" />
     <parameter name = "x" value = "1.0" />
     <parameter name = "y" value = "2.0" />
     <parameter name = "z" value = "3.0" />
  </constraint>
</SystemDataModel>

defines an assortment of mathematical constraints that are defined in terms of real and boolean paramter values.

The abbreviated output is as follows:

Constraint: ID = C-001, type = Linear, satisfaction = Soft
--------------------------------------------------------
Attribute  1:  [key,value] = [ "expression", "x + 2*y + 3*z" ]
--------------------------------------------------------
Parameter  1:  [ name, value, type ] = [ "#x", "1.0", "Real" ]
Parameter  2:  [ name, value, type ] = [ "#y", "2.0", "Real" ]
Parameter  3:  [ name, value, type ] = [ "#z", "3.0", "Real" ]
--------------------------------------------------------
Evaluation: x + 2*y + 3*z --> 14.0 ...
--------------------------------------------------------

Constraint: ID = C-002, type = Nonlinear, satisfaction = Soft
--------------------------------------------------------
Attribute  1:  [key,value] = [ "expression", "Math.sin(Math.PI/2) < 0.0" ]
--------------------------------------------------------
Evaluation: Math.sin(Math.PI/2) < 0.0 --> false ...
--------------------------------------------------------

Constraint: ID = C-003, type = Inequality, satisfaction = Hard
--------------------------------------------------------
Attribute  1:  [key,value] = [ "expression", "Math.sin(v/2) > 0.0" ]
--------------------------------------------------------
Parameter  1:  [ name, value, type ] = [ "#v", "Math.PI", "Real" ]
--------------------------------------------------------
Evaluation: Math.sin(v/2) > 0.0 --> true ...
--------------------------------------------------------

Constraint: ID = C-004, type = Equality
--------------------------------------------------------
Description = [ Inequality expression ... ]
--------------------------------------------------------
Attribute  1:  [key,value] = [ "expression", "x + 2*y + 3*z == 12" ]
--------------------------------------------------------
Parameter  1:  [ name, value, type ] = [ "x", "1.0", "null" ]
Parameter  2:  [ name, value, type ] = [ "y", "2.0", "null" ]
Parameter  3:  [ name, value, type ] = [ "z", "3.0", "null" ]
--------------------------------------------------------
Evaluation: x + 2*y + 3*z == 12 --> false ...
--------------------------------------------------------

Constraint: ID = C-005, type = Inequality
--------------------------------------------------------
Description = [ Inequality expression ... ]
--------------------------------------------------------
Attribute  1:  [key,value] = [ "expression", "x + 2*y + 3*z > 12" ]
--------------------------------------------------------
Parameter  1:  [ name, value, type ] = [ "x", "1.0", "null" ]
Parameter  2:  [ name, value, type ] = [ "y", "2.0", "null" ]
Parameter  3:  [ name, value, type ] = [ "z", "3.0", "null" ]
--------------------------------------------------------
Evaluation: x + 2*y + 3*z > 12 --> true ...
--------------------------------------------------------

Constraint: ID = C-006, type = Inequality
--------------------------------------------------------
Description = [ Inequality expression ... ]
--------------------------------------------------------
Attribute  1:  [key,value] = [ "expression", "3*(a + 3*c) < 4*(10 - b)" ]
--------------------------------------------------------
Parameter  1:  [ name, value, type ] = [ "a", "1.0", "null" ]
Parameter  2:  [ name, value, type ] = [ "b", "2.0", "null" ]
Parameter  3:  [ name, value, type ] = [ "c", "3.0", "null" ]
--------------------------------------------------------
Evaluation: 3*(a + 3*c) < 4*(10 - b) --> true ...
--------------------------------------------------------

Constraint: ID = C-007, type = Equality
--------------------------------------------------------
Description = [ Inequality expression ... ]
--------------------------------------------------------
Attribute  1:  [key,value] = [ "expression", "x + 2*y + 3*z != 15" ]
--------------------------------------------------------
Parameter  1:  [ name, value, type ] = [ "x", "1.0", "null" ]
Parameter  2:  [ name, value, type ] = [ "y", "2.0", "null" ]
Parameter  3:  [ name, value, type ] = [ "z", "3.0", "null" ]
--------------------------------------------------------
Evaluation: x + 2*y + 3*z != 15 --> true ...
--------------------------------------------------------

Constraint: ID = C-008, type = Logical
--------------------------------------------------------
Description = [ Inequality expression ... ]
--------------------------------------------------------
Attribute  1:  [key,value] = [ "expression", "x + 2*y + 3*z == 15 || x < 3 || y <= 2" ]
--------------------------------------------------------
Parameter  1:  [ name, value, type ] = [ "x", "1.0", "null" ]
Parameter  2:  [ name, value, type ] = [ "y", "2.0", "null" ]
Parameter  3:  [ name, value, type ] = [ "z", "3.0", "null" ]
--------------------------------------------------------
Evaluation: x + 2*y + 3*z == 15 || x < 3 || y <= 2 --> true ...
--------------------------------------------------------

Constraint: ID = C-009, type = Logical
--------------------------------------------------------
Description = [ Logical expression ... ]
--------------------------------------------------------
Attribute  1:  [key,value] = [ "expression", "x + 2*y + 3*z == 14 && x < 0 || y <= 2" ]
--------------------------------------------------------
Parameter  1:  [ name, value, type ] = [ "x", "1.0", "null" ]
Parameter  2:  [ name, value, type ] = [ "y", "2.0", "null" ]
Parameter  3:  [ name, value, type ] = [ "z", "3.0", "null" ]
--------------------------------------------------------
Evaluation: x + 2*y + 3*z == 14 && x < 0 || y <= 2 --> true ...
--------------------------------------------------------

Constraint: ID = C-010, type = Logical
--------------------------------------------------------
Description = [ Logical expression ... ]
--------------------------------------------------------
Attribute  1:  [key,value] = [ "expression", "x + 2*y + 3*z == 14 && x < 0 && y <= 2" ]
--------------------------------------------------------
Parameter  1:  [ name, value, type ] = [ "x", "1.0", "null" ]
Parameter  2:  [ name, value, type ] = [ "y", "2.0", "null" ]
Parameter  3:  [ name, value, type ] = [ "z", "3.0", "null" ]
--------------------------------------------------------
Evaluation: x + 2*y + 3*z == 14 && x < 0 && y <= 2 --> false ...
--------------------------------------------------------

--- ============================================ ...
--- Finished!! ...


Component Specifications

Component specifications are mathematical constraints associated with a component.

One can think of these specifications as being constraints that limit what a component can do and how well it can perform.


Adding Support for Composition of Data Files

TBD ...

    ... insert example ...

TBD ...


Definition and Positioning of Workspaces

TBD ...

    ... insert example ...

TBD ...


Composition of Workspaces

Workspaces can be combined into composite hierarchies.

    ... insert example ...

TBD ...


Example 1: Data Model for Rooms and Sensors

This is the case study example in Parastoo's paper ...

Insert content ...

<?xml version="1.0" encoding="UTF-8"?>
<SystemDataModel author="Mark Austin" date="2017-06" source="UMD">

  <-- ======================================= -->
  <-- Part 01: Building Data Model Attributes -->
  <-- ======================================= -->

  <attribute key =    "latitude" value = " 45.00"/>
  <attribute key =   "longitude" value = "-46.00"/>
  <attribute key = "orientation" value = "0.0"/>
  <attribute key =       "units" value =   "m"/>

  <-- =================================== -->
  <-- Part 02: Boundary Nodes and Sensors -->
  <-- =================================== -->

  <!-- Corner point nodes -->

  <node ID="001" x = " 0.0" y = "0.0" type="Point" /> 
  <node ID="002" x = " 8.5" y = "0.0" type="Point" /> 
  <node ID="003" x = " 9.5" y = "0.0" type="Point" /> 
  <node ID="004" x = "10.0" y = "0.0" type="Point" /> 
  <node ID="005" x = "13.5" y = "0.0" type="Point" /> 
  <node ID="006" x = "14.5" y = "0.0" type="Point" /> 
  <node ID="007" x = "15.0" y = "0.0" type="Point" /> 
  <node ID="008" x = " 0.0" y = "5.0" type="Point" /> 
  <node ID="009" x = " 1.5" y = "5.0" type="Point" /> 
  <node ID="010" x = " 3.0" y = "5.0" type="Point" /> 
  <node ID="011" x = " 7.0" y = "5.0" type="Point" /> 
  <node ID="012" x = " 8.5" y = "5.0" type="Point" /> 
  <node ID="013" x = "10.0" y = "5.0" type="Point" /> 
  <node ID="014" x = "12.5" y = "5.0" type="Point" />
  <node ID="015" x = "13.5" y = "5.0" type="Point" />
  <node ID="016" x = "15.0" y = "5.0" type="Point" />

  <!-- =============================== -->
  <!-- Part 03: Ways for Wall Segments -->
  <!-- =============================== -->

  <!-- Sequence of ways defining boundary of AVW 2206 ... -->

  <way ID="001" type="Boundary"> 
      <description text="Interior Office Wall Segment." />
      <attribute key =      "type" value = "InteriorWall"/>
      <attribute key =  "material" value = "drywall"/>
      <attribute key = "thickness" value = "20"/>
      <attribute key =     "units" value = "cm"/>
      <node ID="002" />
      <node ID="001" />
      <node ID="008" />
      <shape type = "LineString">
          <attribute key = "width" value = "6.0"/>
      </shape>
  </way>

  <way ID="002" type="Boundary"> 
      <description text="Exterior Wall Segment." />
      <attribute key =      "type" value = "ExteriorWall"/>
      <attribute key =  "material" value = "Masonry"/>
      <attribute key = "thickness" value = "30"/>
      <attribute key =     "units" value = "cm"/>
      <node ID="008" />
      <node ID="009" />
      <shape type = "LineString">
          <attribute key = "width" value = "9.0"/>
      </shape>
  </way>

  <way ID="003" type="Boundary"> 
      <description text="Exterior Wall Segment." />
      <attribute key =      "type" value = "ExteriorWall"/>
      <attribute key =  "material" value = "Masonry"/>
      <attribute key = "thickness" value = "30"/>
      <attribute key =     "units" value = "cm"/>
      <node ID="010" />
      <node ID="011" />
      <shape type = "LineString">
          <attribute key = "width" value = "9.0"/>
      </shape>
  </way>

  <way ID="004" type="Boundary">
      <description text="Exterior Wall Segment." />
      <attribute key =      "type" value = "ExteriorWall"/>
      <attribute key =  "material" value = "Masonry"/>
      <attribute key = "thickness" value = "30"/>
      <attribute key =     "units" value = "cm"/>
      <node ID="012" />
      <node ID="013" />
      <shape type = "LineString">
          <attribute key = "width" value = "9.0"/>
      </shape>
  </way>

  <way ID="006" type="Boundary">
      <description text="Interior Office Wall Segment." />
      <attribute key =      "type" value = "InteriorWall"/>
      <attribute key =  "material" value = "drywall"/>
      <attribute key = "thickness" value = "20"/>
      <attribute key =     "units" value = "cm"/>
      <node ID="013" />
      <node ID="004" />
      <node ID="003" />
      <shape type = "LineString">
          <attribute key = "width" value = "6.0"/>
      </shape>
  </way>

  <!-- Sequence of ways defining boundary of AVW 2210 ... -->

  <way ID="007" type="Boundary">
      <description text="Interior Office Wall Segment." />
      <attribute key =      "type" value = "InteriorWall"/>
      <attribute key =  "material" value = "drywall"/>
      <attribute key = "thickness" value = "20"/>
      <attribute key =     "units" value = "cm"/>
      <node ID="005" />
      <node ID="004" />
      <node ID="013" />
      <shape type = "LineString">
          attribute key = "width" value = "6.0"/>
      </shape>
  </way>

  <way ID="008" type="Boundary"> 
      <description text="Exterior Wall Segment." />
      <attribute key =      "type" value = "ExteriorWall"/>
      <attribute key =  "material" value = "Masonry"/>
      <attribute key = "thickness" value = "30"/>
      <attribute key =     "units" value = "cm"/>
      <node ID="013" />
      <node ID="014" />
      <shape type = "LineString">
          <attribute key = "width" value = "9.0"/
      </shape>
  </way>

  <way ID="009" type="Boundary"> 
      <description text="Exterior Wall Segment." />
      <attribute key =      "type" value = "ExteriorWall"/>
      <attribute key =  "material" value = "Masonry"/>
      <attribute key = "thickness" value = "30"/>
      <attribute key =     "units" value = "cm"/>
      <node ID="015" />
      <node ID="016" />
      <shape type = "LineString">
          <attribute key = "width" value = "9.0"/>
      </shape>
  </way>

  <way ID="010" type="Boundary">
      <description text="Interior Office Wall Segment." />
      <attribute key =      "type" value = "InteriorWall"/>
      <attribute key =  "material" value = "drywall"/>
      <attribute key = "thickness" value = "20"/>
      <attribute key =     "units" value = "cm"/>
      <node ID="016" />
      <node ID="007" />
      <node ID="006" />
      <shape type = "LineString">
          <attribute key = "width" value = "6.0"/>
      </shape>
  </way>

  <!-- Window portals for AVW 2206 and AVW 2210 ... -->

  <way ID="011" type="Portal">
      <description text="Window portal for AVW 2206." />
      <attribute key = "type" value = "WindowOpening"/>
      <node ID="009" />
      <node ID="010" />
  </way>

  <way ID="012" type="Portal">
      <description text="Window portal for AVW 2206." />
      <attribute key = "type" value = "WindowOpening"/>
      <node ID="011" />
      <node ID="012" />
  </way>

  <way ID="013" type="Portal">
      <description text="Window portal for AVW 2210." />
      <attribute key = "type" value = "WindowOpening"/>
      <node ID="014" />
      <node ID="015" />
  </way>

  <!-- Doorway portals for AVW 2206 and AVW 2210 ... -->

  <way ID="014" type="Portal">
      <description text="Doorway for AVW 2206." />
      <attribute key = "type" value = "Doorway"/>
      <node ID="002" />
      <node ID="003" />
  </way>

  <way ID="015" type="Portal">
      <description text="Doorway for AVW 2210." />
      <attribute key = "type" value = "Doorway"/>
      <node ID="005" />
      <node ID="006" />
  </way>

  <!-- ============================= -->
  <!-- Part 04: AVW office relations -->
  <!-- ============================= -->

  <!-- Relations for room boundaries ... -->

  <relation ID="001" type="CompositeBoundary">
      <description text="AVW Rm 2206 (Room Boundary)" />
      <attribute key = "function" value = "Office"/>
      <way ID="001" />
      <way ID="002" />
      <way ID="011" />
      <way ID="003" />
      <way ID="012" />
      <way ID="004" />
      <way ID="006" />
      <way ID="014" />
      <shape type = "MultiPolygon">
          <attribute key = "opacity" value = "0.3"/>
          <attribute key =   "color" value = "LightBlue"/>
      </shape>
  </relation>

  <relation ID="002" type="CompositeBoundary"> 
      <description text="AVW Rm 2210 (Room Boundary)" />
      <attribute key = "function" value = "Office"/>
      <way ID="007" />
      <way ID="008" /> 
      <way ID="013" /> 
      <way ID="009" /> 
      <way ID="010" /> 
      <way ID="015" /> 
      <shape type = "MultiPolygon">
          <attribute key = "opacity" value = "0.3"/>
          <attribute key =   "color" value = "LightBlue"/>
      </shape>
  </relation>

  <!-- Relations for individual rooms ... -->

  <relation ID="003" type="Room">
      <description text="AVW Rm 2206 (Faculty Office)" />
      <attribute key = "function" value = "Office"/>
      <attribute key =     "area" value =   "50.0"/>
      <attribute key =    "units" value =    "m^2"/>
      <relation ID="001" />
  </relation>

  <relation ID="004" type="Room">
      <description text="AVW Rm 2210 (Faculty Office)" />
      <attribute key = "function" value = "Office"/>
      <attribute key =     "area" value =   "25.0"/>
      <attribute key =    "units" value =    "m^2"/>
      <relation ID="002" />
  </relation>

  <!-- ============================================ -->
  <!-- Part 05: Architectural and Sensor Components -->
  <!-- ============================================ -->

  <!-- Sensor Components -->

  <component ID="001" x = " 2.5" y = "2.5" type="Sensor">
      <description text="Smoke Detector." />
      <attribute key = "measurement" value = "Smoke"/>
      <attribute key =        "name" value = "Smell-Smoke-Call-911"/>
      <attribute key =      "status" value = "Broken"/>
      <shape type = "Circle">
          <attribute key = "radius" value = "3.0"/>
          <attribute key =  "color" value = "Red"/>
      </shape>
  </component>

  <component ID="002" x = " 7.5" y = "2.5" type="Sensor">
      <description text="Room Thermometer." />
      <attribute key = "measurement" value = "Temperature"/>
      <attribute key =        "name" value = "Excellanto Temp-001"/>
      <attribute key =      "status" value = "Operating"/>
      <shape type = "Square">
          <attribute key =    "side" value = "5.0"/>
          <attribute key = "opacity" value = "1.0"/>
          <attribute key =   "color" value = "Green"/>
      </shape>
  </component>

  <component ID="003" x = " 12.5" y = "2.5" type="Sensor">
      <description text="Room Thermometer." />
      <attribute key = "measurement" value = "Temperature"/>
      <attribute key =        "name" value = "Excellanto Temp-001"/>
      <attribute key =      "status" value = "Operating"/>
      <shape type = "Rectangle">
          <attribute key =   "width" value = "5.0"/>
          <attribute key =  "height" value = "5.0"/>
          <attribute key = "opacity" value = "1.0"/>
          <attribute key =   "color" value = "Green"/>
      </shape>
  </component>

  <!-- Building Architecture Components -->

  <component ID="004" type="Window">
      <description text="Double-Paned Office Window" />
      <attribute key =  "width" value = "1.0"/>
      <attribute key = "height" value = "2.0"/>
      <attribute key =  "units" value = "m"/>
      <attribute key = "status" value = "Open" />
      <shape type = "Rectangle">
          <attribute key = "opacity" value = "1.0"/>
          <attribute key =   "color" value = "Green"/>
      </shape>
  </component>

</SystemDataModel>

Points to note:

  • The geometry of each room is defined by an ordered list of way segments. Way segments are provided for the physical wall as well as the portal openings -- windows and doors can be views as subclasses of a portal opening.
  • Relationship tags are used to represent the perimeter of each room -- this is a sequence of way segments -- as well as collections of attributes for the individual rooms.
  • Since the backend software will be required to support spatial reasoning (e.g., to determine whether or not a room is occupied), it is important that the specification for the room perimeter be closed.


Example 2: Data Model for a Drone Component

We begin with the specification of attributes and geometry for a drone (helicopter) component, i.e.,

A statechart behavior specification and details for traversal of flight pathway will be added later.


Data Model for Drone Attributes, Specifications and Geometry

Insert content ...

<?xml version="1.0" encoding="UTF-8"?>
<SystemDataModel author="Mark Austin" date="2018-01" source="UMD">

  <!-- ================================================================ -->
  <!-- Drone: X47-B (Source: Wikipedia)                                 -->
  <!-- ================================================================ -->
  <!--                                                                  -->
  <!--      ... details removed ...                                     -->
  <!--                                                                  -->
  <!-- ================================================================ -->

  <!-- ======================================== -->
  <!-- Part 01: Drone data model attributes     -->
  <!-- ======================================== -->

  <attribute key = "units" value = "m"/>

  <!-- ======================================== -->
  <!-- Part 02: Drone behavior model            -->
  <!-- ======================================== -->

  <behavior ID="B001" type = "FSM" >

     ... see details in example below ...

  </behavior>

  <!-- =================================== -->
  <!-- Part 03: Drone component model      -->
  <!-- =================================== -->

  <component ID="Drone-X47-B" type="UAV" x = "100.0" y = "200.0" >
      <description text="NAVAIR X47-B Drone" />

      <!-- =============================================== -->
      <!-- Component attributes                            -->
      <!-- =============================================== -->

      <attribute key = "height" value =  "5.5" units = "m" />
      <attribute key = "width"  value = "10.0" units = "m" />

      <!-- =============================================== -->
      <!-- Drone attributes                                -->
      <!-- =============================================== -->

      <attribute key = "length"       value =    "5.5" units = "m" />
      <attribute key = "wingspan"     value =   "10.0" units = "m" />
      <attribute key = "maxRange"     value =  "4,689" units = "km" />
      <attribute key = "maxSpeed"     value =  "1,073" units = "km/hr" />
      <attribute key = "maxEndurance" value =    "6.0" units = "hr" />
      <attribute key = "maxAltitude"  value = "43,000" units = "ft" />

      <!-- =============================================== -->
      <!-- Component state parameters                      -->
      <!-- =============================================== -->

      <parameter name =     "speed" value =   "200.0" units = "m/sec" type = "Real"/>
      <parameter name =    "weight" value =     "0.0" units = "kg"    type = "Real"/>
      <parameter name =  "altitude" value =  "2500.0" units = "m"     type = "Real"/>
      <parameter name =     "range" value = "4,500.0" units = "km" type = "Real"/>
      <parameter name = "endurance" value =     "0.0" units = "hr" type = "Real"/>
      <parameter name =  "latitude" value =     "0.0" />
      <parameter name = "longitude" value =     "0.0" />

      <!-- =============================================== -->
      <!-- Reference to statechart model of drone behavior -->
      <!-- =============================================== -->

      <behavior ID="#B001" />

      <!-- =============================================== -->
      <!-- Drone specifications and measures of effectiveness -->
      <!-- =============================================== -->

      <specification ID = "Drone-X47-B.S01" type = "Performance" >
          <description text="Maximum takeoff weight" />
          <attribute key  = "expression" value = "weight < maxWeight" />
          <parameter name =  "maxWeight" value = "20,215" units = "kg" />
          <parameter name = "#weight" />
      </specification>

      <specification ID = "Drone-X47-B.S02" type = "Performance" >
          <description text="Maximum re-fueled range" />
          <attribute key  = "expression" value = "range < maxRange" />
          <parameter name = "#maxRange" />
          <parameter name = "#range" />
      </specification>

      <specification ID = "Drone-X47-B.S03" type = "Performance" >
          <description text="Maximum speed" />
          <attribute key  = "expression" value = "speed < maxSpeed" />
          <parameter name = "#maxSpeed" />
          <parameter name = "#speed" />
      </specification>

      <specification ID = "Drone-X47-B.S04" type = "Performance" >
          <description text="Maximum endurance" />
          <attribute key  = "expression" value = "endurance < maxEndurance" />
          <parameter name = "#maxEndurance" />
          <parameter name = "#endurance" />
      </specification>

      <specification ID = "Drone-X47-B.S05" type = "Performance" >
          <description text="Maximum altitude" />
          <attribute key  = "expression" value = "altitude < maxAltitude" />
          <parameter name = "#maxAltitude" />
          <parameter name = "#altitude" />
      </specification>

      <!-- ================================== -->
      <!-- Simplified model of drone geometry -->
      <!-- ================================== -->

      <compoundshape ID = "Drone-X47-B.Shape01">
         <shape ID = "Drone-X47-B.Body" type = "Polygon">
             <attribute key = "opacity" value = "1.0"/>
             <attribute key =   "color" value = "green"/>
             <attribute key =    "fill" value = "true"/>
             <node ID="dr01" x =  "-54.0" y = "   0.0" type="Point" />
             <node ID="dr02" x =  "-48.0" y = "   3.0" type="Point" /> 
             <node ID="dr03" x =  "-48.0" y = "  12.0" type="Point" />
             <node ID="dr04" x =  "-45.0" y = "  12.0" type="Point" />
             <node ID="dr05" x =  "-42.0" y = "   2.4" type="Point" />
             <node ID="dr06" x =  "-15.0" y = "   6.0" type="Point" />
             <node ID="dr07" x =   "-9.0" y = "  11.4" type="Point" />
             <node ID="dr08" x =   "12.0" y = "  11.4" type="Point" />
             <node ID="dr09" x =   "16.5" y = "   4.5" type="Point" />
             <node ID="dr10" x =   "16.5" y = "  -4.5" type="Point" />
             <node ID="dr11" x =   "12.0" y = " -11.4" type="Point" />
             <node ID="dr12" x =   "-9.0" y = " -11.4" type="Point" />
             <node ID="dr13" x =  "-15.0" y = "  -6.0" type="Point" />
             <node ID="dr14" x =  "-42.0" y = "  -2.4" type="Point" />
             <node ID="dr15" x =  "-45.0" y = " -12.0" type="Point" />
             <node ID="dr16" x =  "-48.0" y = " -12.0" type="Point" />
             <node ID="dr17" x =  "-48.0" y =  " -3.0" type="Point" />
         </shape>
         <shape ID = "RotorSpace" x = "0.0" y = "0.0" type = "Circle">
             <attribute key = "opacity" value = "0.2"/>
             <attribute key =   "color" value = "grey"/>
             <attribute key =    "fill" value = "true"/>
             <attribute key =  "radius" value = "30.0"/>
         </shape>
         <shape ID = "RotorSpace-Blade01" type = "Polygon">
             <attribute key =  "opacity" value =   "1.0"/>
             <attribute key =    "color" value = "black"/>
             <attribute key =     "fill" value =  "true"/>
             <attribute key = "rotation" value =   "0.0"/>
             <attribute key =    "speed" value =    "20"/>
             <node ID="dr01" x =   "2.0" y = "-1.0" type="Point" />
             <node ID="dr02" x =   "2.0" y =  "1.0" type="Point" />
             <node ID="dr03" x =  "29.5" y =  "2.0" type="Point" />
             <node ID="dr04" x =  "30.0" y =  "0.0" type="Point" /> 
             <node ID="dr05" x =  "29.5" y = "-2.0" type="Point" />
         </shape>
         <shape ID = "RotorSpace-Blade02" type = "Polygon">
             <attribute key =  "opacity" value =   "1.0"/>
             <attribute key =    "color" value = "black"/>
             <attribute key =     "fill" value =  "true"/>
             <attribute key = "rotation" value =   "120"/>
             <attribute key =    "speed" value =    "20"/>
             <node ID="dr01" x =   "2.0" y = "-1.0" type="Point" />
             <node ID="dr02" x =   "2.0" y =  "1.0" type="Point" />
             <node ID="dr03" x =  "29.5" y =  "2.0" type="Point" />
             <node ID="dr04" x =  "30.0" y =  "0.0" type="Point" />
             <node ID="dr05" x =  "29.5" y = "-2.0" type="Point" />
         </shape>
         <shape ID = "RotorSpace-Blade03" type = "Polygon">
             <attribute key =  "opacity" value =   "1.0"/>
             <attribute key =    "color" value = "black"/>
             <attribute key =     "fill" value =  "true"/>
             <attribute key = "rotation" value =   "240"/>
             <attribute key =    "speed" value =    "20"/>
             <node ID="dr01" x =   "2.0" y = "-1.0" type="Point" />
             <node ID="dr02" x =   "2.0" y =  "1.0" type="Point" />
             <node ID="dr03" x =  "29.5" y =  "2.0" type="Point" />
             <node ID="dr04" x =  "30.0" y =  "0.0" type="Point" />
             <node ID="dr05" x =  "29.5" y = "-2.0" type="Point" />
         </shape>
         <shape ID = "RotorSpace-Center" x = "0.0" y = "0.0" type = "Circle">
             <attribute key = "opacity" value = "1.0"/>
             <attribute key =   "color" value = "maroon"/>
             <attribute key =    "fill" value = "true"/>
             <attribute key =  "radius" value = "2.0"/>
         </shape>
      </compoundshape>

  </component>
</SystemDataModel>

Points to note:

  • By default, individual shape elements are assigned a depth = 50. If all of the shape elements have equal depth, then their IDs are ordered alphabetically. Otherwise, shapes depths can be manually controlled by adding a depth attribute (need to finish details of implementation).


Instantiating a Drone Component

The following script of code shows how Whistle reads the XML specification for the drone component, instantiates a generic component model, adds the component to a workspace, and then extracts a composite hierarchy layer that can be visualized in Whistle.

/*
 * ==========================================================================
 * Use system data model to define a drone component and statechart behavior.
 * 
 * Written by: Mark Austin                                          June 2019
 * ==========================================================================
 */

import whistle.util.system.model.SystemDataModel;
import whistle.util.system.model.SystemDataModelComponentVisitor;

import whistle.model.component.ComponentModel;

program ("Simplified Drone System") {

   print "---";
   print "--- ======================================================= ";
   print "--- PART 01. Import and print drone (sdm01) data ...        ";
   print "--- ======================================================= ";
   print "---";

   sdm01 = SystemDataModel();
   sdm01.getData ( "data/umd-system-data-model-drone03.xml" );

   print "---";
   print "--- ======================================================= ";
   print "--- PART 02. Extract drone from sdm01 data model ...        ";
   print "--- ======================================================= ";
   print "---";

   drone01 = sdm01.getComponent( "Drone-X47-B" );
   print drone01.toString();

   print "---";
   print "--- ========================================================== ";
   print "--- PART 03. Extract behavior from component data model ...    ";
   print "--- ========================================================== ";
   print "---";

   behavior01 = drone01.getBehavior();
   print behavior01.toString();

   print "---";
   print "--- ========================================================= ";
   print "--- PART 04. Create generic component model ...               ";
   print "--- ========================================================= ";
   print "---";

   // Create drone from Component Model ....

   drone02 = ComponentModel(2, 2);
   drone02.setName("Drone");

   print "---";
   print "--- ========================================================= ";
   print "--- PART 05. SystemDataModel Component Visitor ...            ";
   print "--- ========================================================= ";
   print "---";

   // Create component model visitor ...

   cmvisitor01 = SystemDataModelComponentVisitor();
   cmvisitor01.add( drone02 );

   // Populate component model with system data model info ....

   sdm01.accept ( cmvisitor01 );

   print "---";
   print "--- ========================================================= ";
   print "--- PART 06. Print Populated ComponentModel ...               ";
   print "--- ========================================================= ";
   print "---";

   print drone02.toString();

   ... etc, etc, etc ...
}

The abbreviated output from Part 6 is as follows:

-----------------------------------
ComponentModel(Drone-X47-B)
-----------------------------------
Position (x,y) = (100.000000, 200.000000) ...
--- height = 5.500000 ...
--- width  = 10.000000 ...
--- no attributes = 8 ...
--- no parameters = 7 ...
-------------------------------------------------
Attribute  1:  [ name, value, units ] = [ "height", "5.5", "m" ]
Attribute  2:  [ name, value, units ] = [ "length", "5.5", "m" ]
Attribute  3:  [ name, value, units ] = [ "maxAltitude", "43,000", "ft" ]
Attribute  4:  [ name, value, units ] = [ "maxEndurance", "6.0", "hr" ]
Attribute  5:  [ name, value, units ] = [ "maxRange", "4,689", "km" ]
Attribute  6:  [ name, value, units ] = [ "maxSpeed", "1,073", "km/hr" ]
Attribute  7:  [ name, value, units ] = [ "width", "10.0", "m" ]
Attribute  8:  [ name, value, units ] = [ "wingspan", "10.0", "m" ]
-------------------------------------------------
Parameter  1:  [ name, value, units, type ] = [ "altitude", "2500.0", "m", "Real" ]
Parameter  2:  [ name, value, units, type ] = [ "endurance", "0.0", "hr", "Real" ]
Parameter  3:  [ name, value ] = [ "latitude", "0.0", "null" ]
Parameter  4:  [ name, value ] = [ "longitude", "0.0", "null" ]
Parameter  5:  [ name, value, units, type ] = [ "range", "4,500.0", "km", "Real" ]
Parameter  6:  [ name, value, units, type ] = [ "speed", "200.0", "m/sec", "Real" ]
Parameter  7:  [ name, value, units, type ] = [ "weight", "0.0", "kg", "Real" ]
-------------------------------------------------

Ports()
-------------------------------------------------
Input Ports:
--- [0] :  = null
--- [1] :  = null
Output Ports:
--- [0] :  = null
--- [1] :  = null
-----------------------------------


Evaluation of Component Specifications

With the component and specification attributes and parameters in place, the fragment of output:

Specification  1: ID = Drone-X47-B.S01, type = Performance ...
--------------------------------------------------------
Description = [ Maximum takeoff weight ]
--------------------------------------------------------
Attribute  1:  [key, value] = [ "expression", "weight < maxWeight" ]
--------------------------------------------------------
Parameter  1:  [ name, value, units, type ] = [ "maxWeight", "20,215", "kg", "null" ]
Parameter  2:  [ name, value, units, type ] = [ "#weight", "0.0", "kg", "Real" ]
--------------------------------------------------------
Evaluation: weight < maxWeight --> true ...
--------------------------------------------------------

Specification  2: ID = Drone-X47-B.S02, type = Performance ...
--------------------------------------------------------
Description = [ Maximum re-fueled range ]
--------------------------------------------------------
Attribute  1:  [key, value] = [ "expression", "range < maxRange" ]
--------------------------------------------------------
Parameter  1:  [ name, value, units, type ] = [ "#maxRange", "4,689", "km", "null" ]
Parameter  2:  [ name, value, units, type ] = [ "#range", "4,500.0", "km", "Real" ]
--------------------------------------------------------
Evaluation: range < maxRange --> true ...

shows typical output that occurs during evaluation of component specifications.


Displaying the Drone Component

The essential details of displaying the drone component are as follows:

   // Step 1: Add drone to a workspace model ...

   wsdrone01 = WorkspaceModel ( 0.0, 0.0, 0.0 );
   wsdrone01.add ( drone02 );

   // Step 2: Extract composite hierarchy from workspace model ...

   chdrone01 = wsdrone01.getWorkspace();

   // Step 3: Build 2D viewer ...

   jfx = Viewer2D();
   jfx.addLayer(  "Drone Mission", chdrone01 );

   // Step 4: Setup and instantiate display ....

   jfx.sleep( 1000 );
   jfx.setSize( 1000, 1000 );
   jfx.setXRange( 0, 800 );
   jfx.setYRange( 0, 800 );
   jfx.display();

Insert content ...


Example 3: Data Model for a Simple Water Supply Network

The adjacent figure shows a very simple water network and pump connecting a reservoir to a storage tank.

The system setup is completely prescribed in XML (details below), and comprises four components -- a reservoir (shown in blue), an elevated water tank (shown in blue, black, dark red, grey and green), a pump (shown in red) and one pipe junction (shown in yellow) -- and three segments of piping (shown in black).

The short segment of Whistle code:

// ========================================================================
// Print model for a small urban water network ...
//
// Written by: Mark Austin                                     October 2019
// ========================================================================

import whistle.gui.Viewer2D;

import whistle.util.system.model.Component;
import whistle.util.system.model.SystemDataModel;

program ("Small Water Network Model") { 

   print "--- ";
   print "--- ===================================================";
   print "--- PART 01: Import water network data model ...       ";
   print "--- ===================================================";
   print "--- ";

   // Import data model for small water network ...

   sdm01 = SystemDataModel();
   sdm01.getData ("data/umd-water-network03.xml");

   print "---";
   print "--- ======================================================";
   print "--- PART 02: Extract models from system data model ...    ";
   print "--- ======================================================";

   // Get composite hierarchy for pump ...

   chpump01 = sdm01.getWorkspace("Pump");
   chpump01.setColor("red");

   // Get composite hierarchy for tank ...

   chpump02 = sdm01.getWorkspace("Tank");

   // Get composite hierarchy for reservoir ...

   chpump03 = sdm01.getWorkspace("Reservoir");
   chpump03.setColor("blue");

   // Get composite hierarchy for pipe junctions ...

   chpump04 = sdm01.getWorkspace("Junction");
   chpump04.setColor("greenyellow");

   // Get composite hierarchy for water pipeline ...

   chpump05 = sdm01.getWorkspace("Pipeline");
   chpump05.setColor("black");

   print "---";
   print "--- ======================================================";
   print "--- PART 03: Build 2D Viewer, then display !! ...         ";
   print "--- ======================================================";
   print "---";

   // Build 2D viewer ...

   ... details removed ...

   // Setup and instantiate display ....

   ... details removed ...
}

loads water-network.xml into a system data model, and then systematically extracts composite hierarchies for the individual layers. The details for building and displaying the Viewer2D can be found elsewhere in this tutorial.

The abbreviated details of water-network03.xml are as follows:

<?xml version="1.0" encoding = "UTF-8"?>
<SystemDataModel author = "Zebo Peng" date = "2019-09"  source = "UMD">

  <!-- ======================================== -->
  <!-- Water network coordinates ...            -->
  <!-- ======================================== -->

  <node ID = "001" x = "100.0" y = "70.0"> 
     <attribute key = "type"      value = "Point"/>
     <attribute key = "elevation" value = "600" units ="ft" />
     <attribute key = "source"    value = "0.0"   />
     <attribute key = "demand"    value = "0.0"   />
  </node>

  ... details of nodes 2 and 3 removed ...

  <!-- ======================================== -->
  <!-- Junction 1 ...                           -->
  <!-- ======================================== -->

  <node ID = "004" x = "295.0" y = "70.0"> 
     <description text="Network Junction 1" />

     <attribute key = "type"      value = "Junction"/>
     <attribute key = "elevation" value = "600" units ="ft" />

     <shape type = "Rectangle">
        <attribute key =   "level" value = "48.0"/>
        <attribute key =   "width" value = "10.0"/>
        <attribute key =  "height" value = "10.0"/>
        <attribute key = "opacity" value = "1.0"/>
        <attribute key =   "color" value = "maroon"/>
     </shape>
  </node>

  <!-- ======================================== -->
  <!-- Junction 2 ...                           -->
  <!-- ======================================== -->

  <node ID = "005" x = "370.0" y =  "70.0">
     <attribute key = "type"      value = "Point"/>
     <attribute key = "elevation" value = "600" units ="ft" />
  </node>

  <node ID = "006" x = "370.0" y = "105.0"> 
     <description text="Network Junction 2" />

     <attribute key = "type"      value = "Junction" />
     <attribute key = "elevation" value = "600" units ="ft" />

     <shape type = "Rectangle">
        <attribute key =   "level" value = "48.0"/>
        <attribute key =   "width" value = "10.0"/>
        <attribute key =  "height" value = "10.0"/>
        <attribute key = "opacity" value = "1.0"/>
        <attribute key =   "color" value = "maroon"/>
     </shape>
  </node>

  <!-- ======================================== -->
  <!-- Junction 3 ...                           -->
  <!-- ======================================== -->

  <node ID = "007" x = "370.0" y = "225.0"> 
     <description text="Network Junction 3" />

     <attribute key = "type"      value = "Junction" />
     <attribute key = "elevation" value = "650" units ="ft" />

     <shape type = "Rectangle">
        <attribute key =   "level" value = "48.0"/>
        <attribute key =   "width" value = "10.0"/>
        <attribute key =  "height" value = "10.0"/>
        <attribute key = "opacity" value = "1.0"/>
        <attribute key =   "color" value = "maroon"/>
     </shape>
  </node>

  <!-- ======================================== -->
  <!-- Junction 4 ...                           -->
  <!-- ======================================== -->

  <node ID = "008" x = "370.0" y = "345.0"> 
     <description text="Network Junction 4" />

     <attribute key = "type"      value = "Junction" />
     <attribute key = "elevation" value = "660" units ="ft" />

     <shape type = "Rectangle">
        <attribute key =   "level" value = "48.0"/>
        <attribute key =   "width" value = "10.0"/>
        <attribute key =  "height" value = "10.0"/>
        <attribute key = "opacity" value =  "1.0"/>
        <attribute key =   "color" value = "maroon"/>
     </shape>
  </node>

  <!-- ======================================== -->
  <!-- Nodes: Junction 4 to Tank .          .   -->
  <!-- ======================================== -->

  <node ID = "009" x = "370.0" y = "400.0">
     <attribute key = "type"      value = "Point"/>
     <attribute key = "elevation" value = "660" units ="ft" />
  </node>

  <node ID = "010" x = "420.0" y = "400.0"> 
     <attribute key = "type"      value = "Point"/>
     <attribute key = "elevation" value = "660" units ="ft" />
  </node>

  <node ID = "011" x = "420.0" y = "380.0"> 
     <attribute key = "type"      value = "Point"/>
     <attribute key = "elevation" value = "660" units ="ft" />
  </node>

  <!-- ======================================== -->
  <!-- Control network coordinates              -->
  <!-- ======================================== -->

  ... details removed ...

  <!-- ======================================== -->
  <!-- Water network ways ...                   -->
  <!-- ======================================== -->

  <way ID="001">
     <description text="Reservoir to Pump." />
     <attribute key = "type" value = "Pipeline"/>

     <!-- Graph layer: Connectivity of pipe ends -->

     <attribute key = "end1" value = "C01" />
     <attribute key = "end2" value = "002" />

     <!-- Geometry layer: Sequence of nodal coordinates -->

     <node ID="001" />
     <node ID="002" />

     <!-- Engineering parameters -->

     <attribute key = "diameter"  value =    "4" />
     <attribute key = "length"    value =   "80" />

     <shape type = "LineString">
          <attribute key = "level" value =  "50.0"/>
          <attribute key = "width" value =   "6.0"/>
          <attribute key = "color" value = "black"/>
     </shape>
  </way>

  <way ID="002">
     <description text="Connect Water Pump to Junction 1." />
     <attribute key = "type" value = "Pipeline"/>

     <!-- Graph layer: Connectivity of pipe ends -->

     <attribute key = "end1" value = "003" />
     <attribute key = "end2" value = "004" />

     <!-- Geometry layer: Sequence of nodal coordinates -->

     <node ID="003" />
     <node ID="004" />

     <!-- Engineering parameters -->

     <attribute key = "diameter"  value =    "4" />
     <attribute key = "length"    value =   "95" />

     ... details of shape removed ...

  </way>

  <way ID="003">
     <description text="Connect Junction 1 to Junction 2." />
     <attribute key = "type" value = "Pipeline"/>

     <!-- Graph layer: Connectivity of pipe ends -->

     <attribute key = "end1" value = "004"/>
     <attribute key = "end2" value = "006"/>

     <!-- Geometry layer: Sequence of nodal coordinates -->

     <node ID="004" />
     <node ID="005" />
     <node ID="006" />

     ... details of shape removed ...

  </way>

  <way ID="004">
     <description text="Connect Junction 2 to Junction 3." />
     <attribute key = "type" value = "Pipeline"/>

     <!-- Graph layer: Connectivity of pipe ends -->

     <attribute key = "end1" value = "006" />
     <attribute key = "end2" value = "007" />

     <!-- Geometry layer: Sequence of nodal coordinates -->

     <node ID="006" />
     <node ID="007" />

     ... details of parameters and shape removed ...

  </way>

  <way ID="005">
     <description text="Connect Junction 3 to Junction 4." />
     <attribute key = "type" value = "Pipeline"/>

     <!-- Graph layer: Connectivity of pipe ends -->

     <attribute key = "end1" value = "007" />
     <attribute key = "end2" value = "008" />

     <!-- Geometry layer: Sequence of nodal coordinates -->

     <node ID="007" />
     <node ID="008" />

     ... details of parameters and shape removed ...

  </way>

  <way ID="006">
     <description text="Connect Junction 4 to Water Tank." />
     <attribute key = "type" value = "Pipeline"/>

     <!-- Graph layer: Connectivity of pipe ends -->

     <attribute key = "end1" value = "008" />
     <attribute key = "end2" value = "C03" />

     <!-- Geometry layer: Sequence of nodal coordinates -->

     <node ID="008" />
     <node ID="009" />
     <node ID="010" />
     <node ID="011" />

     ... details of parameters and shape removed ...

  </way>

  <!-- ============================================= -->
  <!-- Water Reservoir Component ...                 -->
  <!-- ============================================= -->

  <component ID = "C01" x = "0.0" y = "0.0">
     <description text="Water Reservoir" />

     <!-- Engineering parameters -->

     <attribute key = "type"  value = "Reservoir" />
     <attribute key = "elevation" value = "600.0" units ="ft" />
     <attribute key = "head"  value = "600.0" />
     <attribute key = "area"  value = "0.0" />

     <!-- Look-and-feel of component shape -->

     <compoundshape ID = "Reservoir-Shape01">
        <shape ID = "Reservoir" type = "Polygon">
            <attribute key =   "level" value = "48.0"/>
            <attribute key = "opacity" value = "1.0"/>
            <attribute key =   "color" value = "blue"/>
            <attribute key =    "fill" value = "true"/>
            <node ID="r01" x =  "10.0" y =  "10.0" type="Point" />
            <node ID="r02" x = "100.0" y =  "10.0" type="Point" /> 
            <node ID="r03" x = "100.0" y = " 90.0" type="Point" />
            <node ID="r04" x = " 90.0" y = "100.0" type="Point" /> 
            <node ID="r05" x =  "10.0" y = "100.0" type="Point" />
        </shape>
     </compoundshape>
  </component>

  <!-- ============================================= -->
  <!-- Water Tank Component ...                      -->
  <!-- ============================================= -->

  <component ID = "C03" x = "400.0" y = "200.0">
     <description text="Elevated Water Tank" />

     <!-- Engineering parameters -->

     <attribute key = "type"      value = "Tank" />
     <attribute key = "elevation" value = "700.0" units ="ft" />
     <attribute key = "area"      value = "400.0" />
     <attribute key = "minlevel"  value =   "0.0" />
     <attribute key = "maxlevel"  value =  "20.0" />
     <attribute key = "initlevel" value =   "2.0" />

     <!-- Look-and-feel of component shape -->

     <compoundshape ID = "Water-Tank-Shape01">
        <shape type = "Polygon">
            <attribute key =   "level" value = "48.0"/>
            <attribute key =   "color" value = "blue"/>
            <attribute key = "opacity" value = "1.0"/>
            <node ID="n01" x =  "0.0" y = "170.0" type="Point" />
            <node ID="n02" x =  "0.0" y = "120.0" type="Point" /> 
            <node ID="n03" x = "20.0" y = "100.0" type="Point" /> 
            <node ID="n04" x = "60.0" y = "100.0" type="Point" /> 
            <node ID="n05" x = "80.0" y = "120.0" type="Point" /> 
            <node ID="n06" x = "80.0" y = "170.0" type="Point" /> 
        </shape>

        ... details of shape removed ...

     </compoundshape>
  </component>

  <!-- ===================================================== -->
  <!-- Water pump: simplified statechart model ...           -->
  <!-- ===================================================== -->

  <behavior ID="B002" type="FSM">
     <description text="Simple model of pump behavior ..." />

     ... details of finite state machine behavior removed ...

  </behavior>

  <!-- ===================================================== -->
  <!-- Water pump component model ...                        -->
  <!-- ===================================================== -->

  <component ID = "C04" x = "190.0" y = "70.0">
     <description text="Water Pump" />

     <!-- Connectivity of pump to nodes -->

     <attribute key = "end1" value = "002" />
     <attribute key = "end2" value = "003" />

     <!-- Component-level attributes -->

     <attribute key = "type"   value = "Pump" />
     <attribute key = "height" value = "20.0" />
     <attribute key = "width"  value = "20.0" />

     <!-- Pump attributes -->

     <attribute key = "minhead" value =    "0.0" />
     <attribute key = "maxhead" value =  "100.0" />
     <attribute key =   "power" value = "5000.0" />

     <!-- Statechart behavior model .... -->

     <behavior id="#B002"/> 

     ... details of input and output ports removed ...

     <compoundshape ID = "Shape01">
        <shape ID = "sh01" x = "0.0" y = "0.0" type = "Rectangle">
            <attribute key =   "level" value = "50.0"/>
            <attribute key =   "width" value = "20.0"/>
            <attribute key =  "height" value = "20.0"/>
            <attribute key = "opacity" value = "1.0"/>
            <attribute key =    "fill" value = "true"/>
            <attribute key =   "color" value = "blue"/>
        </shape>
     </compoundshape>
  </component> 

</SystemDataModel>


Example 4: Simple Schematic for HVAC/Fan/Room Temperature Control

This section presents the "system data model" version of the simple schematic for temperature control of two rooms with HVAC equipment.

As already noted, the schematic model is orgnized into a two-layer composite hierarchy, and employs a variety of workspace transformations and rotations. The workspace axes and orientations are illustrated with dark black arrows.

    ... insert example ...

Insert content ...


Example 5: Layout of Networks for HVAC/Air Distribution

Here is the floorplan of an office building in Chicago, with a preliminary layout of a network for air distribution:

The size and complexity of this problem (i.e., the data file will be tens-of-thousands of lines long) points to a strong need for tools for (semi-) automated generation of the data files:

    ... insert example ...

Insert content ...

Part 10: Behavior Modeling with Statecharts

  • Frequently-Asked Questions
  • Example 1: Behavior of a Simple Pump
  • Example 2: Behavior of a Surveillance System
  • Example 3: Simplified Statechart Behavior for Drone Operation
  • Example 4: Composite Statechart Behavior of a Drone Surveillance System

Frequently-Asked Questions

  • What is a Statechart?

  • How are Statecharts created in Whistle?

  • Where can I find the statechart code in Whistle?


Working with the System Data Model

Component-level behaviors are described by statecharts, e.g.,

and evaluated with respect to collections of specifications (or constraints) attached to the component.

System-level behaviors will correspond to networks (or collections) of interacting statecharts.

Specification of a Statechart behavior

Statechart behaviors are defined within the behavior tags of the system data model. The basic hierarchy of tags is as follows:

    <behavior>

       <state id="state01" >

          ... details of a state go here ...

       </state>

       <state id="state02" >

          ... details of a state go here ...

       </state>

       <transition id="trans01">

          ... details of a transition go here ...

       </transition>

       <transition id="trans02">

          ... details of a transition go here ...

       </transition>

    </behavior>

tags of the system data model.

Specification of Events

Statecharts respond to sequences of incoming transition events.



Example 1: Behavior of a Simple Pump

In this example we create an XML specification for behavior of a small water pump.

We assume that the pump enters an operating state by connecting the pump to a power source. Within the operating state, the pump may be either on or off. The pump leaves the operating state when the power sources is disconnected.


Specification of Behavior

The specification for pump behavior is as follows:

  <behavior ID="B001" type="FSM">
     <description text="Simple model of pump behavior ..." />

     <!-- Initial behavior state -->

     <state ID = "start" type = "Initial">
         <attribute key = "label" value = "Start"/>
         <shape type = "Rectangle" x = "50" y = "10">
            <attribute key =  "width" value = "40.0"/>
            <attribute key = "height" value = "30.0"/>
            <attribute key =  "style" value = "vertexStyle02"/>
         </shape>
     </state>

     <!-- Pump Operations -->

     <state ID = "operating" type = "Hierarchical" >
         <description text="Hierarchical Statechart: Pumping Operations ..." />
         <attribute key = "label" value = "Pump Operations"/>

         <state ID = "start" type = "Initial">
             <attribute key = "label" value = "s"/>
             <shape type = "Rectangle" x = "15" y = "15">
                <attribute key =  "width" value = "20.0"/>
                <attribute key = "height" value = "20.0"/>
                <attribute key =  "style" value = "vertexStyle02"/>
             </shape>
         </shape>

         <state ID = "off" type = "Regular" >
             <attribute key = "label" value = "Pump Off"/>
             <shape type = "Rectangle" x = "15" y = "70">
                 <attribute key =  "width" value = "200.0"/>
                 <attribute key = "height" value = " 40.0"/>
                 <attribute key =  "style" value = "vertexStyle04"/>
             </shape>
         </shape>

         <state ID = "on" type = "Regular" >
             <attribute key = "label" value = "Pump On"/>
             <shape type = "Rectangle" x = "15" y = "160">
                <attribute key =  "width" value = "200.0"/>
                <attribute key = "height" value = " 40.0"/>
                <attribute key =  "style" value = "vertexStyle04"/>
             </shape>
         </shape>

         <!-- Operating state shape parameters -->

         <shape type = "Rectangle" x = "20" y = "70">
            <attribute key =  "width" value = "230.0"/>
            <attribute key = "height" value = "260.0"/>
            <attribute key =  "style" value = "vertexStyle03"/>
         </shape>
     </state>

     <!-- End state for pump operations -->

     <state ID = "end" type = "Final" >
         <attribute key = "label" value = "End"/>
         <shape type = "Rectangle" x = "180" y = "360">
            <attribute key =  "width" value = "40.0"/>
            <attribute key = "height" value = "30.0"/>
            <attribute key =  "style" value = "vertexStyle01"/>
         </shape>
     </state>

     <!-- List of system transitions -->

     <transition id = "tr01" type="Local" event = "connect" source = "start" target="operating">
        <attribute key = "label" value = "connect"/>
        <shape type = "LineString">
            <attribute key = "x1" value = "1.0"/>
            <attribute key = "y1" value = "0.5"/>
            <attribute key = "x2" value = "0.5"/>
            <attribute key = "y2" value = "0.0"/>
            <attribute key = "style" value = "edgeStyle01"/>
        </shape>
     </transition>

     <transition id = "tr02" type="Local" event = "start" source = "operating.start" target="operating.off">
        <attribute key = "label" value = "start"/>
        <shape type = "LineString">
            <attribute key = "x1" value = "1.0"/>
            <attribute key = "y1" value = "0.5"/>
            <attribute key = "x2" value = "0.3"/>
            <attribute key = "y2" value = "0.0"/>
            <attribute key = "style" value = "edgeStyle01"/>
        </shape>
     </transition>

     <transition id = "tr03" type="Local" event = "on" source = "operating.off" target="operating.on">
        <attribute key = "label" value = "on"/>
        <shape type = "LineString">
            <attribute key = "x1" value = "0.3"/>
            <attribute key = "y1" value = "1.0"/>
            <attribute key = "x2" value = "0.3"/>
            <attribute key = "y2" value = "0.0"/>
            <attribute key = "style" value = "edgeStyle01"/>
        </shape>
     </transition>

     <transition id = "tr04" type="Local" event = "off" source = "operating.on" target="operating.off">
        <attribute key = "label" value = "off"/>
        <shape type = "LineString">
            <attribute key = "x1" value = "0.7"/>
            <attribute key = "y1" value = "0.0"/>
            <attribute key = "x2" value = "0.7"/>
            <attribute key = "y2" value = "1.0"/>
            <attribute key = "style" value = "edgeStyle01"/>
        </shape>
     </transition>

     <transition id = "tr05" type="Local" event = "disconnect" source = "operating" target="end">
        <attribute key = "label" value = "disconnect"/>
        <shape type = "LineString">
            <attribute key = "x1" value = "0.5"/>
            <attribute key = "y1" value = "1.0"/>
            <attribute key = "x2" value = "0.0"/>
            <attribute key = "y2" value = "0.5"/>
            <attribute key = "style" value = "edgeStyle01"/>
        </shape>
     </transition>

  </behavior>

Points to note:

  • .....

  • .....

  • .....


Creating a Statechart Model

Steps 1 and 2 in the fragment of Whistle code:

// ========================================================================
// Print statechart associated with a pump component ....
//
// Written by: Mark Austin                                      August 2019
// ========================================================================


import whistle.util.system.model.Behavior;
import whistle.util.system.model.Component;
import whistle.util.system.model.SystemDataModel;
import whistle.util.system.model.BehaviorStatechartVisitor;

import whistle.util.statechart.StatechartMVC;

program ("Assemble Statecharts in Statechart model") {

   // Step 1: Import pump data into system data model ...

   sdm01 = SystemDataModel();
   sdm01.getData ("data/umd-water-pump03.xml");

   // Step 2: Extract behavior B001 from system data model, then print ...

   b01 = sdm01.getBehavior("B001");
   print b01.toString();

   // Step 3: Create empty statechart model ... ";

   chart = StatechartMVC();
   chart.setTitle("Pump Operations");
   chart.setSize( 300, 450);
   chart.createMVC();

   // Step 4: Create statechart model visitor ...

   visitor01 = BehaviorStatechartVisitor();
   visitor01.add( chart );

   // Step 5: Add states and transitions to statechart model ...

   b01.accept ( visitor01 );

   // Step 6: Process statechart model ...

   chart.build();

   // Step 7: Print populated statechart model ... 

   stm = chart.getModel();
   print stm.toString();

   // Step 8: Start statechart MVC ...

   chart.startStatechart();

   ... etc ...
}

imports the water pump data into the system data model, extracts and prints the statechart behavior B001.

The abbreviated output is:

Behavior: ID = B001, type = FSM 
-------------------------------------------------------- 
Description = [ Simple model of pump behavior ... ] 
-------------------------------------------------------- 
No attributes  = [  0 ] 
No states      = [  6 ] 
No transitions = [  5 ] 
-------------------------------------------------------- 

-------------------------------------------------------- 
STATE  1: 
******************************************************** 
Local Name = end 
Full Name  = B001.end 
Type       = Final 
-------------------------------------------------------- 
No attributes  = [ 1.0 ] 
No internal transitions = [ 0.0 ] 
No internal states      = [ 0.0 ] 
-------------------------------------------------------- 
Attribute  1:  [key, value] = [ "label", "End" ]
-------------------------------------------------------- 
Shape: type = Rectangle
-------------------------------------------------------- 
Position: (x,y) = ( 180.0, 360.0 ) 
-------------------------------------------------------- 
No attributes = [ 3.0 ] 
No nodes      = [ 0.0 ] 
-------------------------------------------------------- 
Attribute 1:  [key, value] = [ "width", "40.0" ]
Attribute 2:  [key, value] = [ "height", "30.0" ]
Attribute 3:  [key, value] = [ "style", "vertexStyle01" ]
-------------------------------------------------------- 

-------------------------------------------------------- 
STATE  2: 
******************************************************** 
Local Name = operating 
Full Name  = B001.operating 
Type       = Hierarchical 
-------------------------------------------------------- 
Description = [ Hierarchical Statechart: Pumping Operations ... ] 
-------------------------------------------------------- 
No attributes  = [ 1.0 ] 
No internal transitions = [ 0.0 ] 
No internal states      = [ 3.0 ] 
-------------------------------------------------------- 
Attribute  1:  [key, value] = [ "label", "Pump Operations" ]
-------------------------------------------------------- 
Shape: type = Rectangle
-------------------------------------------------------- 
Position: (x,y) = ( 20.0, 70.0 ) 
-------------------------------------------------------- 
No attributes = [ 3.0 ] 
No nodes      = [ 0.0 ] 
-------------------------------------------------------- 
Attribute 1:  [key, value] = [ "width", "230.0" ]
Attribute 2:  [key, value] = [ "height", "260.0" ]
Attribute 3:  [key, value] = [ "style", "vertexStyle03" ]
-------------------------------------------------------- 

-------------------------------------------------------- 
STATE  3: 
******************************************************** 
Local Name = off 
Full Name  = B001.operating.off 
Type       = Regular 
-------------------------------------------------------- 
No attributes  = [ 1.0 ] 
No internal transitions = [ 0.0 ] 
No internal states      = [ 0.0 ] 
-------------------------------------------------------- 
Attribute  1:  [key, value] = [ "label", "Pump Off" ]
-------------------------------------------------------- 
Shape: type = Rectangle
-------------------------------------------------------- 
Position: (x,y) = ( 15.0, 70.0 ) 
-------------------------------------------------------- 
No attributes = [ 3.0 ] 
No nodes      = [ 0.0 ] 
-------------------------------------------------------- 
Attribute 1:  [key, value] = [ "width", "200.0" ]
Attribute 2:  [key, value] = [ "height", " 50.0" ]
Attribute 3:  [key, value] = [ "style", "vertexStyle02" ]
-------------------------------------------------------- 

... details of states 4 and 5 removed ...

-------------------------------------------------------- 
STATE  6: 
******************************************************** 
Local Name = start 
Full Name  = B001.start 
Type       = Initial 
-------------------------------------------------------- 
No attributes  = [ 1.0 ] 
No internal transitions = [ 0.0 ] 
No internal states      = [ 0.0 ] 
-------------------------------------------------------- 
Attribute  1:  [key, value] = [ "label", "Start" ]
-------------------------------------------------------- 
Shape: type = Rectangle
-------------------------------------------------------- 
Position: (x,y) = ( 50.0, 10.0 ) 
-------------------------------------------------------- 
No attributes = [ 3.0 ] 
No nodes      = [ 0.0 ] 
-------------------------------------------------------- 
Attribute 1:  [key, value] = [ "width", "40.0" ]
Attribute 2:  [key, value] = [ "height", "30.0" ]
Attribute 3:  [key, value] = [ "style", "vertexStyle02" ]
-------------------------------------------------------- 

-------------------------------------------------------- 
TRANSITION  1: 
******************************************************** 
Local Name = tr01 
Full Name  = B001.tr01 
Type       = Local 
-------------------------------------------------------- 
Source state = "B001.start" 
Target state = "B001.operating" 
Event  = "connect" 
-------------------------------------------------------- 
Attribute 1:  [key, value] = [ "label", "connect" ]
-------------------------------------------------------- 
Shape: type = LineString
-------------------------------------------------------- 
No attributes = [ 5.0 ] 
No nodes      = [ 0.0 ] 
-------------------------------------------------------- 
Attribute 1:  [key, value] = [ "x1", "1.0" ]
Attribute 2:  [key, value] = [ "y1", "0.5" ]
Attribute 3:  [key, value] = [ "x2", "0.5" ]
Attribute 4:  [key, value] = [ "y2", "0.0" ]
Attribute 5:  [key, value] = [ "style", "edgeStyle01" ]
-------------------------------------------------------- 

... details of transitions 2 through 4 removed ...

-------------------------------------------------------- 
TRANSITION  5: 
******************************************************** 
Local Name = tr05 
Full Name  = B001.tr05 
Type       = Local 
-------------------------------------------------------- 
Source state = "B001.operating" 
Target state = "B001.end" 
Event  = "disconnect" 
-------------------------------------------------------- 
Attribute 1:  [key, value] = [ "label", "disconnect" ]
-------------------------------------------------------- 
Shape: type = LineString
-------------------------------------------------------- 
No attributes = [ 5.0 ] 
No nodes      = [ 0.0 ] 
-------------------------------------------------------- 
Attribute 1:  [key, value] = [ "x1", "0.5" ]
Attribute 2:  [key, value] = [ "y1", "1.0" ]
Attribute 3:  [key, value] = [ "x2", "0.0" ]
Attribute 4:  [key, value] = [ "y2", "0.5" ]
Attribute 5:  [key, value] = [ "style", "edgeStyle01" ]
-------------------------------------------------------- 

Steps 3 through 8 show the process of creating an empty StatechartMVC. The statechart is populated with states and transitions by visiting the Behavior object.

The abbreviated statechart model output is:

STATECHART DATA MODEL: name = null ...
==================================================== ...
No states      = 6 ...
No transitions = 5 ...
No concurrent regions = 0 ...
==================================================== ...
STATECHART STATES ...
==================================================== ...

--- State  1 ...
---   State name   = B001.end.vertex ...
---   Parent state = Generic Statechart ...
---   State label  = End ...
---   State type   = FINALSTATE ...
---   No exit transitions = 0 ...
---   Has entry action = null ... 
---   Has do action    = null ... 
---   Has exit action  = null ... 

--- State  2 ...
---   State name   = B001.operating.vertex ...
---   Parent state = Generic Statechart ...
---   State label  = Pump Operations ...
---   State type   = HIERARCHICALSTATE ...
---   Exit transition (1) = B001.tr05.edge ...
---   Has entry action = null ... 
---   Has do action    = null ... 
---   Has exit action  = null ... 

--- State  3 ...
---   State name   = B001.operating.off.vertex ...
---   Parent state = B001.operating.vertex ...
---   State label  = Pump Off ...
---   State type   = STATE ...
---   Exit transition (1) = B001.tr03.edge ...
---   Has entry action = null ... 
---   Has do action    = null ... 
---   Has exit action  = null ... 

--- State  4 ...
---   State name   = B001.operating.on.vertex ...
---   Parent state = B001.operating.vertex ...
---   State label  = Pump On ...
---   State type   = STATE ...
---   Exit transition (1) = B001.tr04.edge ...
---   Has entry action = null ... 
---   Has do action    = null ... 
---   Has exit action  = null ... 

--- State  5 ...
---   State name   = B001.operating.start.vertex ...
---   Parent state = B001.operating.vertex ...
---   State label  = s ...
---   State type   = PSEUDOSTATE ...
---   Exit transition (1) = B001.tr02.edge ...
---   Has entry action = null ... 
---   Has do action    = null ... 
---   Has exit action  = null ... 

--- State  6 ...
---   State name   = B001.start.vertex ...
---   Parent state = Generic Statechart ...
---   State label  = Start ...
---   State type   = PSEUDOSTATE ...
---   Exit transition (1) = B001.tr01.edge ...
---   Has entry action = null ... 
---   Has do action    = null ... 
---   Has exit action  = null ... 

==================================================== ...
STATECHART TRANSITIONS ...
==================================================== ...

--- Transition  1 ...
---   name  = B001.tr05.edge ...
---   label =  ...
---   Source state    = B001.operating.vertex ...
---   Target state    = B001.end.vertex ...
---   Has trigger action  = false ...
---   Has trigger event   = Statchart.model.TransitionEvent(disconnect) ... ...
---   Has guard condition = false ...

--- Transition  2 ...
---   name  = B001.tr03.edge ...
---   label =  ...
---   Source state    = B001.operating.off.vertex ...
---   Target state    = B001.operating.on.vertex ...
---   Has trigger action  = false ...
---   Has trigger event   = Statchart.model.TransitionEvent(on) ... ...
---   Has guard condition = false ...

--- Transition  3 ...
---   name  = B001.tr04.edge ...
---   label =  ...
---   Source state    = B001.operating.on.vertex ...
---   Target state    = B001.operating.off.vertex ...
---   Has trigger action  = false ...
---   Has trigger event   = Statchart.model.TransitionEvent(off) ... ...
---   Has guard condition = false ...

--- Transition  4 ...
---   name  = B001.tr02.edge ...
---   label =  ...
---   Source state    = B001.operating.start.vertex ...
---   Target state    = B001.operating.off.vertex ...
---   Has trigger action  = false ...
---   Has trigger event   = Statchart.model.TransitionEvent(start) ... ...
---   Has guard condition = false ...

--- Transition  5 ...
---   name  = B001.tr01.edge ...
---   label =  ...
---   Source state    = B001.start.vertex ...
---   Target state    = B001.operating.vertex ...
---   Has trigger action  = false ...
---   Has trigger event   = Statchart.model.TransitionEvent(connect) ... ...
---   Has guard condition = false ...

==================================================== 


Processing Sequences of Statechart Events

Statechart behavior corresponds to the processing of sequences of statechart events.

For example, the sequence of state configuations:

in generated by the fragment of code:

   // Event 1: Connect pump to power source and transition to operating state.

      chart.executeTransitionEvent( "B001.connect.edge" );

   // Event 2: Transition to off state within operating ...

      chart.executeTransitionEvent( "B001.start.edge" );

   // Event 3: Turn pump on ...

      chart.executeTransitionEvent( "B001.on.edge" );

   // Event 4: Turn pump off ...

      chart.executeTransitionEvent( "B001.off.edge" );

   // Event 5: Disconnect pump ...

      chart.executeTransitionEvent( "B001.disconnect.edge" );

Points to note:

  • .....

  • .....

  • .....


Example 2: Behavior of a Surveillance System


Specification of Behavior

    ... details coming soon ... 


Processing Sequences of Statechart Events

    ... details coming soon ... 

    ... details coming soon ... 


Example 3: Simplified Statechart Behavior for Drone Operation


Specification of Behavior

The abbreviated specification of statechart behavior is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<SystemDataModel author="Mark Austin" date="2018-01" source="UMD">

  .... details removed ....  

  <!-- ============================================ -->
  <!-- Part 02: Drone behavior model                -->
  <!-- ============================================ -->

  <behavior ID="B001" type = "FSM" >
     <description text="Composite Statechart model of drone behavior ..." />

     <!-- ===================================== -->
     <!-- Composite State 01: Flying operations -->
     <!-- ===================================== -->

     <state ID = "CompositeState01" type = "Composite" >
        <description text="Composite Statechart: Flying Operations ..." />

        <state ID = "State01" type = "Regular" >
           <description text="Takeoff" />
           <:shape type = "Rectangle">
              <attribute key = "width" value = "6.0"/>
              <node ID="State1-01" x = "  0.0" y =   "0.0" type="Point" />
              <node ID="State1-02" x = "100.0" y =   "0.0" type="Point" />
              <node ID="State1-03" x = "100.0" y = "100.0" type="Point" />
              <node ID="State1-04" x = "  0.0" y = "100.0" type="Point" />
           </shape>
        </state>

        <state ID = "State02" type = "Regular" >
           <description text="Hovering" />
           <transition id = "tr02" type="Local" event = "command.forward" target="#State03">
               <shape type = "LineString">
                  <attribute key = "width" value = "6.0"/>
                  <node ID="State02-01" x = "300.0" y = "100.0" type="Point" />
                  <node ID="State02-02" x = "400.0" y = "100.0" type="Point" />
               </shape>
           </transition>
           <shape type = "Rectangle">
              <attribute key = "width" value = "6.0"/>
              <node ID="State2-01" x = "300.0" y = "100.0" type="Point" />
              <node ID="State2-02" x = "400.0" y = "100.0" type="Point" />
              <node ID="State2-03" x = "400.0" y = "200.0" type="Point" />
              <node ID="State2-04" x = "300.0" y = "200.0" type="Point" /> 
           </shape>
        </state>

        <state ID = "State03" type = "Regular">
           <description text="Forward" />
           <transition id = "tr03" type="Local" event = "command.hover" target="#State02" />
           <transition id = "tr04" type="Local" event = "command.land"  target="#State04" />
           <shape type = "Rectangle">
              <attribute key = "width" value = "6.0"/>
              <node ID="State3-01" x = "500.0" y = "100.0" type="Point" />
              <node ID="State3-02" x = "600.0" y = "100.0" type="Point" /> 
              <node ID="State3-03" x = "600.0" y = "200.0" type="Point" />
              <node ID="State3-04" x = "600.0" y = "200.0" type="Point" />
           </shape>
        </state>

        <state ID = "State04" type = "Regular">
           <description text="Land" />
           <shape type = "Rectangle">
              <attribute key = "width" value = "6.0"/>
              <node ID="State4-01" x = "700.0" y = "100.0" type="Point" />
              <node ID="State4-02" x = "800.0" y = "100.0" type="Point" />
              <node ID="State4-03" x = "800.0" y = "200.0" type="Point" />
              <node ID="State4-04" x = "700.0" y = "200.0" type="Point" />
           </shape>
        </state>

        <state ID = "State05" type = "Final">
           <shape type = "Circle">
              <attribute key = "center" value = "#State5-01" />
              <attribute key = "radius" value = "10.0"/>
              <node ID="State5-01" x = "500.0" y = "500.0" type="Point" />
           </shape>
        </state>

        <transition id="tr01" type = "Local" event = "command.takeoff" source = "#State01" target="#State02">
           <shape type = "LineString">
              <attribute key = "width" value = "6.0"/>
              <node ID="tr01" x =   "0.0" y = "50.0" type="Point" />
              <node ID="tr02" x = "200.0" y = "50.0" type="Point" />
           </shape>
        </transition>

     </state>

     <!-- ============================================= -->
     <!-- Composite State 02: Mode of flying operations -->
     <!-- ============================================= -->

     <state ID = "CompositeState02" type = "Composite" >
        <description text="Composite Statechart: Mode of Flying Operations ..." />

        <state ID = "State01" type = "Regular" >
            <description text="Manual Operation" />
            <transition id = "tr01" type="Local" event = "communication.working" target="#State02" />
        </state>

        <state ID = "State02" type = "Regular" >
            <description text="Autonomous Operation" />
        </state>

        <transition id = "tr02" type="Local" event = "communication.failure"
                    source = "#State02" target="#State01">
        </transition>

     </state>

     <!-- =================================== -->
     <!-- External definition of transitions  -->
     <!-- =================================== -->

     <transition id = "tr01" type = "Local" event = "command.takeoff"
                 source = "#CompositeState01" target="#CompositeState02">
     </transition>

     <transition id = "tr05" type = "Local" event = "command.forward"
                source = "#CompositeState01.State04" target="#CompositeState02.State01">
        <shape type = "LineString">
            <attribute key = "width" value = "6.0"/>
            <node ID="tr01" x = "800.0" y = "150.0" type="Point" />
            <node ID="tr02" x = "900.0" y = "150.0" type="Point" />
            <node ID="tr03" x = "900.0" y = "250.0" type="Point" />
            <node ID="tr04" x = "150.0" y = "250.0" type="Point" />
            <node ID="tr05" x = "150.0" y = "200.0" type="Point" />
        </shape>
     </transition>

  </behavior>

  <!-- =================================== -->
  <!-- Part 03: Drone component model      -->
  <!-- =================================== -->

  <component ID="Drone-X47-B" type="UAV" x = "100.0" y = "200.0" >
      lt&description text="NAVAIR X47-B Drone" />

      ... details removed ...

      <!-- =============================================== -->
      <!-- Reference to statechart model of drone behavior -->
      <!-- =============================================== -->

      <behavior ID="#B001" />

      ... details removed ...

  </component>
</SystemDataModel>


Processing Sequences of Statechart Events

    ... details coming soon ... 


Example 4: Composite Statechart Behavior of a Drone Surveillance System

Simulation Scenario (Work in Progress) ....

Consider a scenario where a drone ...


Snapshots of Behavior


Specification of Behavior

    ... details coming soon ... 


Processing Sequences of Statechart Events

    ... details coming soon ... 

Part 11: Spatial Modeling and Reasoning with Java Topology Suite

  • Frequently-Asked Questions
  • Example 1: Definition of Points and Polygons
  • Example 2: Set Operations on Polygons
  • Example 3: Set Operations for Drone Operations on a Flight Pathway
  • Example 4: Stitching Fragments of JTS Geometry
  • Example 5: Stitching Fragments of OSM Geometry
  • Example 6: Simplifying Geometry with the DouglasPeucker Algorithm

Frequently-Asked Questions

  • What is the Java Topology Suite (JTS)?

    The Java Topology Suite (JTS) is an open source Java Library for creating and manipulating vector geometry.

    Although coordinates maybe specified as points in three-dimensional space (i.e., x, y and z), algorithms for computational geometry are restricted to two dimensions.

  • What kinds of object can be modeled in JTS?

    Geometric object models can be created for Points, LineStrings, Polygons, and collections of objects.

  • Can JTS models be represented as Strings?

    Yes. String representations of JTS objects follow the well-known text (WKT) format.

  • Can I extract JTS models from OSM and SDM?

    The general idea is that an empty JTS model would visit a OSM/SDM model and extract the relevant geometry information.

    This is a work-in-progress.

  • Where can I find the JTS code in Whistle?

    Look under: src/whistle/util/jts/ ...


Example 1: Definition of Points and Polygons

The fragment on code:

// ===============================================================================
// input-geometry02: Definition and computation with polygons and their connection
// to the Java Topology Suite ...
//
// Written by: Mark Austin                                          September 2016
// ===============================================================================

import whistle.util.collections.HashSet;
import whistle.util.collections.ArrayList;
import whistle.util.jts.Point;
import whistle.util.jts.LineString;
import whistle.util.jts.LinearRing;
import whistle.util.jts.Polygon;

program ("Exercise Polygon Objects") {

   print "---";
   print "--- Part 01: Create basic whistle.model.geometry.Polygon() object ...";
   print "--- =================================================================";
   print "---";

   point01 = Point("pt01",    0.0,    0.0 );
   point02 = Point("pt02", 2000.0,    0.0 );
   point03 = Point("pt03", 2000.0, 2000.0 );
   point04 = Point("pt04",    0.0, 2000.0 );

   array01 = ArrayList();
   array01.add( point01 );
   array01.add( point02 );
   array01.add( point03 );
   array01.add( point04 );
   array01.add( point01 );

   shell01 = LinearRing("Polygon Shell");
   shell01.add( array01 );
   print shell01.toString();

   // Part 04: Assemble and print polygon ...

   polygon01 = Polygon();
   polygon01.setName("Test Polygon 01");
   polygon01.addShell( shell01 );
   polygon01.compile();
   print polygon01.toString();

   print "---";
   print "--- Part 02: Create basic whistle.model.geometry.Polygon() object ... ";
   print "--- ================================================================= ";
   print "---";

   point01 = Point("pt01", 1000.0, 1000.0 );
   point02 = Point("pt02", 3000.0, 1000.0 );
   point03 = Point("pt03", 3000.0, 3000.0 );
   point04 = Point("pt04", 1000.0, 3000.0 );

   array04 = ArrayList();
   array04.add( point01 ); array04.add( point02 );
   array04.add( point03 ); array04.add( point04 );
   array04.add( point01 );

   shell02 = LinearRing("Polygon Shell");
   shell02.add( array04 );

   polygon02 = Polygon();
   polygon02.setName("Test Polygon 02");
   polygon02.addShell( shell02 );
   polygon02.compile();
   print polygon02.toString();

   print "---";
   print "--- Part 03: Compute polygon01.intersection ( polygon 02 ) ... ";
   print "--- =================================================================";
   print "---";

   polygon03 = polygon01.intersection( polygon02 );
   polygon03.setName("Intersection Polygon 03");
   print polygon03.toString();

   print "---";
   print "--- Part 04: Compute polygon01.union ( polygon 02 ) ... ";
   print "--- =================================================================";
   print "---";

   polygon04 = polygon01.union( polygon02 );
   polygon04.setName("Union Polygon 04");
   print polygon04.toString();

   print "---";
   print "--- Part 05: Test containment of a point inside a polygon ... ";
   print "--- ================================================================="

   // Test containment of points inside polygon01 ...

   print "";
   point13 = Point(  100.0, 200.0 );
   x = polygon01.contains ( point13 );
   print "--- Test: polygon01.contains ( Coordinate(  100,  200) ) --> ", x;

   point14 = Point( -100.0, 200.0 );
   x = polygon01.contains ( point14 );
   print "--- Test: polygon01.contains ( Coordinate( -100,  200) ) --> ", x;

   point15 = Point( 1500.0, 2200.0 );
   x = polygon01.contains ( point15 );
   print "--- Test: polygon01.contains ( Coordinate( 1500, 2200) ) --> ", x;

   point16 = Point( 3500.0, 3500.0 );
   x = polygon01.contains ( point16 );
   print "--- Test: polygon01.contains ( Coordinate( 3500, 3500) ) --> ", x;

   point17 = Point( 5200.0, 5200.0 );
   x = polygon01.contains ( point17 );
   print "--- Test: polygon01.contains ( Coordinate( 5200, 5200) ) --> ", x;

   // Test containment of points inside polygon02 ...

   print "";
   x = polygon02.contains ( point13 );
   print "--- Test: polygon02.contains ( Coordinate(  100,  200) ) --> ", x;

   x = polygon02.contains ( point14 );
   print "--- Test: polygon02.contains ( Coordinate( -100,  200) ) --> ", x;

   x = polygon02.contains ( point15 );
   print "--- Test: polygon02.contains ( Coordinate( 1500, 2200) ) --> ", x;

   x = polygon02.contains ( point16 );
   print "--- Test: polygon02.contains ( Coordinate( 3500, 3500) ) --> ", x;

   x = polygon02.contains ( point17 );
   print "--- Test: polygon02.contains ( Coordinate( 5200, 5200) ) --> ", x;

   // Test containment of points inside polygon03 ...

   print "";
   x = polygon03.contains ( point13 );
   print "--- Test: polygon03.contains ( Coordinate(  100,  200) ) --> ", x;

   x = polygon03.contains ( point14 );
   print "--- Test: polygon03.contains ( Coordinate( -100,  200) ) --> ", x;

   x = polygon03.contains ( point15 );
   print "--- Test: polygon03.contains ( Coordinate( 1500, 2200) ) --> ", x;

   x = polygon03.contains ( point16 );
   print "--- Test: polygon03.contains ( Coordinate( 3500, 3500) ) --> ", x;

   x = polygon03.contains ( point17 );
   print "--- Test: polygon03.contains ( Coordinate( 5200, 5200) ) --> ", x;

   // Test containment of points inside polygon04 ...

   print "";
   x = polygon04.contains ( point13 );
   print "--- Test: polygon04.contains ( Coordinate(  100,  200) ) --> ", x;

   x = polygon04.contains ( point14 );
   print "--- Test: polygon04.contains ( Coordinate( -100,  200) ) --> ", x;

   x = polygon04.contains ( point15 );
   print "--- Test: polygon04.contains ( Coordinate( 1500, 2200) ) --> ", x;

   x = polygon04.contains ( point16 );
   print "--- Test: polygon04.contains ( Coordinate( 3500, 3500) ) --> ", x;

   x = polygon04.contains ( point17 );
   print "--- Test: polygon04.contains ( Coordinate( 5200, 5200) ) --> ", x;

   print "---";
   print "--- ======================================================== ";
   print "--- Finished !! ... ";
   print "---";

defines polygon01 as a rectangle with corner points [(0,0), (2000,2000)], and polygon02 as a rectangle with corner points [(1000,1000), (3000,3000)]. Two set operations are computed:

  • polygon03 is the intersection of polygon01 and polygon02.

  • polygon04 is the union of polygon01 and polygon02.

The abbreviated textual output is:

--- Part 01: Create basic whistle.model.geometry.Polygon() object ...
--- =================================================================

--- whistle.util.jts.LinearRing() ... 
--- ============================================ 
--- Name = "Polygon Shell"
--- Is closed?    = true ... 
--- Dimension = 1.0
--- JTS Geometry Type = LINEARRING
--- JTS Geometry = LINEARRING (0 0, 2000 0, 2000 2000, 0 2000, 0 0)
--- Length    = [ 8000, null]
--- NumberPoints  = 5.0

--- List of Points 
--- ===================================== 
---  1: (x,y) = (      0.00,      0.00) 
---  2: (x,y) = (   2000.00,      0.00) 
---  3: (x,y) = (   2000.00,   2000.00) 
---  4: (x,y) = (      0.00,   2000.00) 
---  5: (x,y) = (      0.00,      0.00) 
--- ===================================== 

--- whistle.util.jts.Polygon() ... 
--- ============================================ 
--- Name = "Test Polygon 01"
--- Dimension    = 2.0
--- JTS Geometry Type = POLYGON
--- JTS Geometry = POLYGON ((0 0, 2000 0, 2000 2000, 0 2000, 0 0))
--- Number of points =  5 
--- Number of holes  =  0 
--- Area =  4000000.000 

--- Shell coordinates ...                 
--- ===================================== 
---  1: (x,y) = (      0.00,      0.00) 
---  2: (x,y) = (   2000.00,      0.00) 
---  3: (x,y) = (   2000.00,   2000.00) 
---  4: (x,y) = (      0.00,   2000.00) 
---  5: (x,y) = (      0.00,      0.00) 
--- ===================================== 
--- Perimeter =   8.00e+03 

--- Part 02: Create basic whistle.model.geometry.Polygon() object ... 
--- ================================================================= 

--- whistle.util.jts.Polygon() ... 
--- ============================================ 
--- Name = "Test Polygon 02"
--- Dimension    = 2.0
--- JTS Geometry Type = POLYGON
--- JTS Geometry = POLYGON ((1000 1000, 3000 1000, 3000 3000, 1000 3000, 1000 1000))
--- Number of points =  5 
--- Number of holes  =  0 
--- Area =  4000000.000 

--- Shell coordinates ...                 
--- ===================================== 
---  1: (x,y) = (   1000.00,   1000.00) 
---  2: (x,y) = (   3000.00,   1000.00) 
---  3: (x,y) = (   3000.00,   3000.00) 
---  4: (x,y) = (   1000.00,   3000.00) 
---  5: (x,y) = (   1000.00,   1000.00) 
--- ===================================== 
--- Perimeter =   8.00e+03 

--- Part 03: Compute polygon01.intersection ( polygon 02 ) ... 
--- =================================================================

--- whistle.util.jts.Polygon() ... 
--- ============================================ 
--- Name = "Intersection Polygon 03"
--- Dimension    = 2.0
--- JTS Geometry Type = POLYGON
--- JTS Geometry = POLYGON ((1000 2000, 2000 2000, 2000 1000, 1000 1000, 1000 2000))
--- Number of points =  5 
--- Number of holes  =  0 
--- Area =  1000000.000 

--- Shell coordinates ...                 
--- ===================================== 
---  1: (x,y) = (   1000.00,   2000.00) 
---  2: (x,y) = (   2000.00,   2000.00) 
---  3: (x,y) = (   2000.00,   1000.00) 
---  4: (x,y) = (   1000.00,   1000.00) 
---  5: (x,y) = (   1000.00,   2000.00) 
--- ===================================== 
--- Perimeter =   4.00e+03 

--- Part 04: Compute polygon01.union ( polygon 02 ) ... 
--- =================================================================

--- whistle.util.jts.Polygon() ... 
--- ============================================ 
--- Name = "Union Polygon 04"
--- Dimension    = 2.0
--- JTS Geometry Type = POLYGON
--- JTS Geometry = POLYGON ((2000 1000, 2000 0, 0 0, 0 2000, 1000 2000, 1000 3000, 3000 3000, 3000 1000, 2000 1000))
--- Number of points =  9 
--- Number of holes  =  0 
--- Area =  7000000.000 

--- Shell coordinates ...                 
--- ===================================== 
---  1: (x,y) = (   2000.00,   1000.00) 
---  2: (x,y) = (   2000.00,      0.00) 
---  3: (x,y) = (      0.00,      0.00) 
---  4: (x,y) = (      0.00,   2000.00) 
---  5: (x,y) = (   1000.00,   2000.00) 
---  6: (x,y) = (   1000.00,   3000.00) 
---  7: (x,y) = (   3000.00,   3000.00) 
---  8: (x,y) = (   3000.00,   1000.00) 
---  9: (x,y) = (   2000.00,   1000.00) 
--- ===================================== 
--- Perimeter =   1.20e+04 

--- Part 05: Test containment of a point inside a polygon ... 
--- =================================================================

--- Test: polygon01.contains ( Coordinate(  100,  200) ) --> true
--- Test: polygon01.contains ( Coordinate( -100,  200) ) --> false
--- Test: polygon01.contains ( Coordinate( 1500, 2200) ) --> false
--- Test: polygon01.contains ( Coordinate( 3500, 3500) ) --> false
--- Test: polygon01.contains ( Coordinate( 5200, 5200) ) --> false

--- Test: polygon02.contains ( Coordinate(  100,  200) ) --> false
--- Test: polygon02.contains ( Coordinate( -100,  200) ) --> false
--- Test: polygon02.contains ( Coordinate( 1500, 2200) ) --> true
--- Test: polygon02.contains ( Coordinate( 3500, 3500) ) --> false
--- Test: polygon02.contains ( Coordinate( 5200, 5200) ) --> false

--- Test: polygon03.contains ( Coordinate(  100,  200) ) --> false
--- Test: polygon03.contains ( Coordinate( -100,  200) ) --> false
--- Test: polygon03.contains ( Coordinate( 1500, 2200) ) --> false
--- Test: polygon03.contains ( Coordinate( 3500, 3500) ) --> false
--- Test: polygon03.contains ( Coordinate( 5200, 5200) ) --> false

--- Test: polygon04.contains ( Coordinate(  100,  200) ) --> true
--- Test: polygon04.contains ( Coordinate( -100,  200) ) --> false
--- Test: polygon04.contains ( Coordinate( 1500, 2200) ) --> true
--- Test: polygon04.contains ( Coordinate( 3500, 3500) ) --> false
--- Test: polygon04.contains ( Coordinate( 5200, 5200) ) --> false


Example 2: Set Operations and Visualization of Polygons

This example demonstrates set operations on three rectangles, one of them containing two holes.

The fragment on code:

// ===============================================================================
// input-geometry04: Demonstrate:
//
// -- Graphical display of multipoints and multipolygons ...
//
// Written by: Mark Austin                                          September 2016
// ===============================================================================

import whistle.util.collections.HashSet;
import whistle.util.collections.ArrayList;
import whistle.util.jts.Point;
import whistle.util.jts.LineString;
import whistle.util.jts.LinearRing;
import whistle.util.jts.Polygon;
import whistle.util.jts.MultiPoint;
import whistle.util.jts.MultiPolygon;

import whistle.gui.Viewer2D;
import whistle.model.geometry.GridModel;

program ("Exercise Polygon Objects") { 

   print "--- PART 01: Create basic whistle.model.geometry.Polygon() object ...";
   print "--- =================================================================";

   point01 = Point("pt01",   0.0,   0.0 );
   point02 = Point("pt02", 500.0,   0.0 );
   point03 = Point("pt03", 500.0, 500.0 );
   point04 = Point("pt04",   0.0, 500.0 );

   array01 = ArrayList();
   array01.add( point01 );
   array01.add( point02 );
   array01.add( point03 );
   array01.add( point04 );
   array01.add( point01 );

   shell01 = LinearRing("Polygon Shell");
   shell01.add( array01 );
   print shell01.toString();

   point05 = Point("pt05", 100.0, 100.0 );
   point06 = Point("pt06", 200.0, 100.0 );
   point07 = Point("pt07", 200.0, 200.0 );
   point08 = Point("pt08", 100.0, 200.0 );

   array02 = ArrayList();
   array02.add( point05 );
   array02.add( point06 );
   array02.add( point07 );
   array02.add( point08 );
   array02.add( point05 );

   hole01 = LinearRing("Hole 01");
   hole01.add( array02 );
   print hole01.toString();

   // Part 03: Create linear ring for Hole 02 ... 

   point09 = Point("pt09", 300.0, 200.0 );
   point10 = Point("pt10", 400.0, 200.0 );
   point11 = Point("pt11", 400.0, 400.0 );
   point12 = Point("pt12", 300.0, 400.0 );

   array03 = ArrayList();
   array03.add( point09 ); array03.add( point10 );
   array03.add( point11 ); array03.add( point12 );
   array03.add( point09 );

   hole02 = LinearRing("Hole 02");
   hole02.add( array03 );
   print hole02.toString();

   // Part 04: Assemble and print polygon ... 

   polygon01 = Polygon();
   polygon01.setName("Test Polygon 01");
   polygon01.addShell( shell01 );
   polygon01.addHole(  hole01 );
   polygon01.addHole(  hole02 );
   polygon01.compile();
   print polygon01.toString();

   print "--- PART 02: Create basic whistle.model.geometry.Polygon() objects ... ";
   print "--- ================================================================== ";

   // Part 01: Create linear ring for polygon shell ... 

   point13 = Point("pt13", 350.0, 350.0 );
   point14 = Point("pt14", 800.0, 350.0 );
   point15 = Point("pt15", 800.0, 800.0 );
   point16 = Point("pt16", 350.0, 800.0 );

   array04 = ArrayList();
   array04.add( point13 ); array04.add( point14 );
   array04.add( point15 ); array04.add( point16 );
   array04.add( point13 );

   shell02 = LinearRing("Polygon Shell");
   shell02.add( array04 );

   // Part 02: Assemble and print polygon ... 

   polygon02 = Polygon();
   polygon02.setName("Test Polygon 02");
   polygon02.addShell( shell02 );
   polygon02.compile();

   print polygon02.toString();

   // Part 01: Create linear ring for polygon shell ... 

   point17 = Point("pt17", 100.0, 700.0 );
   point18 = Point("pt18", 100.0, 800.0 );
   point19 = Point("pt19", 200.0, 800.0 );
   point20 = Point("pt20", 200.0, 700.0 );

   array05 = ArrayList();
   array05.add( point17 ); array05.add( point18 );
   array05.add( point19 ); array05.add( point20 );
   array05.add( point17 );

   shell03 = LinearRing("Polygon Shell");
   shell03.add( array05 );

   // Part 02: Assemble and print polygon ... 

   polygon03 = Polygon();
   polygon03.setName("Test Polygon 02");
   polygon03.addShell( shell03 );
   polygon03.compile();

   print polygon03.toString();

   print "--- PART 03: Compute polygon01.union ( polygon 02 ) ... ";
   print "--- =================================================================";

   polygon04 = polygon01.union( polygon02 );
   polygon04.setName("Union Polygon 04");
   print polygon04.toString();

   print "--- PART 04: Create multipoint and multipolygon groups ...";
   print "--- ======================================================";

   group01 = MultiPoint();
   group01.setName("Boundary points from Polygon01");
   group01.add( point01 );
   group01.add( point02 );
   group01.add( point03 );
   group01.add( point04 );
   group01.add( point05 );
   group01.add( point06 );
   group01.add( point07 );
   group01.add( point08 );
   group01.add( point09 );
   group01.add( point10 );
   group01.add( point11 );
   group01.add( point12 );
   print group01.toString();

   group02 = MultiPoint();
   group02.setName("Boundary points from Polygon02");
   group02.add( point13 );
   group02.add( point14 );
   group02.add( point15 );
   group02.add( point16 );
   print group02.toString();

   group03 = MultiPoint();
   group03.setName("Boundary points from Polygon03");
   group03.add( point17 );
   group03.add( point18 );
   group03.add( point19 );
   group03.add( point20 );
   print group03.toString();

   group04 = MultiPolygon();
   group04.setName("Group of Polygons");
   group04.add( polygon01 );
   group04.add( polygon02 );
   group04.add( polygon03 );
   print group04.toString();

   group05 = MultiPolygon();
   group05.setName("Union Polygons");
   group05.add( polygon04 );
   print group05.toString();

   print "--- PART 05: Create workspaces and graphical layers ... ";
   print "--- ===================================================";

   layer01 = group01.getWorkspace( "Group 1" );
   layer01.setColor("blue");
   layer02 = group02.getWorkspace( "Group 2" );
   layer02.setColor("red");
   layer03 = group03.getWorkspace( "Group 3" );
   layer03.setColor("green");
   layer04 = group04.getWorkspace( "Group 4" );
   layer04.setColor("orange");
   layer05 = group05.getWorkspace( "Group 5" );
   layer05.setColor("black");

   print "--- PART 06: Build and Display 2D Viewer ... ";
   print "--- ===================================================";

   jfx = Viewer2D();
   jfx.addLayer( "Points: Polygon01",   layer01 );
   jfx.addLayer( "Points: Polygon02",   layer02 );
   jfx.addLayer( "Points: Polygon03",   layer03 );
   jfx.addLayer( "Polygon 01-02-03",    layer04 );
   jfx.addLayer( "Polygon 01-02 Union", layer05 );

   jfx.sleep( 1000 );
   jfx.setSize( 1000, 1000 );
   jfx.setXRange( 0, 1000 );
   jfx.setYRange( 0, 1000 );
   jfx.display();
   jfx.sleep( 1000 );

   print "--- ======================================================== ";
   print "--- Finished !! ... ";
}

generates the textual output:

--- 
--- PART 01: Create basic whistle.model.geometry.Polygon() object ...
--- =================================================================

--- whistle.util.jts.LinearRing() ... 
--- ============================================ 
--- Name = "Polygon Shell"
--- Is closed?    = true ... 
--- Dimension = 1.0
--- JTS Geometry Type = LINEARRING
--- JTS Geometry = LINEARRING (0 0, 500 0, 500 500, 0 500, 0 0)
--- Length    = [ 2000, null]
--- NumberPoints  = 5.0

--- List of Points 
     [java] --- ===================================== 
     [java] ---  1: (x,y) = (      0.00,      0.00) 
     [java] ---  2: (x,y) = (    500.00,      0.00) 
     [java] ---  3: (x,y) = (    500.00,    500.00) 
     [java] ---  4: (x,y) = (      0.00,    500.00) 
     [java] ---  5: (x,y) = (      0.00,      0.00) 
     [java] --- ===================================== 

--- whistle.util.jts.LinearRing() ... 
--- ============================================ 
--- Name = "Hole 01"
--- Is closed?    = true ... 
--- Dimension = 1.0
--- JTS Geometry Type = LINEARRING
--- JTS Geometry = LINEARRING (100 100, 200 100, 200 200, 100 200, 100 100)
--- Length    = [ 400.0, null]
--- NumberPoints  = 5.0

--- List of Points 
--- ===================================== 
---  1: (x,y) = (    100.00,    100.00) 
---  2: (x,y) = (    200.00,    100.00) 
---  3: (x,y) = (    200.00,    200.00) 
---  4: (x,y) = (    100.00,    200.00) 
---  5: (x,y) = (    100.00,    100.00) 
--- ===================================== 

--- whistle.util.jts.LinearRing() ... 
--- ============================================ 
--- Name = "Hole 02"
--- Is closed?    = true ... 
--- Dimension = 1.0
--- JTS Geometry Type = LINEARRING
--- JTS Geometry = LINEARRING (300 200, 400 200, 400 400, 300 400, 300 200)
--- Length    = [ 600.0, null]
--- NumberPoints  = 5.0

--- List of Points 
--- ===================================== 
---  1: (x,y) = (    300.00,    200.00) 
---  2: (x,y) = (    400.00,    200.00) 
---  3: (x,y) = (    400.00,    400.00) 
---  4: (x,y) = (    300.00,    400.00) 
---  5: (x,y) = (    300.00,    200.00) 
--- ===================================== 

--- whistle.util.jts.Polygon() ... 
--- ============================================ 
--- Name = "Test Polygon 01"
--- Dimension    = 2.0
--- JTS Geometry Type = POLYGON
--- JTS Geometry = POLYGON ((0 0, 500 0, 500 500, 0 500, 0 0),
                            (100 100, 200 100, 200 200, 100 200, 100 100),
                            (300 200, 400 200, 400 400, 300 400, 300 200))
--- Number of points = 15 
--- Number of holes  =  2 
--- Area =   220000.000 

--- Shell coordinates ...                 
--- ===================================== 
---  1: (x,y) = (      0.00,      0.00) 
---  2: (x,y) = (    500.00,      0.00) 
---  3: (x,y) = (    500.00,    500.00) 
---  4: (x,y) = (      0.00,    500.00) 
---  5: (x,y) = (      0.00,      0.00) 
--- ===================================== 
--- Perimeter =   2.00e+03 

--- Hole  1 
--- ===================================== 
---  1: (x,y) = (    100.00,    100.00) 
---  2: (x,y) = (    200.00,    100.00) 
---  3: (x,y) = (    200.00,    200.00) 
---  4: (x,y) = (    100.00,    200.00) 
---  5: (x,y) = (    100.00,    100.00) 
--- ===================================== 
--- Perimeter =   4.00e+02 

--- Hole  2 
--- ===================================== 
---  1: (x,y) = (    300.00,    200.00) 
---  2: (x,y) = (    400.00,    200.00) 
---  3: (x,y) = (    400.00,    400.00) 
---  4: (x,y) = (    300.00,    400.00) 
---  5: (x,y) = (    300.00,    200.00) 
--- ===================================== 
--- Perimeter =   6.00e+02 

--- PART 02: Create basic whistle.model.geometry.Polygon() objects ... 
--- ================================================================== 

--- whistle.util.jts.Polygon() ... 
--- ============================================ 
--- Name = "Test Polygon 02"
--- Dimension    = 2.0
--- JTS Geometry Type = POLYGON
--- JTS Geometry = POLYGON ((350 350, 800 350, 800 800, 350 800, 350 350))
--- Number of points =  5 
--- Number of holes  =  0 
--- Area =   202500.000 

--- Shell coordinates ...                 
--- ===================================== 
---  1: (x,y) = (    350.00,    350.00) 
---  2: (x,y) = (    800.00,    350.00) 
---  3: (x,y) = (    800.00,    800.00) 
---  4: (x,y) = (    350.00,    800.00) 
---  5: (x,y) = (    350.00,    350.00) 
--- ===================================== 
--- Perimeter =   1.80e+03 

--- whistle.util.jts.Polygon() ... 
--- ============================================ 
--- Name = "Test Polygon 02"
--- Dimension    = 2.0
--- JTS Geometry Type = POLYGON
--- JTS Geometry = POLYGON ((100 700, 100 800, 200 800, 200 700, 100 700))
--- Number of points =  5 
--- Number of holes  =  0 
--- Area =    10000.000 

--- Shell coordinates ...                 
--- ===================================== 
---  1: (x,y) = (    100.00,    700.00) 
---  2: (x,y) = (    100.00,    800.00) 
---  3: (x,y) = (    200.00,    800.00) 
---  4: (x,y) = (    200.00,    700.00) 
---  5: (x,y) = (    100.00,    700.00) 
--- ===================================== 
--- Perimeter =   4.00e+02 

--- PART 06: Compute polygon01.union ( polygon 02 ) ... 
--- =================================================================

--- whistle.util.jts.Polygon() ... 
--- ============================================ 
--- Name = "Union Polygon 04"
--- Dimension    = 2.0
--- JTS Geometry Type = POLYGON
--- JTS Geometry = POLYGON ((500 350, 500 0, 0 0, 0 500, 350 500, 350 800, 800 800,
                             800 350, 500 350), (100 100, 200 100, 200 200, 100 200, 100 100),
                            (300 200, 400 200, 400 350, 350 350, 350 400, 300 400, 300 200))
--- Number of points = 21 
--- Number of holes  =  2 
--- Area =   402500.000 

--- Shell coordinates ...                 
--- ===================================== 
---  1: (x,y) = (    500.00,    350.00) 
---  2: (x,y) = (    500.00,      0.00) 
---  3: (x,y) = (      0.00,      0.00) 
---  4: (x,y) = (      0.00,    500.00) 
---  5: (x,y) = (    350.00,    500.00) 
---  6: (x,y) = (    350.00,    800.00) 
---  7: (x,y) = (    800.00,    800.00) 
---  8: (x,y) = (    800.00,    350.00) 
---  9: (x,y) = (    500.00,    350.00) 
--- ===================================== 
--- Perimeter =   3.20e+03 

--- Hole  1 
--- ===================================== 
---  1: (x,y) = (    100.00,    100.00) 
---  2: (x,y) = (    200.00,    100.00) 
---  3: (x,y) = (    200.00,    200.00) 
---  4: (x,y) = (    100.00,    200.00) 
---  5: (x,y) = (    100.00,    100.00) 
--- ===================================== 
--- Perimeter =   4.00e+02 

--- Hole  2 
--- ===================================== 
---  1: (x,y) = (    300.00,    200.00) 
---  2: (x,y) = (    400.00,    200.00) 
---  3: (x,y) = (    400.00,    350.00) 
---  4: (x,y) = (    350.00,    350.00) 
---  5: (x,y) = (    350.00,    400.00) 
---  6: (x,y) = (    300.00,    400.00) 
---  7: (x,y) = (    300.00,    200.00) 
--- ===================================== 
--- Perimeter =   6.00e+02 

--- PART 03: Create multipoint and multipolygon groups ...
--- ======================================================

--- whistle.model.geometry.MultiPoint() ... 
--- ============================================ 
--- Name = "Boundary points from Polygon01"
--- No points = 12 
--- ============================================ 
---  1: (x,y) = (    300.00,    400.00) 
---  2: (x,y) = (    100.00,    200.00) 
---  3: (x,y) = (      0.00,    500.00) 
---  4: (x,y) = (    400.00,    400.00) 
---  5: (x,y) = (      0.00,      0.00) 
---  6: (x,y) = (    500.00,      0.00) 
---  7: (x,y) = (    300.00,    200.00) 
---  8: (x,y) = (    500.00,    500.00) 
---  9: (x,y) = (    100.00,    100.00) 
--- 10: (x,y) = (    200.00,    200.00) 
--- 11: (x,y) = (    400.00,    200.00) 
--- 12: (x,y) = (    200.00,    100.00) 
--- ============================================ 

--- whistle.model.geometry.MultiPoint() ... 
--- ============================================ 
--- Name = "Boundary points from Polygon02"
--- No points =  4 
--- ============================================ 
---  1: (x,y) = (    800.00,    800.00) 
---  2: (x,y) = (    800.00,    350.00) 
---  3: (x,y) = (    350.00,    350.00) 
---  4: (x,y) = (    350.00,    800.00) 
--- ============================================ 

--- whistle.model.geometry.MultiPoint() ... 
--- ============================================ 
--- Name = "Boundary points from Polygon03"
--- No points =  4 
--- ============================================ 
---  1: (x,y) = (    100.00,    800.00) 
---  2: (x,y) = (    200.00,    800.00) 
---  3: (x,y) = (    100.00,    700.00) 
---  4: (x,y) = (    200.00,    700.00) 
--- ============================================ 

--- MultiPolygon: Name = "Group of Polygons"
--- ============================================ 
--- No items =  3 
--- ============================================ 
--- Polygon  1: name = "Test Polygon 02" .. 
--- Polygon  2: name = "Test Polygon 01" .. 
--- Polygon  3: name = "Test Polygon 02" .. 
--- ============================================ 

--- MultiPolygon: Name = "Union Polygons"
--- ============================================ 
--- No items =  1 
--- ============================================ 
--- Polygon  1: name = "Union Polygon 04" .. 
--- ============================================ 

--- PART 04: Create workspaces and graphical layers ... 
--- ===================================================

--- PART 05: Build and Display 2D Viewer ... 
--- ===================================================

--- Adding layer: "Points: Polygon01" to  hashmap ... 
--- Adding layer: "Points: Polygon02" to  hashmap ... 
--- Adding layer: "Points: Polygon03" to  hashmap ... 
--- Adding layer: "Polygon 01-02-03" to  hashmap ... 
--- Adding layer: "Polygon 01-02 Union" to  hashmap ... 

--- Starting Viewer2DApplication.run() ... 
--- Entering Viewer2DApplication.init() ... 

--- ======================================================== 
--- Finished !! ... 

Points to note:

  • ....

  • ....

  • ....


Example 3: Set Operations for Drone Operations on a Flight Pathway

With the infrastructure for the definition of a drone and various spatial regions in place, we can now simulate drone traversal of a flight pathway and computation of the drone position with respect to various spatial regions.

The test case setup is as follows:

The drone traverses the pathway in a clockwise direction beginning and ending at coordinate (50,450).

The flight pathway is defined as follows:

import whistle.util.pathway.WayPoint;
import whistle.util.pathway.PathwayDataModel;

program ("Simplified Drone System") {

   print "--- ========================================================= ";
   print "--- PART 08. Create Flight Pathway ...                        ";
   print "--- ========================================================= ";
   print "---";

   // Create waypoints ....

   print "--- Create way points ...";

   wp01 = WayPoint ( "wp01",  50.0, 450.0 );
   wp02 = WayPoint ( "wp02", 400.0, 450.0 );
   wp03 = WayPoint ( "wp03", 400.0, 750.0 );
   wp04 = WayPoint ( "wp04", 700.0, 750.0 );
   wp05 = WayPoint ( "wp05", 750.0, 670.0 );
   wp06 = WayPoint ( "wp06", 750.0, 400.0 );
   wp07 = WayPoint ( "wp07", 750.0, 200.0 );
   wp08 = WayPoint ( "wp08", 650.0, 200.0 );
   wp09 = WayPoint ( "wp09", 650.0,  50.0 );
   wp10 = WayPoint ( "wp10", 300.0,  50.0 );
   wp11 = WayPoint ( "wp11",  50.0,  50.0 );
   wp12 = WayPoint ( "wp12",  50.0, 200.0 );
   wp13 = WayPoint ( "wp13",  50.0, 450.0 );

   // Instantiate flight pathway ....

   print "--- Assemble pathway data model ...";

   pathway01 = PathwayDataModel("Rescue Mission");
   pathway01.add ( wp01 );
   pathway01.add ( wp02 );
   pathway01.add ( wp03 );
   pathway01.add ( wp04 );
   pathway01.add ( wp05 );
   pathway01.add ( wp06 );
   pathway01.add ( wp07 );
   pathway01.add ( wp08 );
   pathway01.add ( wp09 );
   pathway01.add ( wp10 );
   pathway01.add ( wp11 );
   pathway01.add ( wp12 );
   pathway01.add ( wp13 );

   // Indicate that pathway is closed ...

   pathway01.setIsClosed (true);

   // Create composite hierarchy for flight pathway ...

   chpathway01 = pathway01.getWorkspace("Flight Pathway");
}

Points to note:

  • In addition to storing (x,y) coordinates, WayPoint objects keep track or their distance from the pathway origin.

  • The PathwayDataModel stores the complete details of the pathway, including the source and destination way points, and methods for computing the position and orientation of a point at a prescribed distance from the pathway origin.

    As we will soon see, the latter allows a Whistle script to precisely position an object on a pathway.

  • The getWorkspace() method extracts a composite hierarchy model from the pathway data model.

The essential details of controlling the drone movement and evaluation of set operations is as follows:

   print "---";
   print "--- ======================================================";
   print "--- PART 10: TRAVERSE FLIGHT PATHWAY ...";
   print "--- ======================================================";
   print "---";
   print "--- Pathway length = ", pathway01.getLength();
   print "---";

   for (distance = 0.0; distance < pathway01.getLength() ; distance = distance + 10.0 ) {

       point = pathway01.pointAtDistance( distance );
       angle = pathway01.angleAtDistance( distance );

       // Extract (x,y) coordinates ...

       x = point.getX();
       y = point.getY();

       // Check to see if point is inside polygons 01 through 04 ....

       testPoint01 = Point( x, y );
       result01 = polygon01.contains ( testPoint01 );
       result02 = polygon02.contains ( testPoint01 );
       result03 = polygon03.contains ( testPoint01 );
       result04 = polygon04.contains ( testPoint01 );

       // Print distance and results of geometric tests ...

       print "--- ";
       print "--- Distance = ", distance, " (x,y) = (", x, " ,", y, "), angle = ", angle;

       print "    Is (x,y) inside polygon01: result --> ", result01;
       print "    Is (x,y) inside polygon02: result --> ", result02;
       print "    Is (x,y) inside polygon03: result --> ", result03;
       print "    Is (x,y) inside polygon04: result --> ", result04;

       // Reposition drone workspace ...

       jfx.setPosition( "Drone Mission", point.getX(), point.getY(), angle );

       // Refresh graphics then sleep 200 milliseconds ...

       jfx.refresh();
       jfx.sleep( 100 );
   }

Set operation are computed at each step of the simulation. A fragment of the program output is:

---
--- Pathway length = [ 2764, null]
---
--- Distance = [ 0.000, null] (x,y) = ([ 50.00, null] ,[ 450.0, null]), angle = [ 0.000, null]
    Is (x,y) inside polygon01: result --> true
    Is (x,y) inside polygon02: result --> false
    Is (x,y) inside polygon03: result --> false
    Is (x,y) inside polygon04: result --> true

    ... details of simulation output removed ...

--- Distance = [ 430.0, ] (x,y) = ([ 400.0, null] ,[ 530.0, null]), angle = [ 1.571, null]
    Is (x,y) inside polygon01: result --> false
    Is (x,y) inside polygon02: result --> true
    Is (x,y) inside polygon03: result --> false
    Is (x,y) inside polygon04: result --> true

    ... details of simulation output removed ...

--- Distance = [ 1640, ] (x,y) = ([ 650.0, null] ,[ 174.3, null]), angle = [ 4.712, null]
    Is (x,y) inside polygon01: result --> false
    Is (x,y) inside polygon02: result --> false
    Is (x,y) inside polygon03: result --> false
    Is (x,y) inside polygon04: result --> false

    ... details of simulation output removed ...

Points to note:

  • .....

  • .....

  • .....


Example 4: Stitching Fragments of JTS Geometry

    ... TBD ....


Example 5: Stitching Fragments of OSM Geometry

    ... TBD ....


Example 6: Simplifying Geometry with the DouglasPeucker Algorithm

    ... TBD ....

Part 12: Formal Analysis of Graphs and Networks with JGraphT

  • Frequently-Asked Questions
  • Working with JGraphT
  • Importing data from DOT, SDM and OSM
  • Example 1: Simulating Urban Evacuation with All Directed Paths Analysis
  • Example 2: Formal Analysis of Washington DC Metro System

Frequently-Asked Questions

  • What is JGraphT?

    JGraphT is an open-source Java Library of graph theory data structures and algorithms.

    We assume here that the nodes and edges will represent entities in an urban setting,
    and thus, also need to be visualized in terms of their spatial layout.

  • What kinds of graphs can be modeled?

    JGraphT supports modeling of simple graphs, multigraphs, pseudographs, e.g.,

    A simple graph is an undirected, unweighted graph that contains no multiple edges and no graph loops (or self-directed loops). Multigraphs allow for nodes to be connected with multiple edges. A pseudograph is a non-simple graph in which both multiple edges and self-directed graph loops are permitted.

    Graph edges may also be undirected, directed, labeled and weighted, e.g.,

    And all compositions of above graphs. From a software standpoint, listeners can be attached to the
    nodes and edges in a JGraphT model, thereby allowing external entities to track modification events.

  • What kinds of graph analysis algorithms are available?

    Graph algorithms that can be solved in polynomial time:

    • Connected components; Topological sorting; Minimum spanning tree; Shortest path computations;
      Matching; Edge and vertex connectivity.

    Harder graph problems (cannot be solved in polynomial time):

    • Vertex cover; Hamiltonian cycle; Travelling salesman problem.

  • Can I extract a JTS model from OSM? And how would this work?

    The most straight forward way of doing this is with a counterpart of the getWorkspace() method.

    Otherwise, use a visitor ...

  • Where can I find the JGraphT code in Whistle?

    Look under: src/whistle/util/jgrapht/ ...


Working with JGraphT

Simple Graph Analysis

Consider a small graph containing 11 vertices and 13 edges:

The fragment of code:

// =============================================================================
// input-jgrapht01: Create and analyze simple directed graph structure.
//
// Written by: Mark Austin                                             June 2020
// =============================================================================

import whistle.util.jgrapht.SimpleGraph;

program ("Exercise Simple Graph") { 

   print "--- Part 01: Create simple undirected graph ... ";
   print "--- =========================================== ";
   print "---";

   graph01 = SimpleGraph("test");
   graph01.addVertex("A");
   graph01.addVertex("B");
   graph01.addVertex("C");
   graph01.addVertex("D");
   graph01.addVertex("E");
   graph01.addVertex("F");
   graph01.addVertex("G");
   graph01.addVertex("H");
   graph01.addVertex("I");
   graph01.addVertex("J");
   graph01.addVertex("K");
   graph01.addVertex("L");
   graph01.addVertex("M");
   graph01.addVertex("N");

   graph01.addEdge("A", "B");
   graph01.addEdge("B", "C");
   graph01.addEdge("B", "E");
   graph01.addEdge("C", "D");
   graph01.addEdge("D", "E");
   graph01.addEdge("E", "F");
   graph01.addEdge("F", "A");
   graph01.addEdge("E", "G");
   graph01.addEdge("F", "K");
   graph01.addEdge("G", "J");
   graph01.addEdge("J", "I");
   graph01.addEdge("I", "H");
   graph01.addEdge("H", "G");
   graph01.addEdge("G", "I");
   graph01.addEdge("K", "J");
   graph01.addEdge("I", "L");
   graph01.addEdge("J", "M");
   graph01.addEdge("K", "N");

   print graph01.toString();

   print "--- Shortest path computations    ...";
   print "--- ----------------------------- ...";

   graph01.shortestPath( "A", "L" );  // Path: node A --> node L ...
   graph01.shortestPath( "K", "G" );  // Path: node K --> node G ...
}

generates the output:

--- Part 01: Create simple undirected graph ... 
--- =========================================== 

--- Graph(test): ([A, B, C, D, E, F, G, H, I, J, K, L, M, N],
                  [{A,B}, {B,C}, {B,E}, {C,D}, {D,E}, {E,F}, {F,A}, {E,G}, {F,K},
                   {G,J}, {J,I}, {I,H}, {H,G}, {G,I}, {K,J}, {I,L}, {J,M}, {K,N}]) ...
--- No vertices = 14 ...
--- No edges    = 18 ...
---
--- Shortest path computations    ...
--- ----------------------------- ...
---
--- Shortest Path(A --> L) ...
---   Path: [(F : A), (F : K), (K : J), (J : I), (I : L)] ...
---   Cost:   5.00 ...
---
--- Shortest Path(K --> G) ...
---   Path: [(K : J), (G : J)] ...
---   Cost:   2.00 ...

The script systematically assembles the graph, prints its contents, and then computes shortest paths between source and target nodes.


Directed Graph Analysis

Now let's replace the undirected edges with directed edges.

The abbreviated script of code:

// =============================================================================
// input-jgrapht01: Create and analyze simple directed graph structure.
//
// Written by: Mark Austin                                             June 2020
// =============================================================================

import whistle.util.jgrapht.SimpleDirectedGraph;

program ("Exercise Simple Graph") { 

   print "--- Part 02: Create simple directed graph ... ";
   print "--- ========================================= ";
   print "---";

   graph02 = SimpleDirectedGraph("test");
   graph02.addVertex("A");
   graph02.addVertex("B");

   ... vertices removed ...

   graph02.addVertex("M");
   graph02.addVertex("N");

   graph02.addEdge("A", "B");
   graph02.addEdge("B", "C");

   ... edges removed ...

   graph02.addEdge("I", "L");
   graph02.addEdge("J", "M");
   graph02.addEdge("K", "N");

   print graph02.toString();

   print "---";
   print "--- Strongly connected components ... ";
   print "--- ----------------------------- ...";

   graph02.printStronglyConnected();

   print "---";
   print "--- Shortest path computations    ...";
   print "--- ----------------------------- ...";

   graph02.shortestPath( "A", "L" ); // Path: node A --> node L ...
   graph02.shortestPath( "K", "G" ); // Path: node K --> node G ...
   graph02.shortestPath( "I", "A" ); // Path: node I --> node A ...
}

generates the output:

--- Part 02: Create simple directed graph ... 
--- ========================================= 

--- Graph(test): ([A, B, C, D, E, F, G, H, I, J, K, L, M, N],
                  [(A,B), (B,C), (B,E), (C,D), (D,E), (E,F), (F,A), (E,G), (F,K),
                   (G,J), (J,I), (I,H), (H,G), (G,I), (K,J), (I,L), (J,M), (K,N)]) ...
--- No vertices = 14 ...
--- No edges    = 18 ...

---
--- Strongly connected components ...
--- ----------------------------- ...
--- Group 1: ([A, B, C, D, E, F], [(A,B), (B,C), (B,E), (C,D), (D,E), (E,F), (F,A)])
--- Group 2: ([K], [])
--- Group 3: ([N], [])
--- Group 4: ([G, H, I, J], [(G,J), (J,I), (I,H), (H,G), (G,I)])
--- Group 5: ([M], [])
--- Group 6: ([L], [])

---
--- Shortest path computations    ...
--- ----------------------------- ...
---
--- Shortest Path(A --> L) ...
---   Path: [(A : B), (B : E), (E : G), (G : I), (I : L)] ...
---   Cost:   5.00 ...
---
--- Shortest Path(K --> G) ...
---   Path: [(K : J), (J : I), (I : H), (H : G)] ...
---   Cost:   4.00 ...
---
--- Shortest Path(I --> A) ...
---   Path: null ...
---

Points to note:

  • A graph is said to be strongly connected if every node is reachable from every other node. This example has two groups of strongly connected nodes, [A, B, C, D, E, F] and [ E, F, G, H ].

  • Notice how: (1) the directionality of edge E-H extends the shortest pathway from node I to node E, and (2) the directionality of edge D-E makes node A unreachable from node I.


Weighted Directed Graphs

The next logical step is to add weights to the directed edges to represent a cost (could also be a distance or time) for traversal:

The script of code:

   print "---";
   print "--- Part 03: Create simple directed weighted graph ... ";
   print "--- ================================================== ";
   print "---";

   graph03 = SimpleDirectedWeightedGraph("test");

   print graph03.toString();
   graph03.addVertex("A");
   graph03.addVertex("B");

   ... details of vertices removed ...

   graph03.addVertex("L");
   graph03.addVertex("M");
   graph03.addVertex("N");

   graph03.addEdge("A", "B",  "2");
   graph03.addEdge("B", "C",  "2");
   graph03.addEdge("B", "E", "10");
   graph03.addEdge("C", "D",  "2");
   graph03.addEdge("D", "E",  "2");
   graph03.addEdge("E", "F",  "2");
   graph03.addEdge("F", "A",  "2");
   graph03.addEdge("E", "G",  "5");
   graph03.addEdge("F", "K", "15");
   graph03.addEdge("G", "J",  "3");
   graph03.addEdge("J", "I",  "2");
   graph03.addEdge("I", "H",  "2");
   graph03.addEdge("H", "G",  "2");
   graph03.addEdge("G", "I", "10");
   graph03.addEdge("K", "J",  "2");
   graph03.addEdge("I", "L",  "3");
   graph03.addEdge("J", "M",  "4");
   graph03.addEdge("K", "N",  "5");

   print graph03.toString();

   print "---";
   print "--- Shortest path computations    ...";
   print "--- ----------------------------- ...";

   // Shortest path computation: node A --> node L ...

   graph03.shortestPath( "A", "L" );

   // Shortest path computation: node K --> node G ...

   graph03.shortestPath( "K", "G" );

   // Shortest path computation: node I --> node A ...

   graph03.shortestPath( "I", "A" );

generates the output:

---
--- Part 03: Create simple directed weighted graph ... 
--- ================================================== 

--- Graph(test): ([A, B, C, D, E, F, G, H, I, J, K, L, M, N],
                  [(A,B), (B,C), (B,E), (C,D), (D,E), (E,F), (F,A), (E,G), (F,K),
                   (G,J), (J,I), (I,H), (H,G), (G,I), (K,J), (I,L), (J,M), (K,N)]) ...
--- No vertices = 14 ...
--- No edges    = 18 ...

---
--- Shortest path computations    ...
--- ----------------------------- ...
---
--- Shortest Path(A --> L) ...
---   Path: [(A : B), (B : C), (C : D), (D : E), (E : G), (G : J), (J : I), (I : L)] ...
---   Cost:  21.00 ...
---
--- Shortest Path(K --> G) ...
---   Path: [(K : J), (J : I), (I : H), (H : G)] ...
---   Cost:   8.00 ...
---
--- Shortest Path(I --> A) ...
---   Path: null ...
---

Notice: the minimum cost pathway from node A to node L circumvents the two links having weight = 10, i.e.,

Also, in order for the pathway A-B-E-F-K-J-I-L to be competitive, the weight on edge F-K would need to be less than or equal to 4.


All Directed Paths Analysis

All directed paths analysis is a Dijkstra-like algorithm to find all paths between two sets of nodes in a directed graph, with options to search only simple paths and to limit the path length.

For example, the fragment of code:

   print "---";
   print "--- All directed paths computation    ...";
   print "--- --------------------------------- ...";

   graph03.allDirectedPaths( "A", "L" );

computes and ranks all paths connecting nodes A and J. In this particular case, there are four paths, i.e.,

--- All directed paths computation    ...
--- --------------------------------- ...
--- 
--- All Paths(A --> L) ...
--- 
--- No paths = 6 ...
--- Path  1: [(A : B), (B : E), (E : G), (G : I), (I : L)] ...
--- Path  2: [(A : B), (B : E), (E : G), (G : J), (J : I), (I : L)] ...
--- Path  3: [(A : B), (B : E), (E : F), (F : K), (K : J), (J : I), (I : L)] ...
--- Path  4: [(A : B), (B : C), (C : D), (D : E), (E : G), (G : I), (I : L)] ...
--- Path  5: [(A : B), (B : C), (C : D), (D : E), (E : G), (G : J), (J : I), (I : L)] ...
--- Path  6: [(A : B), (B : C), (C : D), (D : E), (E : F), (F : K), (K : J), (J : I), (I : L)] ...
---
--- Sort paths by cost of traversal ...
--- Path  1: 21.00: [(A : B), (B : C), (C : D), (D : E), (E : G), (G : J), (J : I), (I : L)] ...
--- Path  2: 25.00: [(A : B), (B : E), (E : G), (G : J), (J : I), (I : L)] ...
--- Path  3: 26.00: [(A : B), (B : C), (C : D), (D : E), (E : G), (G : I), (I : L)] ...
--- Path  4: 30.00: [(A : B), (B : E), (E : G), (G : I), (I : L)] ...
--- Path  5: 32.00: [(A : B), (B : C), (C : D), (D : E), (E : F), (F : K), (K : J), (J : I), (I : L)] ...
--- Path  6: 36.00: [(A : B), (B : E), (E : F), (F : K), (K : J), (J : I), (I : L)] ...

The allDirectedPaths() method can also handle sets of sources and sets of targets.


Importing data from DOT, SDM and OSM

The Whistle implementation of JGraphT supports data input from DOT, System Data Model (SDM) and Open Street Map (OSM) data files.


Reading a DOT file

DOT is a graph description language. Various programs (e.g., dot) can read dot files and create graphical renderings.

DOT can be used to describe undirected and directed graphs, and subgraphs, with attributes applied to the nodes and edges.

Here, we are interested in using DOT to store graphs in string representations.

    ... detail coming soon ....

Points to note:

  • ....
  • ....


Populating JGraphT models with SDM data

To populate a JGraphtT model with networks of SDM data (e.g., highways, transit lines, etc), we simply visit the SDM, and extract the nodes, ways and attributes in the relevant layers.

The script of code:

    ... detail coming soon ....

shows the SDM counterpart of the small graphs already covered.


Populating JGraphT models with OSM data

To populate a JGraphtT model with networks of OSM data (e.g., highways, transit lines, etc), we simply visit the OSM data model and extract the relevant layers, i.e.,

    ... detail coming soon ....

Notice ...


Example 1: Simulating Urban Evacuation with All Directed Paths Analysis

Suppose that a small group of towns, A, B and C, is subject to an environmental threat and evacuation by road to one of three safe areas (L, M and N) is required. The basic question is as folllows: if I live in town X what area Y should I travel to?

We can study this problem using directed weighted graphs, and all directed paths analysis.

The abbreviated source code:

// ================================================================================
// input-jgrapht02: Use all directed paths to simulate options for urban evacuation
//
// Written by: Mark Austin                                                June 2020
// ================================================================================

import whistle.util.collections.HashSet;
import whistle.util.jgrapht.SimpleDirectedWeightedGraph;

program ("Exercise Simple Graph") {

   print "---";
   print "--- Part 01: Create simple directed weighted graph ... ";
   print "--- ================================================== ";
   print "---";

   graph03 = SimpleDirectedWeightedGraph("Road Network");
   graph03.addVertex("A");
   graph03.addVertex("B");

   ... vertices removed ...

   graph03.addVertex("M");
   graph03.addVertex("N");

   graph03.addEdge("A", "B",  "2");
   graph03.addEdge("B", "C",  "2");
   graph03.addEdge("B", "E", "10");
   graph03.addEdge("C", "D",  "2");
   graph03.addEdge("D", "E",  "2");
   graph03.addEdge("E", "F",  "2");
   graph03.addEdge("F", "A",  "2");
   graph03.addEdge("E", "G",  "5");
   graph03.addEdge("F", "K", "15");
   graph03.addEdge("G", "J",  "3");
   graph03.addEdge("J", "I",  "2");
   graph03.addEdge("I", "H",  "2");
   graph03.addEdge("H", "G",  "2");
   graph03.addEdge("G", "I", "10");
   graph03.addEdge("K", "J",  "2");
   graph03.addEdge("I", "L",  "3");
   graph03.addEdge("J", "M",  "4");
   graph03.addEdge("K", "N",  "5");

   print graph03.toString();

   print "---";
   print "--- Part 02: Sets of source and target vertices ...";
   print "--- =========================================== ...";

   sources01 = HashSet("Rural Towns");
   sources01.add( "A" );
   sources01.add( "B" );
   sources01.add( "C" );

   targets01 = HashSet("Safe Areas");
   targets01.add( "L" );
   targets01.add( "M" );
   targets01.add( "N" );

   print "---";
   print "--- Part 03: All directed paths computation ...";
   print "--- ======================================= ...";

   graph03.allDirectedPaths( sources01, targets01 );
}

generates the output:

---
--- Part 01: Create simple directed weighted graph ...
--- ==================================================

--- Graph(Road Network): ([A, B, C, D, E, F, G, H, I, J, K, L, M, N],
                          [(A,B), (B,C), (B,E), (C,D), (D,E), (E,F), (F,A), (E,G), (F,K),
                           (G,J), (J,I), (I,H), (H,G), (G,I), (K,J), (I,L), (J,M), (K,N)]) ...
--- No vertices = 14 ...
--- No edges    = 18 ...

--- Part 02: Sets of source and target vertices ...
--- ==================================================
---
--- Part 03: All directed paths computation    ...
--- ==================================================
--- 
--- HashSet (cloned): [A, B, C] ...
--- HashSet (cloned): [L, M, N] ...
--- No paths = 30 ...
--- Path  1: [(A : B), (B : E), (E : G), (G : I), (I : L)] ...
--- Path  2: [(A : B), (B : E), (E : G), (G : J), (J : M)] ...
--- Path  3: [(A : B), (B : E), (E : G), (G : J), (J : I), (I : L)] ...
--- Path  4: [(A : B), (B : E), (E : F), (F : K), (K : N)] ...
--- Path  5: [(A : B), (B : E), (E : F), (F : K), (K : J), (J : M)] ...
--- Path  6: [(A : B), (B : E), (E : F), (F : K), (K : J), (J : I), (I : L)] ...
--- Path  7: [(A : B), (B : C), (C : D), (D : E), (E : G), (G : I), (I : L)] ...
--- Path  8: [(A : B), (B : C), (C : D), (D : E), (E : G), (G : J), (J : M)] ...
--- Path  9: [(A : B), (B : C), (C : D), (D : E), (E : G), (G : J), (J : I), (I : L)] ...
--- Path 10: [(A : B), (B : C), (C : D), (D : E), (E : F), (F : K), (K : N)] ...
--- Path 11: [(A : B), (B : C), (C : D), (D : E), (E : F), (F : K), (K : J), (J : M)] ...
--- Path 12: [(A : B), (B : C), (C : D), (D : E), (E : F), (F : K), (K : J), (J : I), (I : L)] ...
--- Path 13: [(B : C), (C : D), (D : E), (E : G), (G : I), (I : L)] ...
--- Path 14: [(B : C), (C : D), (D : E), (E : G), (G : J), (J : M)] ...
--- Path 15: [(B : C), (C : D), (D : E), (E : G), (G : J), (J : I), (I : L)] ...
--- Path 16: [(B : C), (C : D), (D : E), (E : F), (F : K), (K : N)] ...
--- Path 17: [(B : C), (C : D), (D : E), (E : F), (F : K), (K : J), (J : M)] ...
--- Path 18: [(B : C), (C : D), (D : E), (E : F), (F : K), (K : J), (J : I), (I : L)] ...
--- Path 19: [(B : E), (E : G), (G : I), (I : L)] ...
--- Path 20: [(B : E), (E : G), (G : J), (J : M)] ...
--- Path 21: [(B : E), (E : G), (G : J), (J : I), (I : L)] ...
--- Path 22: [(B : E), (E : F), (F : K), (K : N)] ...
--- Path 23: [(B : E), (E : F), (F : K), (K : J), (J : M)] ...
--- Path 24: [(B : E), (E : F), (F : K), (K : J), (J : I), (I : L)] ...
--- Path 25: [(C : D), (D : E), (E : G), (G : I), (I : L)] ...
--- Path 26: [(C : D), (D : E), (E : G), (G : J), (J : M)] ...
--- Path 27: [(C : D), (D : E), (E : G), (G : J), (J : I), (I : L)] ...
--- Path 28: [(C : D), (D : E), (E : F), (F : K), (K : N)] ...
--- Path 29: [(C : D), (D : E), (E : F), (F : K), (K : J), (J : M)] ...
--- Path 30: [(C : D), (D : E), (E : F), (F : K), (K : J), (J : I), (I : L)] ...
---
--- Sort paths by cost of traversal ...
--- Path  1: 16.00: [(C : D), (D : E), (E : G), (G : J), (J : M)] ...
--- Path  2: 17.00: [(C : D), (D : E), (E : G), (G : J), (J : I), (I : L)] ...
--- Path  3: 18.00: [(B : C), (C : D), (D : E), (E : G), (G : J), (J : M)] ...
--- Path  4: 19.00: [(B : C), (C : D), (D : E), (E : G), (G : J), (J : I), (I : L)] ...
--- Path  5: 20.00: [(A : B), (B : C), (C : D), (D : E), (E : G), (G : J), (J : M)] ...
--- Path  6: 21.00: [(A : B), (B : C), (C : D), (D : E), (E : G), (G : J), (J : I), (I : L)] ...
--- Path  7: 22.00: [(B : E), (E : G), (G : J), (J : M)] ...
--- Path  8: 22.00: [(C : D), (D : E), (E : G), (G : I), (I : L)] ...
--- Path  9: 23.00: [(B : E), (E : G), (G : J), (J : I), (I : L)] ...
--- Path 10: 24.00: [(A : B), (B : E), (E : G), (G : J), (J : M)] ...
--- Path 11: 24.00: [(B : C), (C : D), (D : E), (E : G), (G : I), (I : L)] ...
--- Path 12: 25.00: [(A : B), (B : E), (E : G), (G : J), (J : I), (I : L)] ...
--- Path 13: 26.00: [(A : B), (B : C), (C : D), (D : E), (E : G), (G : I), (I : L)] ...
--- Path 14: 26.00: [(C : D), (D : E), (E : F), (F : K), (K : N)] ...
--- Path 15: 27.00: [(C : D), (D : E), (E : F), (F : K), (K : J), (J : M)] ...
--- Path 16: 28.00: [(B : C), (C : D), (D : E), (E : F), (F : K), (K : N)] ...
--- Path 17: 28.00: [(B : E), (E : G), (G : I), (I : L)] ...
--- Path 18: 28.00: [(C : D), (D : E), (E : F), (F : K), (K : J), (J : I), (I : L)] ...
--- Path 19: 29.00: [(B : C), (C : D), (D : E), (E : F), (F : K), (K : J), (J : M)] ...
--- Path 20: 30.00: [(A : B), (B : E), (E : G), (G : I), (I : L)] ...
--- Path 21: 30.00: [(A : B), (B : C), (C : D), (D : E), (E : F), (F : K), (K : N)] ...
--- Path 22: 30.00: [(B : C), (C : D), (D : E), (E : F), (F : K), (K : J), (J : I), (I : L)] ...
--- Path 23: 31.00: [(A : B), (B : C), (C : D), (D : E), (E : F), (F : K), (K : J), (J : M)] ...
--- Path 24: 32.00: [(A : B), (B : C), (C : D), (D : E), (E : F), (F : K), (K : J), (J : I), (I : L)] ...
--- Path 25: 32.00: [(B : E), (E : F), (F : K), (K : N)] ...
--- Path 26: 33.00: [(B : E), (E : F), (F : K), (K : J), (J : M)] ...
--- Path 27: 34.00: [(A : B), (B : E), (E : F), (F : K), (K : N)] ...
--- Path 28: 34.00: [(B : E), (E : F), (F : K), (K : J), (J : I), (I : L)] ...
--- Path 29: 35.00: [(A : B), (B : E), (E : F), (F : K), (K : J), (J : M)] ...
--- Path 30: 36.00: [(A : B), (B : E), (E : F), (F : K), (K : J), (J : I), (I : L)] ...

Analysis summary:

  • If you are in Town "A", then go to safe area "M" (Minimum pathway cost 20).
  • If you are in Town "B", then go to safe area "M" (Minimum pathway cost 18).
  • If you are in Town "C", then go to safe area "M" (Minimum pathway cost 16).

This analysis provides a set of reference trajectories and recommendations, and can be thought of as static mission pathway planning.

Trajectory Adjustment in a Dynamic Environment

Now suppose that an environmental event (e.g., flooding) occurs in the vicinity of road segment E-G, causing delays, and increasing the edge weight for E-G from 5 to 20.

The revised pathways and cost estimates are as follows:

  • If you are in Town "A", then go to safe area "N" (Minimum pathway cost 30).
  • If you are in Town "B", then go to safe area "N" (Minimum pathway cost 28).
  • If you are in Town "C", then go to safe area "N" (Minimum pathway cost 26).

Scaling thing up, the edge weight values will be affected by a multitude of factors and change as a function of time.
One can imagine using machine learning algorithms to identify patterns in observational data, and then make predictions for the most likely (or approriate) edge weight value.


Example 2: Formal Analysis of Washington DC Metro System

This example demonstrates how the system data model can be used for the specification of a simplified representation of the Washington DC Metro System.

The specification is divided into input files:

  • Metro stations are treated as components in the model.
  • The metro system is treated as a graph (or network) connecting metro stations.

Additional challenges:

  • We are using a micro-version of the car model (a compound model) as a parking icon. Need to find a way of associating this compound model with a boolean variable (?).

Part 1: Specification of Metro Stations

Insert material ...

    ... detail coming soon ....

Part 2: Graph-Level Specification of Metro System Network Structure

Insert material ...

    ... detail coming soon ....

Part 3: Geometry-Level Specification of Metro System Network Structure

Insert material ...

    ... detail coming soon ....

Part 13: Semantic Modeling and Event-based Reasoning with Apache Jena

  • Frequently-Asked Questions
  • Working with Apache Jena
  • Example 1: Temporal Ontologies and Time-Based Reasoning
  • Example 2: Physical Quantity Ontologies and Reasoning
  • Example 3: Spatial Reasoning with Sensors and Rooms
  • Example 4: Semantic Analysis and Control of Drone Operations

Frequently-Asked Questions

  • What is Apache Jena?

  • Where can I find the Jena code in Whistle?


Working with Apache Jena


Loading Ontologies into Jena

    ... detail coming soon ....


Adding Individuals to a Semantic Graph

The fragment of code:

    ... detail coming soon ....

creates three times instances. Initially, properties are as follows:



Generic Rule Engines and Jena Rules

    ... detail coming soon ....


Example 1: Temporal Ontologies and Time-based Reasoning

Semantic models of time and time-based reasoning are based on ontological models of time, and an algebra of binary relations of intervals developed by Allen in the mid- 1980s.

Simplified Time Ontology and Allen's Temporal Algebra

Consider the simplified time ontology:

Key points to note:

  • Temporal Entity: A temporal entity has data properties: hasBeginning and hasEnd that link to temporal instants defining its boundaries.
    Temporal entities have a duration.
  • Instant: Instants of time are point-like in the sense that they have no interior points.
  • Interval: Intervals are things with temporal extent.
  • Proper Interval: A proper interval has well defined start and end times.
  • Open Interval: Open intervals are unbounded in their duration and are used to talk about things that happen in the past or in the future.

The time ontology is simplified in sense that time zones, calendars, multi-lingual support, etc, are abstracted from consideration.

Relationships between two intervals of time are described by Allen's temporal algebra, and seven elementary relations:

Each elementary relation has an inverse operation. For example, the inverse (not shown) of intBefore(i,j) is intAfter (j,i).


Working with the Simplified Time Ontology

The script of code:

    osm.model = ModelFactory.createOntologyModel( OntModelSpec.OWL_DL_MEM );
    osm.model.read( "file:ontology/umd-time-ontology2014.owl" );
    osm.printNamedClasses( "Time Ontology Model" );

systematically loads the time ontology into Jena,

Named Classes: Time Ontology Model ...
=============================================================

Named Class(1): ProperTimeInterval
--- Full Name: http://www.isi.edu/~pan/damltime/time-entry.owl#ProperTimeInterval
--- Superclass: TemporalEntity ... 
--- Disjoint with: OpenTimeInterval ... 
--- Disjoint with: Instant ... 
--- Data Property Name: beginsAt ... 
---             Domain: TemporalEntity ... 
--- Data Property Name: endsAt ... 
---             Domain: TemporalEntity ... 
--- Data Property Name: hasTimeDuration ... 
---             Domain: TemporalEntity ... 
--- Object Property: intervalBefore ... 
---           Range: ProperTimeInterval ... 
--- Object Property: intervalAfter ... 
---           Range: ProperTimeInterval ... 
--- Object Property: intervalFinishedBy ... 
---           Range: ProperTimeInterval ... 
--- Object Property: intervalDisjoint ... 
---           Range: ProperTimeInterval ... 
--- Object Property: intervalMeets ... 
---           Range: ProperTimeInterval ... 
--- Object Property: intervalStartedBy ... 
---           Range: ProperTimeInterval ... 
--- Object Property: intervalEquals ... 
---           Range: ProperTimeInterval ... 
--- Object Property: intervalMetBy ... 
---           Range: ProperTimeInterval ... 
--- Object Property: after ... 
---           Range: TemporalEntity ... 
--- Object Property: intervalDuring ... 
---           Range: ProperTimeInterval ... 
--- Object Property: intervalStarts ... 
---           Range: ProperTimeInterval ... 
--- Object Property: intervalContains ... 
---           Range: ProperTimeInterval ... 
--- Object Property: intervalOverlaps ... 
---           Range: ProperTimeInterval ... 
--- Object Property: intervalOverlappedBy ... 
---           Range: ProperTimeInterval ... 
--- Object Property: intervalFinishes ... 
---           Range: ProperTimeInterval ... 
--- Object Property: before ... 
---           Range: TemporalEntity ... 

Named Class(2): Instant
--- Full Name: http://www.isi.edu/~pan/damltime/time-entry.owl#Instant
--- Superclass: TemporalEntity ... 
--- Disjoint with: OpenTimeInterval ... 
--- Disjoint with: ProperTimeInterval ... 
--- Data Property Name: hasTime ... 
---             Domain: Instant ... 
--- Data Property Name: beginsAt ... 
---             Domain: TemporalEntity ... 
--- Data Property Name: endsAt ... 
---             Domain: TemporalEntity ... 
--- Data Property Name: hasTimeDuration ... 
---             Domain: TemporalEntity ... 
--- Object Property: after ... 
---           Range: TemporalEntity ... 
--- Object Property: before ... 
---           Range: TemporalEntity ... 

Named Class(3): OpenTimeInterval
--- Full Name: http://www.isi.edu/~pan/damltime/time-entry.owl#OpenTimeInterval
--- Superclass: TemporalEntity ... 
--- Disjoint with: ProperTimeInterval ... 
--- Disjoint with: Instant ... 
--- Data Property Name: beginsAt ... 
---             Domain: TemporalEntity ... 
--- Data Property Name: endsAt ... 
---             Domain: TemporalEntity ... 
--- Data Property Name: hasTimeDuration ... 
---             Domain: TemporalEntity ... 
--- Data Property Name: hasTimeAnchor ... 
---             Domain: OpenTimeInterval ... 
--- Data Property Name: hasTimeDirection ... 
---             Domain: OpenTimeInterval ... 
--- Object Property: after ... 
---           Range: TemporalEntity ... 
--- Object Property: before ... 
---           Range: TemporalEntity ... 

Named Class(4): TemporalEntity
--- Full Name: http://www.isi.edu/~pan/damltime/time-entry.owl#TemporalEntity
--- Superclass: Thing ... 
--- Subclass: Instant ... 
--- Subclass: ProperTimeInterval ... 
--- Subclass: OpenTimeInterval ... 
--- Data Property Name: beginsAt ... 
---             Domain: TemporalEntity ... 
--- Data Property Name: endsAt ... 
---             Domain: TemporalEntity ... 
--- Data Property Name: hasTimeDuration ... 
---             Domain: TemporalEntity ... 
--- Object Property: after ... 
---           Range: TemporalEntity ... 
--- Object Property: before ... 
---           Range: TemporalEntity ... 

=============================================================

Points to note:


Adding Individuals (Instants and Intervals of Time) to the Semantic Model

The fragment of code:

   osm.addTimeInstant( "A", "2020-06-30T09:00:00Z" );
   osm.addTimeInstant( "B", "2020-06-30T09:10:00Z" );
   osm.addTimeInstant( "C", "2020-06-30T09:20:00Z" );

adds time instances A, B and C to the semantic model, and then prints details of their representation in Jena, i.e.,

Statements: Time Instant A ...
=============================================================
Statement[   1]
   Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#A 
   Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#hasTime 
   Object   : "2020-06-30T09:00:00Z^^http://www.w3.org/2001/XMLSchema#dateTime" 
Statement[   2]
   Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#A 
   Predicate: http://www.w3.org/1999/02/22-rdf-syntax-ns#type 
   Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#Instant 
=============================================================

Statements: Time Instant B ...
=============================================================
Statement[   1]
   Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#B 
   Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#hasTime 
   Object   : "2020-06-30T09:10:00Z^^http://www.w3.org/2001/XMLSchema#dateTime" 
Statement[   2]
   Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#B 
   Predicate: http://www.w3.org/1999/02/22-rdf-syntax-ns#type 
   Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#Instant 
=============================================================

Statements: Time Instant C ...
=============================================================
Statement[   1]
   Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#C 
   Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#hasTime 
   Object   : "2020-06-30T09:20:00Z^^http://www.w3.org/2001/XMLSchema#dateTime" 
Statement[   2]
   Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#C 
   Predicate: http://www.w3.org/1999/02/22-rdf-syntax-ns#type 
   Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#Instant 
=============================================================


Exercising Relations among Instants of Time

The fragment:

// ==================================================
// Rules for time instances ...
// ==================================================

// Instant 01: Definition of an Instant from TemporalEntity ...

[ Instant01: (?x rdf:type te:TemporalEntity) (?x te:hasTime ?t)
           noValue(?x rdf:type te:Instant) -> (?x rdf:type te:Instant) ]  

// Instant 02: Deduction of happensBefore time instant ...

[ Instant02: (?x rdf:type te:Instant) (?y rdf:type te:Instant) notEqual(?x, ?y) (?x te:hasTime ?t1)
             (?y te:hasTime ?t2) lessThan(?t1,?t2) -> (?x te:before ?y) ]

// Instant 03 : Deduction of happensAfter time instant ...

[ Instant03: (?x rdf:type te:Instant) (?y rdf:type te:Instant) notEqual(?x, ?y) (?x te:hasTime ?t1)
             (?y te:hasTime ?t2) greaterThan(?t2,?t1) -> (?y te:after ?x) ]

shows Jena Rules to identify relationships among instants of time.

The modified semantic graph is as follows:

--- 
Statements: Time Instant A ...
=============================================================
Statement[   1]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#A 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#before 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#B 
Statement[   2]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#A 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#before 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#C 
Statement[   3]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#A 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#hasTime 
  Object   : "2020-06-30T09:00:00Z^^http://www.w3.org/2001/XMLSchema#dateTime" 
Statement[   4]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#A 
  Predicate: http://www.w3.org/1999/02/22-rdf-syntax-ns#type 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#Instant 
=============================================================
     [java] 
     [java] Statements: Time Instant B ...
=============================================================
Statement[   1]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#B 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#after 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#A 
Statement[   2]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#B 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#before 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#C 
Statement[   3]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#B 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#hasTime 
  Object   : "2020-06-30T09:10:00Z^^http://www.w3.org/2001/XMLSchema#dateTime" 
Statement[   4]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#B 
  Predicate: http://www.w3.org/1999/02/22-rdf-syntax-ns#type 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#Instant 
=============================================================


Exercising Allen's Temporal Algebra ....

Now let's define three time intervals:

   osm.addTimeInterval( "D", "2020-06-30T09:18:00Z", "2020-06-30T09:25:00Z" );
   osm.addTimeInterval( "E", "2020-06-30T09:18:00Z", "2020-06-30T09:45:00Z" );
   osm.addTimeInterval( "F", "2020-06-30T09:50:00Z", "2020-06-30T10:15:00Z" );

Interval D covers [9.18am -- 9.25 am]; Interval E covers [9.18am -- 9.45 am]; and interval F covers [9.50am -- 10.15 am].

Notice that intervals "D" and "E" begin at the same time, and that interval "F" commences after intervals "D" and "E" have finished.

The set of Jena rules (abbreviated):

@prefix te: .
@prefix rdf: .

// Rules for Proper Time Intervals ...

[ Proper01: (?x rdf:type te:ProperTimeInterval) (?x te:beginsAt ?t1) (?x te:endsAt ?t2)
             getDurationInterval(?t1,?t2,?d) noValue(?x te:hasDuration ?d) -> (?x te:hasDuration ?d) ]

// Allen 01: Identify intervalAfter relationship between time intervals ...

[ Allen01: (?x rdf:type te:ProperTimeInterval) (?y rdf:type te:ProperTimeInterval) notEqual(?x, ?y) 
           (?x te:endsAt ?t1) (?y te:beginsAt ?t2) lessThan(?t1,?t2) -> (?y te:intervalAfter ?x) ]

// Allen 02: Identify intervalBefore relationship between time intervals ...

[ Allen02: (?x rdf:type te:ProperTimeInterval) (?y rdf:type te:ProperTimeInterval) notEqual(?x, ?y) 
           (?x te:endsAt ?t1) (?y te:beginsAt ?t2) lessThan(?t1,?t2) -> (?x te:intervalBefore ?y) ]

// Allen 03: Identify intervalContains relationship between time intervals ...

[ Allen03: (?x rdf:type te:ProperTimeInterval) (?y rdf:type te:ProperTimeInterval) notEqual(?x, ?y) 
           (?x te:beginsAt ?t1) (?x te:endsAt ?t2) (?y te:beginsAt ?t3) (?y te:endsAt ?t4)
           greaterThan(?t3,?t1) lessThan(?t4,?t2) -> (?x te:intervalContains ?y) ]

... rules removed ...

// Allen 10: Deduction of intervalOverlaps time intervals ...

[ Allen10: (?x rdf:type te:ProperTimeInterval) (?y rdf:type te:ProperTimeInterval) notEqual(?x, ?y) 
           (?x te:beginsAt ?t1) (?x te:endsAt ?t2) (?y te:beginsAt ?t3) (?y te:endsAt ?t4)
           lessThan(?t1,?t3) greaterThan(?t2,?t3)
           lessThan(?t2,?t4) -> (?x te:intervalOverlaps ?y) (?y te:intervalOverlapedBy ?x) ]

transforms the interval descriptions into:

Statements: Time Interval D ...
=============================================================
Statement[   1]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#D 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#hasDuration 
  Object   : "PT7M^^http://www.w3.org/2001/XMLSchema#duration" 
Statement[   2]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#D 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#intervalStarts 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#E 
Statement[   3]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#D 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#intervalBefore 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#F 
Statement[   4]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#D 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#intervalDisjoint 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#F 
Statement[   5]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#D 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#endsAt 
  Object   : "2020-06-30T09:25:00Z^^http://www.w3.org/2001/XMLSchema#dateTime" 
Statement[   6]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#D 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#beginsAt 
  Object   : "2020-06-30T09:18:00Z^^http://www.w3.org/2001/XMLSchema#dateTime" 
Statement[   7]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#D 
  Predicate: http://www.w3.org/1999/02/22-rdf-syntax-ns#type 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#ProperTimeInterval 
=============================================================

Statements: Time Interval E ...
=============================================================
Statement[   1]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#E 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#hasDuration 
  Object   : "PT27M^^http://www.w3.org/2001/XMLSchema#duration" 
Statement[   2]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#E 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#intervalStarts 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#D 
Statement[   3]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#E 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#intervalBefore 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#F 
Statement[   4]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#E 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#intervalDisjoint 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#F 
Statement[   5]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#E 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#endsAt 
  Object   : "2020-06-30T09:45:00Z^^http://www.w3.org/2001/XMLSchema#dateTime" 
Statement[   6]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#E 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#beginsAt 
  Object   : "2020-06-30T09:18:00Z^^http://www.w3.org/2001/XMLSchema#dateTime" 
Statement[   7]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#E 
  Predicate: http://www.w3.org/1999/02/22-rdf-syntax-ns#type 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#ProperTimeInterval 
=============================================================

Statements: Time Interval F ...
=============================================================
Statement[   1]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#F 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#hasDuration 
  Object   : "PT25M^^http://www.w3.org/2001/XMLSchema#duration" 
Statement[   2]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#F 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#intervalAfter 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#E 
Statement[   3]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#F 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#intervalAfter 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#D 
Statement[   4]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#F 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#intervalDisjoint 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#D 
Statement[   5]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#F 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#intervalDisjoint 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#E 
Statement[   6]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#F 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#endsAt 
  Object   : "2020-06-30T10:15:00Z^^http://www.w3.org/2001/XMLSchema#dateTime" 
Statement[   7]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#F 
  Predicate: http://www.isi.edu/~pan/damltime/time-entry.owl#beginsAt 
  Object   : "2020-06-30T09:50:00Z^^http://www.w3.org/2001/XMLSchema#dateTime" 
Statement[   8]
  Subject  : http://www.isi.edu/~pan/damltime/time-entry.owl#F 
  Predicate: http://www.w3.org/1999/02/22-rdf-syntax-ns#type 
  Object   : http://www.isi.edu/~pan/damltime/time-entry.owl#ProperTimeInterval 
=============================================================

To summarize:

  • Time intervals "D", "E", and "F" have duration 7 minutes, 27 minutes, and 25 minutes, respectively.

  • Time intervals "D" and "E" begin at the same time and are disjoint (they do not overlap in any way) from interval "F".

  • Time interval "F" occurs after intervals "D" and "E".

The rules can be easily tweaked to test containment of a time instant within a time interval.


Example 2: Physical Quantity Ontologies and Physical-Quantity-based Reasoning

Basic Requirements

  • The purpose of an ontology for units of measure is to facilitate measurement of physical objects and associated processes.

  • Any connection to physical objects requires ontologies for various kinds of units as well as their relationship to the phyiscal objects being measured.

  • Present-day ontologies for physical units are expressed in OWL (e.g., QUDT, OM), and have focussed on exhaustive taxonomies of quantities.

  • Ontologies for units of measure that extend upper ontologies do not contain explicit axioms for specifying how units may be combined and manipulated.


RELATED WORK

Option 1: NASA QUDT (Quantity Unit Dimension Type) Ontology

Motivation and Background:

  • Provides a formal way of specifying units, thereby avoiding tacit conversions that are prone to misinterpretation.

  • Distinguish variants of a given unit (e.g, various flavors of thermal units).

  • Distinguish between units of different types that are commonly referred to with the same name (e.g., second: unit of time, second: unit of plane angle).

  • Provides support for explicit conversion of compatible units. Avoids unit conversion errors.

QUDT supports reasoning over quantity kinds as well as units.

QUDT ontologies are now open sourced (2019--2020).

Core Design Pattern:

Here:

  • Quantity: A quantity instance has a property qudt:value, which can be assigned a value, and a pointer to an instance of qudt:Unit.

  • Quantity Kind: Quantity kinds are sets of things we might want to measure.
    They are defined in terms of dimension vectors (e.g., length (m), time (sec), amount of substance (mole), temperature (kelvin K), luminous intensity (candela), mass (kg).

  • Unit: Abbreviated example:
        unit: MilliM
            rdf:type qudt:Unit;
            rdfs:label "Millimetre"@en;
            rdfs:label "Millimeter"@en-us;
            qudt:conversionMultiplier 0.001;
            qudt:conversionOffset       0.0;
            qudt:hasDimensionVector qkdv:A0E0L1I0M0T0D0;
            qudt:hasPlainTextDescription "The millimetre";
            qudt:hasQuantityKind quantitykind:Length;
            rdfs:isDefinedBy ;
    

    Every unit is associated with a quantity kind. Usually one, but could be more than one.
    For example, the units "N.m" can map to work "J" and "Torque" ...

  • Quantity Kind: Each quantity kind can be measured in multiple ways.
    For example, a "length" can be measured in "inch" and "ft", "cm", "mm" amd "m" ...

  • Quantity Kind Dimension Vector: All physical measurements can be described in terms of 7 basic dimensions and associated units.

    • Amount of substance -- mole (mole).
    • Electric current -- ampere (A).
    • Length - meter (m).
    • Luminous Intensity -- candela (cd).
    • Mass -- kilogram (kg).
    • Temperature -- kelvin (K).
    • Time Duration -- second (sec).

    Dimension vectors are comprised of the seven basic dimensions and associated exponents.

    Dimensions and dimensionless values are indicated with single letter codes, which are ordered: A, E, L, I, M, H, T, and D.

    The string encoding for force (mass*accn = kg*m/sec^2) is: "qkdv:A0E0L10I0M10HT-2D0" ...

    Unit conversions can be applied to sets of units having the same quantity kind dimension vector.

    QUDT handles unit conversions via SPARQL queries, but we could use Jena Rules instead ...

    QUDT provides special features to deal with kinds of dimensionless quantities (e.g., pressure ratios) and quantities with multiple units (e.g., temperature --> K, C and F). Dimensionless quantities are often the ratio of two quantities that not dimensionless (e.g, angular radians).

  • System of Quantity Kinds: ...

  • System of Units: ...

Framework allows for: unit conversion, dimensional analysis, and identification/validation checking for commesurate units.

System Statistics:

  • Number of units = 1,522. No quantity kinds = 746. No dimension vectors = 148.

  • Cross references to codes from S.I., ISO/IEC 800000, IEC, UCUM, UN/ECE.


Option 2: QUDV (Quantity, Unit, Dimension, Value) (2009 timeframe)

Key points:

  • Based on ISO standard ...
  • ....

Key Concepts:

Unit Concepts:

Quantity Kind Concepts:


Option 3: JSR 363 Units of Measurement (2014)

Motivation and Background:

  • Software developers who need to work with physical quantities need to handle measurements in their programs.

  • Lack of models of physical measurements leads to code that is ambiguous in the use of units, which leads to calculation errors.

  • Provide support for symbolic parsing (including prefixes) and runtime handling and printing of units, quantities and dimensions.

  • Intended use: transmission of physical quantities over network links.
    Very important for implementation of IoT.

Packages:

  • javax.measure: Core API interfaces for Dimension, Quantity, Unit ...

  • javax.measure.format: Interfaces for quantity/unit formatting and parsing.

  • javax.measure.quantity: Interfaces for quantity types (equivalent to quantity kinds in QUDT model).

  • javax.measure.spi: Service provider interface (implementation discovery).

Class Diagram:

Here:

  • Systems of Units: SI (Metric), US or Imperial System.

  • Base Unit: Mutually independent units (e.g., L, M, t, T), meaning that they cannot be derived from any other units (e.g., kg, s).

  • Derived Unit: A unit formed as the product of powers of the base units. Some derived units have special names.

  • Product Unit: Product units comprise rational powers of existing units (e.g., m^2/s^2).

  • Alternate Unit: Alternate units are used in expressions to distinguish between quantities of a different nature, but of the same dimension).

  • Transform Unit: Are derived from other units through a converter (e.g., ft --> m, K --> C).

  • Compound Unit: Are mult-radix units used for formatting purpose (e.g., km/hr).

Also:

  • Quantity: An interface to any type of quantitative property or thing.
    Mass, time, distance, heat are examples of quantitative properties.

Examples: ....

 
public interface Torque extends Quantity {
    public final static Unit UNIT = new ProductUnit(SI.NEWTON.times(SI.METRE));
}

public final class SI extends SystemOfUnits {
    private static HashSet> UNITS = new HashSet>();

    // Base Units ...

    public static final BaseUnit AMPERE = si(new BaseUnit( "A"));
    public static final BaseUnit CANDELA = si(new BaseUnit("cd"));
    public static final BaseUnit KELVIN = si(new BaseUnit("K"));
    public static final BaseUnit KILOGRAM = si(new BaseUnit("kg"));
    public static final BaseUnit  METRE = si(new BaseUnit("m"));

    public static final BaseUnit MOLE = si(new BaseUnit("mol"));
    public static final BaseUnit SECOND = si(new BaseUnit("s"));

    // Alternate Units ...

    public static final Unit GRAM = KILOGRAM.divide(1000);
    public static final AlternateUnit RADIAN = si(new AlternateUnit("rad", Unit.ONE));
    public static final AlternateUnit STERADIAN = si(new AlternateUnit("sr", Unit.ONE));
    public static final AlternateUnit BIT = si(new AlternateUnit("bit", Unit.ONE));

    public static final AlternateUnit HERTZ = si(new AlternateUnit("Hz", Unit.ONE.divide(SECOND)));
    public static final AlternateUnit    NEWTON = si(new AlternateUnit("N", METRE.times(KILOGRAM).divide(SECOND.pow(2))));

    public static final AlternateUnit PASCAL = si(new AlternateUnit("Pa", NEWTON.divide(METRE.pow(2))));
    public static final AlternateUnit    JOULE = si(new AlternateUnit("J", NEWTON.times(METRE)));
    public static final AlternateUnit      WATT = si(new AlternateUnit("W", JOULE.divide(SECOND)));

    public static final AlternateUnit COULOMB = si(new AlternateUnit("C", SECOND.times(AMPERE)));
    public static final AlternateUnit VOLT = si(new AlternateUnit("V", WATT.divide(AMPERE)));

    public static final AlternateUnit   FARAD = si(new AlternateUnit("F", COULOMB.divide(VOLT)));
    public static final AlternateUnit      OHM = si(new AlternateUnit("Ω", VOLT.divide(AMPERE)));
    public static final AlternateUnit SIEMENS = si(new AlternateUnit("S", AMPERE.divide(VOLT)));

    public static final AlternateUnit WEBER = si(new AlternateUnit("Wb", VOLT.times(SECOND)));
    public static final AlternateUnit TESLA = si(new AlternateUnit("T", WEBER.divide(METRE.pow(2))));

    public static final AlternateUnit HENRY = si(new AlternateUnit("H", WEBER.divide(AMPERE)));
    public static final Unit CELSIUS = si(KELVIN.plus(273.15));
    public static final AlternateUnit LUMEN = si(new AlternateUnit("lm", CANDELA.times(STERADIAN)));

    public static final AlternateUnit LUX = si(new AlternateUnit("lx", LUMEN.divide(METRE.pow(2))));
    public static final AlternateUnit BECQUEREL = si(new AlternateUnit("Bq", Unit.ONE.divide(SECOND)));
    public static final AlternateUnit GRAY = si(new AlternateUnit("Gy", JOULE.divide(KILOGRAM)));

    public static final AlternateUnit SIEVERT = si(new AlternateUnit("Sv", JOULE.divide(KILOGRAM)));
    public static final AlternateUnit KATAL = si(new AlternateUnit("kat", MOLE.divide(SECOND)));

    // Derived product units ...

    public static final Unit METRES_PER_SECOND = si(new ProductUnit(METRE.divide(SECOND)));
    public static final Unit METERS_PER_SECOND = METRES_PER_SECOND;
    public static final Unit METRES_PER_SQUARE_SECOND = si(new ProductUnit(METRES_PER_SECOND.divide(SECOND)));

    public static final Unit SQUARE_METRE = si(new ProductUnit(METRE.times(METRE)));
    public static final Unit CUBIC_METRE = si(new ProductUnit(SQUARE_METRE.times(METRE)));
    public static final Unit CUBIC_METRE_PER_SECOND = si(new ProductUnit(CUBIC_METRE.divide(SECOND)));

    public static final Unit KILOMETRE = METER.times(1000);
    public static final Unit KILOMETER = KILOMETRE;
    public static final Unit CENTIMETRE = METRE.divide(100);
    public static final Unit CENTIMETER = CENTIMETRE;
    public static final Unit MILLIMETRE = METRE.divide(1000);
    public static final Unit MILLIMETER = MILLIMETRE;

    // SI Prefixes ...

    public static  Unit YOTTA(Unit unit) {
        return unit.transform(E24);
    }

    public static  Unit ZETTA(Unit unit) ---> return unit.transform(E21);
    public static  Unit EXA(Unit unit  ) ---> return unit.transform(E18);
    public static  Unit PETA(Unit unit) ---> return unit.transform(E15);
    public static  Unit TERA(Unit unit) ---> return unit.transform(E12);

    public static  Unit MILLI(Unit unit) ---> return unit.transform(Em3);
    public static  Unit MICRO(Unit unit) ---> return unit.transform(Em6);

    public static  Unit ZEPTO(Unit unit) ---> return unit.transform(Em21);
    public static  Unit YOCTO(Unit unit) ---> return unit.transform(Em24);


Option 4: Variant of OM: Ontology of units of Measure

Motivation and background:

  • Goal is to provide operational support for interpretation of data, and integration of data from a variety of sources. Lack of standardization and formal meaning hinders interoperability.

  • OM distills a semi-formal description (OWL 2) of the domain of units of measure, and is intended for use in science and engineering practice.

  • OM is more-or-less standalone and not grounded in foundational ontologies (e.g., DOLCE).

  • OM needs to support relationships between application area instances and quantity classes, and between quantity classes and commonly-used instances of units of measure.

OM class diagram ...

Here:

  • Prefix: Name that precedes a basic unit of measure to indicate a decimal multiple or fraction of a unit (e.g., milli).

  • Unit of Measure: Definite magnitude of a quantity (e.g., length is a quantity; the metre is a unit of length that represents a definite predetermined length).

  • Measure: Combines a number to a unit of mesasure (or an interval or ratio scale).
    For example, 3.0 kg.

  • Measurement Scale: Concepts used for the expression of quantites. Four example: nominal scale, ordinal scale, interval scales and ratio scales. The Celsius scale is a temperature scale.

  • Quantity: ...

  • Dimension: Abstract properties of units and quantities neglecting their vectorial and all numerical factors.

  • System of Units: Defined by a systems base units, units that are considered to be mutually independent.

Loading OM into Jena 3.8: 28,625 statements, 990 classes.

OM versus QUDT

The architecture of an ontology has a direct bearing on how knowledge

Specification of the diameter of an apple in OM: can be stored and queried.

Specification of the diameter of an apple in QUDT:

Note:

  • QUDT specifies 239 quantity kinds and 801 units. OM specifies 463 quantity kinds (subclasses of om:Quantity) and 1,033 units.
  • OM models quantity kinds as classes. QUDT models quantity kinds as instances of QuantityKind.
  • Units in QUDT are instances of Unit -- Units are linked to their quantity by the property quantityKind.
  • QUDT specifies many physical constants. OM only a few.
  • Application areas are modeled in QUDT as instances of the class QuantityKindCategory -- we do not need this ...
  • Other groupings: qudt:SpaceAndTImeUnit.


Option 5: FOUnt.

FOUnt (Gruninger, Aameris, Chui, Hahmann and Ru, 2018) is a set of ontologies in which each unit of measure has its own modular ontology that includes:

  • Axioms for how the unit is manipulated and combined.
  • Axioms for the nature of the thing being measured.

FOUnt comprises six ontologies:

  • The quantity ontologies axiomatize how units of measure can be combined and how they are related to other units of measure.
  • The unit of measure ontologies axiomatize specific units of measure.
  • The generic object ontologies axiomatize the generic ontologies that underly the quantity ontologies.
  • The measure ontologies introduce functions that map generic objects to their corresponding units of measure.
  • The physical object ontologies axiomatize the different kinds of physical objects that are measured.
  • The physical measure ontologies axiomatize the relationship between the mea- sure ontologies and the physical object ontologies, so that one can speak about the units of measure for a class of physical objects.

Notions such as quantity kinds, units and dimensions are classified the the meta-level. Thus, the focus is on ...

... physical objects and the units that are used to measure them.

Relationship to QUDT and OM

  • Since QUDT and OM are specified in OWL, axiom support is too weak to specify relationships to upper ontologies. No semantic requirements are presented and thus, and evaluation of the correctness and completeness of the ontologies is not possible.

  • QUDT and OM focus is on support for conversion between systems of units rather than what is being measured.

  • EngMath (Gruber, Olsen, 1994) was the original ontology for units of measure with full axiomization.
    This approach seems very similar to the software implementatio of quantities and units in Whistle.


Proposed Model (Work in Progress):

Physical Quantity (Simplified QUDT) Ontology:

In Whistle, physical quantities are modeled as a numerical value + units comprised of a scale (or conversion) factor, a vector of dimension exponents, and support of alternate (or supplementary) units, i.e.,

The physical units ontology:

  • Adds notions of quantity kind (e.g., area, volume, force), thereby opening doors to semantic queries involving groups of quantities having a specific type or kind.

  • Adds support for physical quantity constants (e.g., a day is 86,400 seconds; acceleration due to gravity is 9.81 meters per second squared.


Physical Units Ontology Structure

The high-level class structure and relationships among classes is as follows:

The classes Unit and QuantityKind are expanded to support specific types of units and kinds of quantities -- a partial list is as follows:

The named classes and their object and data properties are as follows:

Named Classes: UMD (Simplified QUDT) Physical Units Ontology Model ...
======================================================================

Named Class(1): AbstractNamedEntity
--- Full Name: http://www.isr.umd.edu/physical-units#AbstractNamedEntity
--- Superclass: Thing ... 
--- Subclass: Dimension ... 
--- Subclass: UnitPrefix ... 
--- Subclass: Unit ... 
--- Subclass: QuantityKind ... 
--- Subclass: Quantity ... 
--- Subclass: SystemOfUnits ... 
--- Data Property Name: hasTextDescription ... 
---             Domain: AbstractNamedEntity ... 
--- Data Property Name: hasFullName ... 
---             Domain: AbstractNamedEntity ... 
--- Data Property Name: hasName ... 
---             Domain: AbstractNamedEntity ... 

Named Class(2): Day
--- Full Name: http://www.isr.umd.edu/physical-units#Day
--- Superclass: Unit-Time-Duration ... 

... data properties removed ...

--- Object Property: hasSystemOfUnits ... 
---           Range: SystemOfUnits ... 
--- Object Property: hasPrefixedUnit ... 
---           Range: UnitPrefix ... 

Named Class(3): Dimension
--- Full Name: http://www.isr.umd.edu/physical-units#Dimension
--- Superclass: AbstractNamedEntity ... 
--- Data Property Name: hasScaleFactor ... 
---             Domain: Dimension ... 
--- Data Property Name: hasTextDescription ... 
---             Domain: AbstractNamedEntity ... 
--- Data Property Name: hasDimensionVector ... 
---             Domain: Dimension ... 
--- Data Property Name: hasFullName ... 
---             Domain: AbstractNamedEntity ... 
--- Data Property Name: hasName ... 
---             Domain: AbstractNamedEntity ... 
--- Object Property: hasSystemOfUnitsType ... 
---           Range: SystemOfUnits ... 

Named Class(4): NonSI
--- Full Name: http://www.isr.umd.edu/physical-units#NonSI
--- Superclass: SystemOfUnits ... 

... data properties removed ...

--- Object Property: hasUnitType ... 
---           Range: Unit ... 

Named Class(5): QantityKind-Temperature
--- Full Name: http://www.isr.umd.edu/physical-units#QantityKind-Temperature
--- Superclass: QuantityKind-Base ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(6): Quantity
--- Full Name: http://www.isr.umd.edu/physical-units#Quantity
--- Superclass: AbstractNamedEntity ... 
--- Subclass: QuantityConstant ... 

... data properties removed ...

--- Object Property: hasUnit ... 
---           Range: Unit ... 

Named Class(7): QuantityConstant
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityConstant
--- Superclass: Quantity ... 

... data properties removed ...

--- Object Property: hasUnit ... 
---           Range: Unit ... 

Named Class(8): QuantityKind
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind
--- Superclass: AbstractNamedEntity ... 
--- Subclass: QuantityKind-Alternate ... 
--- Subclass: QuantityKind-Base ... 
--- Subclass: QuantityKind-Derived ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(9): QuantityKind-Acceleration
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind-Acceleration
--- Superclass: QuantityKind-Derived ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(10): QuantityKind-Alternate
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind-Alternate
--- Superclass: QuantityKind ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(11): QuantityKind-Area
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind-Area
--- Superclass: QuantityKind-Derived ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(12): QuantityKind-Base
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind-Base
--- Superclass: QuantityKind ... 
--- Subclass: QantityKind-Temperature ... 
--- Subclass: QuantityKind-Time ... 
--- Subclass: QuantityKind-Length ... 
--- Subclass: QuantityKind-Mass ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(13): QuantityKind-Derived
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind-Derived
--- Superclass: QuantityKind ... 
--- Subclass: QuantityKind-Velocity ... 
--- Subclass: QuantityKind-Acceleration ... 
--- Subclass: QuantityKind-Power ... 
--- Subclass: QuantityKind-Energy ... 
--- Subclass: QuantityKind-Force ... 
--- Subclass: QuantityKind-Volume ... 
--- Subclass: QuantityKind-Area ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(14): QuantityKind-Diameter
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind-Diameter
--- Superclass: QuantityKind-Length ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(15): QuantityKind-Energy
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind-Energy
--- Superclass: QuantityKind-Derived ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(16): QuantityKind-Force
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind-Force
--- Superclass: QuantityKind-Derived ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(17): QuantityKind-Height
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind-Height
--- Superclass: QuantityKind-Length ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(18): QuantityKind-Length
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind-Length
--- Superclass: QuantityKind-Base ... 
--- Subclass: QuantityKind-Diameter ... 
--- Subclass: QuantityKind-Height ... 
--- Subclass: QuantityKind-Width ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(19): QuantityKind-Mass
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind-Mass
--- Superclass: QuantityKind-Base ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(20): QuantityKind-Power
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind-Power
--- Superclass: QuantityKind-Derived ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(21): QuantityKind-Time
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind-Time
--- Superclass: QuantityKind-Base ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(22): QuantityKind-Velocity
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind-Velocity
--- Superclass: QuantityKind-Derived ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(23): QuantityKind-Volume
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind-Volume
--- Superclass: QuantityKind-Derived ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(24): QuantityKind-Width
--- Full Name: http://www.isr.umd.edu/physical-units#QuantityKind-Width
--- Superclass: QuantityKind-Length ... 

... data properties removed ...

--- Object Property: hasDimension ... 
---           Range: Dimension ... 

Named Class(25): SI
--- Full Name: http://www.isr.umd.edu/physical-units#SI
--- Superclass: SystemOfUnits ... 

... data properties removed ...

--- Object Property: hasUnitType ... 
---           Range: Unit ... 

Named Class(26): SystemOfUnits
--- Full Name: http://www.isr.umd.edu/physical-units#SystemOfUnits
--- Superclass: AbstractNamedEntity ... 
--- Subclass: NonSI ... 
--- Subclass: SI ... 

... data properties removed ...

--- Object Property: hasUnitType ... 
---           Range: Unit ... 

Named Class(27): Unit
--- Full Name: http://www.isr.umd.edu/physical-units#Unit
--- Superclass: AbstractNamedEntity ... 
--- Subclass: Unit-Alternate ... 
--- Subclass: Unit-Derived ... 
--- Subclass: Unit-Base ... 
--- Data Property Name: hasLabel ... 
---             Domain: Unit ... 
--- Data Property Name: hasConversionOffset ... 
---             Domain: Unit ... 
--- Data Property Name: hasTextDescription ... 
---             Domain: AbstractNamedEntity ... 
--- Data Property Name: hasConversionMultiplier ... 
---             Domain: Unit ... 
--- Data Property Name: hasFullName ... 
---             Domain: AbstractNamedEntity ... 
--- Data Property Name: hasName ... 
---             Domain: AbstractNamedEntity ... 
--- Object Property: hasSystemOfUnits ... 
---           Range: SystemOfUnits ... 
--- Object Property: hasPrefixedUnit ... 
---           Range: UnitPrefix ... 

Named Class(28): Unit-Alternate
--- Full Name: http://www.isr.umd.edu/physical-units#Unit-Alternate
--- Superclass: Unit ... 

... data properties removed ...

--- Object Property: hasSystemOfUnits ... 
---           Range: SystemOfUnits ... 
--- Object Property: hasPrefixedUnit ... 
---           Range: UnitPrefix ... 

Named Class(29): Unit-Area
--- Full Name: http://www.isr.umd.edu/physical-units#Unit-Area
--- Superclass: Unit-Derived ... 

... data properties removed ...

--- Object Property: hasSystemOfUnits ... 
---           Range: SystemOfUnits ... 
--- Object Property: hasPrefixedUnit ... 
---           Range: UnitPrefix ... 

Named Class(30): Unit-Base
--- Full Name: http://www.isr.umd.edu/physical-units#Unit-Base
--- Superclass: Unit ... 
--- Subclass: Unit-Temperature-Kelvin ... 
--- Subclass: Unit-Time-Duration ... 
--- Subclass: Unit-Length ... 
--- Subclass: Unit-Mass ... 

... data properties removed ...

--- Object Property: hasSystemOfUnits ... 
---           Range: SystemOfUnits ... 
--- Object Property: hasPrefixedUnit ... 
---           Range: UnitPrefix ... 

Named Class(31): Unit-Derived
--- Full Name: http://www.isr.umd.edu/physical-units#Unit-Derived
--- Superclass: Unit ... 
--- Subclass: Unit-Volume ... 
--- Subclass: Unit-Area ... 

... data properties removed ...

--- Object Property: hasSystemOfUnits ... 
---           Range: SystemOfUnits ... 
--- Object Property: hasPrefixedUnit ... 
---           Range: UnitPrefix ... 

Named Class(32): Unit-Length
--- Full Name: http://www.isr.umd.edu/physical-units#Unit-Length
--- Superclass: Unit-Base ... 

... data properties removed ...

--- Object Property: hasSystemOfUnits ... 
---           Range: SystemOfUnits ... 
--- Object Property: hasPrefixedUnit ... 
---           Range: UnitPrefix ... 

Named Class(33): Unit-Mass
--- Full Name: http://www.isr.umd.edu/physical-units#Unit-Mass
--- Superclass: Unit-Base ... 

... data properties removed ...

--- Object Property: hasSystemOfUnits ... 
---           Range: SystemOfUnits ... 
--- Object Property: hasPrefixedUnit ... 
---           Range: UnitPrefix ... 

Named Class(34): Unit-Temperature-Kelvin
--- Full Name: http://www.isr.umd.edu/physical-units#Unit-Temperature-Kelvin
--- Superclass: Unit-Base ... 

... data properties removed ...

--- Object Property: hasSystemOfUnits ... 
---           Range: SystemOfUnits ... 
--- Object Property: hasPrefixedUnit ... 
---           Range: UnitPrefix ... 

Named Class(35): Unit-Time-Duration
--- Full Name: http://www.isr.umd.edu/physical-units#Unit-Time-Duration
--- Superclass: Unit-Base ... 
--- Subclass: Day ... 

... data properties removed ...

--- Object Property: hasSystemOfUnits ... 
---           Range: SystemOfUnits ... 
--- Object Property: hasPrefixedUnit ... 
---           Range: UnitPrefix ... 

Named Class(36): Unit-Volume
--- Full Name: http://www.isr.umd.edu/physical-units#Unit-Volume
--- Superclass: Unit-Derived ... 

... data properties removed ...

--- Object Property: hasSystemOfUnits ... 
---           Range: SystemOfUnits ... 
--- Object Property: hasPrefixedUnit ... 
---           Range: UnitPrefix ... 

Named Class(37): UnitPrefix
--- Full Name: http://www.isr.umd.edu/physical-units#UnitPrefix
--- Superclass: AbstractNamedEntity ... 
--- Data Property Name: hasTextDescription ... 
---             Domain: AbstractNamedEntity ... 
--- Data Property Name: hasFullName ... 
---             Domain: AbstractNamedEntity ... 
--- Data Property Name: hasName ... 
---             Domain: AbstractNamedEntity ... 

=============================================================


Instantiating Physical Quantities

    ... detail coming soon ....


Checking Consistency of Physical Quantities

    ... detail coming soon ....


Working with Aliases for Physical Units

    ... detail coming soon ....


Mapping Keywords to Physical Units

    ... detail coming soon ....


Example 3: Spatial Ontologies and Reasoning

Semantic Model for Two-Dimensional Geometry

TBD ....

Rules for Spatial Reasoning

TBD ....


Application: Reasoning about Sensors and Rooms

    ... detail coming soon ....


Example 4: Semantic Analysis and Control of Drone Operations

    ... detail coming soon ....

Last Modified: December 27, 2020.