SEDRIS Reference Manual
Section 2 - STANDARD SEDRIS FUNCTIONS AND DATA STRUCTURES
2.1 Introduction

This section deals with the standard SEDRIS function calls, as well as critical data structures. The parameters associated with each function call are detailed, and the resultant processing is described. The function calls are listed alphabetically, each in a separate appendix associated with its SEDRIS component. Note that SRM and EDCS are separate stand-alone entities, so for information specific to SRM or EDCS, see their individual reference manuals. The DRM, while it is the heart of SEDRIS, also has its own reference manual (SEDRIS Documentation Set Part 4, Volume 2) in regard to its data representation aspects, such as its data structures and classes.

Function call information corresponds to the source code files shown in the table below.

COMPONENT SOURCE CODE FILES
Transmittal Access Level 0 API se_extract.h
se_extract_print.h
se_extract_types.h
se_extract_valid.h
se_extract_util.h
se_insert.h
se_insert_print.h
se_insert_types.h
Transmittal Access Level 1 API se_read1_data_table.h
se_read1_error.h
se_read1_image.h
se_read1_print.h
se_read1_utils.h
se_read1_valid.h
se_write1.h

NOTES:

  1. For items that follow, names in all uppercase indicate enumerants.
  2. A greater level of comprehension can be achieved by reviewing the appendices detailing the functions and data types of the associated modules.
2.2 Data Representation Model (DRM) Functions and Data Structures

See Part 4, Volume 2: The SEDRIS Data Representation Model.

2.3 Transmittal Access Level 0 API Functions and Data Structures
2.3.1 Overview
The Transmittal Access Level 0 API functions and data structures are listed alphabetically in Appendix B.
2.3.2 Iterators
2.3.2.1 Overview

Iterators are the heart of the Transmittal Access Level 0 API. Stepping through the list provided by an iterator is a multi-step process, as outlined below.

  • Specify any search filter needed to limit the search (this is optional; see next section for details).

  • Call one of the following functions to create the iterator, passing in the start object of the search (depending on the type of iterator needed):

  • If a search filter was specified, and if the user will not be using it to create more iterators, the user frees it at this point. (The iterator maintains a copy of the search filter in the API's memory space, so that freeing a search filter while an iterator still exists that uses it causes no problems.) If the search filter isn't freed at this point, remember that it must be freed eventually, to avoid "orphaning" the memory associated with the search filter.

  • Use SE_GetNextObject() to retrieve objects from the iterator. Don't forget to call SE_FreeObject() for each object retrieved, including the link objects.

    Repeat this step as needed.

  • When finished retrieving objects from the iterator, call SE_FreeIterator() to release the iterator that was allocated by the third step.

2.3.2.2 Search Filters and the Search Macros

Any iterator may specify a set of search rules to limit its search by way of a search filter, although some search rules only apply to specific kinds of iterators. This is a two step process.

  • Specify any search rules needed to limit the search. The search macros (see SE_Search_Rule_Type for a list) can be used to specify search rules. They are designed to make arrays of search rules more readable. By using these macros, search rules can appear to be defined with in-fix notation expressions even though the macros actually build up a post-fix notation expression stored in an array.

  • Pass the array of search rules to SE_CreateSearchFilter() to create a search filter. (Note that a search filter is valid only for the API implementation for which it was created.)

The real work of creating a search filter, therefore, lies in creating its search rules. To simplify the specification of search rules, the SEDRIS API implementation provides various macros.

Searches can be based on any of the following:

  • whether an object is of a particular class,
  • whether an object of a specific class has an instance of another, possibly different, class as a component,
  • the value of one of the fields of an object,
  • the value of one of the fields of a class of component of an object,
  • the passing of a user-defined test function,
  • the existence of any associations,
  • the existence of associations to objects of a particular class, and
  • any combination of 'ANDing', 'ORing', or 'NOTing' these conditions.

Searches are always started from a single 'start object' passed in as a parameter to a functions used to initialize iterators, such as SE_InitializeAggregateIterator(), SE_InitializeAssociateIterator(), SE_InitializeComponentIterator(), and SE_InitializeInheritedComponentIterator().

By default, searches in a Component Iterator start from the given start object and search the entire tree, in a breadth-first manner, returning all objects that meet the given search criteria (all objects that pass the search rules and fall within the given spatial boundaries). A limit can be placed on the number of levels 'down' that a component iterator will search by using the maximum search depth macro. By ANDing the maximum search depth rule with other rules, the maximum search depth can apply to the entire search or to portions of a search (see examples in the table below).

Search Description Search Rules

As an example of a search based on object class, this set of rules is used to find all <Polygon> instances within the given scope.

SE_Search_Rule rules[] = {
SE_DRM_CLASS_MATCH(POLYGON)
SE_END
};

A set of rules designed to find all objects in the given scope that have associations to other objects.

SE_Search_Rule rules[] =
{
SE_ASSOCIATE
SE_END
};

As an example of a search based on whether instances of a given class have instances of a specific (possibly different) class as components, this set of rules is designed to find all <Linear Feature> instances in the given scope that have <Property Value> components.

SE_Search_Rule rules[] =
{
SE_COMPONENT_DRM_CLASS_MATCH(LINEAR_FEATURE, PROPERTY_VALUE)
SE_END
};

As an example of a combination of simple rules, including a constraint on the maximum search depth, this set of rules finds any object that is either a <Aggregate Feature> instance (that is, an instance of any concrete subclass of <Aggregate Feature>) or a <Primitive Feature> instance (that is, an instance of any concrete subclass of <Primitive Feature>) within 4 levels of the start object.

SE_Search_Rule rules[] =
{
SE_AND
(
SE_OR
(
SE_DRM_CLASS_MATCH(AGGREGATE_FEATURE),
SE_DRM_CLASS_MATCH(PRIMITIVE_FEATURE)
),
SE_MAX_SEARCH_DEPTH(4)
)
SE_END
};

This is an example of a combination of search rules, including a constraint on the maximum search depth. This set of search rules finds any object that is either an <Aggregate Geometry> instance (that is, an instance of any concrete subclass of <Aggregate Geometry>) within 4 levels from the start object of the search, or is a <Feature Representation> instance within 3 levels of the start object.

SE_Search_Rule rules[] =
{
SE_OR
(
SE_AND
(
SE_DRM_CLASS_MATCH(AGGREGATE_GEOMETRY),
SE_MAX_SEARCH_DEPTH(4)
),
SE_AND
(
SE_DRM_CLASS_MATCH(FEATURE),
SE_MAX_SEARCH_DEPTH(3)
)
)
SE_END
};

As an example of a set of search rules that detect all instances of a given class such that each instance I has a component of another (possibly different class) C, where the instance of C has field values matching specific criteria, this set of search rules finds all <Model> instances in the given scope with an <Overload Priority Index> component with a value not equal to 1.

static SRM_Short_Integer search_value = 1;

SE_Search_Rule rules[] =
{
    SE_OBJECT_AND
    (
        SE_COMPONENT_DRM_CLASS_MATCH(MODEL, OVERLOAD_PRIORITY_INDEX),
        SE_NOT
        (
            SE_COMPONENT_FIELD_MATCH
            (
                MODEL,
                OVERLOAD_PRIORITY_INDEX,
                Overload_Priority_Index,
                overload_priority,
                &search_value,
                SE_SEARCHVALTYP_SHORT_INTEGER
            )
        )
    )
    SE_END
};

As an example of a search based on the class of object that then determines whether a field of that object falls within a specified range of values, this set of search rules is designed to find all <CD Surface Location> instances between 50 degrees latitude, 30 degrees longitude, and 60 degrees latitude, 40 degrees longitude.

static SRM_Long_Float latitude1 = 50, latitude2 = 60;
static SRM_Long_Float longitude1 = 30, longitude2 = 40;

SE_Search_Rule rules[] =
{
SE_AND
(
SE_FIELD_RANGE
(
CD_SURFACE_LOCATION,
geodetic_latitude,
&latitude1,
&latitude2,
SE_SEARCHVALTYP_LONG_FLOAT
),
SE_FIELD_RANGE
(
CD_SURFACE_LOCATION,
geodetic_longitude,
&longitude1,
&longitude2,
SE_SEARCHVALTYP_LONG_FLOAT
)
)
};

As an example of a search based on finding objects of a given class that have components of a specific (possibly different) class, and as an example of a search condition based on the passing of a user-defined test function, this set of search rules is designed to find all <Linear Feature> instances that have at least one <Property Value> component that passes a given predicate test. (The predicate test is designed to check for certain values of <Property Value>, most probably.)

The application must define the test_func function. A NULL is being passed for the user-test-data to this testing function - implying that this testing function does not take advantage of user-supplied test data.

SE_Boolean test_func (SE_Object test_object, SE_Object, void*);

SE_Search_Rule rules[] =
{
SE_AND
(
SE_COMPONENT_DRM_CLASS_MATCH(LINEAR_FEATURE, PROPERTY_VALUE),
SE_PREDICATE_MATCH(test_func, NULL)
)
SE_END
};

Find all <Areal Feature> instances in the given scope; in addition, find only the <Linear Feature> instances in the given scope with at least one <Property Value> component that passes the given predicate test. (The application must define the test_func function). This particular test function does not examine the link class object and also does not use any user-supplied test data.

   SE_Boolean test_func (SE_Object test_object,
                         SE_Object,
                         void*);

SE_Search_Rule rules[] =
{
    SE_OR
    (
        SE_DRM_CLASS_MATCH(AREAL_FEATURE),
        SE_AND
        (
            SE_COMPONENT_DRM_CLASS_MATCH(LINEAR_FEATURE, PROPERTY_VALUE),
            SE_PREDICATE_MATCH(test_func, NULL)
        )
    )
    SE_END
};

The use of these macros is VERY strongly recommended rather than 'building the rules manually', that is, without the aid of these macros. Consider the following example.

Search Description Using Macros Without Using Macros

This set of search rules finds any object that is either an <Aggregate Geometry> instance (that is, an instance of any concrete subclass of <Aggregate Geometry>) or a <Feature Representation> instance within 4 levels of the start object of the search.

SE_Search_Rule rules[] = {
SE_AND
(
SE_OR
(
SE_DRM_CLASS_MATCH
(
AGGREGATE_GEOMTETRY
),
SE_DRM_CLASS_MATCH
(
PRIMITIVE_FEATURE
)
),
SE_MAX_SEARCH_DEPTH(4)
)
SE_END
};
SE_Search_Rule *rules;

rules = (SE_Search_Rule *)
calloc(6, sizeof(SE_Search_Rule));

rules[0].rule_type = SE_SEARCHRULTYP_DRM_CLASS;
rules[0].object_drm_class = SE_CLS_DRM_AGGREGATE_FEATURE;

rules[1].rule_type = SE_SEARCHRULTYP_DRM_CLASS;
rules[1].object_drm_class = SE_CLS_DRM_PRIMITIVE_FEATURE;

rules[2].rule_type = SE_SEARCHRULTYP_OR;

rules[3].rule_type = SE_SEARCHRULTYP_MAX_SEARCH_DEPTH;
rules[3].max_depth = 4;

rules[4].rule_type = SE_SEARCHRULTYP_AND;

rules[5].rule_type = SE_SEARCHRULTYP_END;

2.3.2.3 Component Iterators: Selection Parameters

SE_InitializeComponentIterator()'s SE_Hierarchy_Select_Parameters argument ( select_parameters_ptr ) can be used to restrict the scope of a search. If the parameter is "NULL", it is ignored. If it is non-NULL, then it is used to determine which components to traverse when an <Aggregate Feature> or <Aggregate Geometry> instance is encountered.

The SE_Hierarchy_Select_Parameters type is defined in se_extract_types.h in the Transmittal Access Level 0 API. To use this structure:

  • Use SE_InitializeHierarchySelectParameters() (a helper function) to initialize this structure, or initialize it yourself.

  • Fill out the fields of general_hierarchy_mask to specify which of the remaining fields will be used. All the field values must be set. (Exception: if time_related is "SE_FALSE", the related booleans are ignored, and if lod_related is "SE_FALSE", its related booleans are ignored).

  • For each of the fields within the general_hierarchy_mask, the corresponding _branches field MUST be initialized. (Otherwise, the iterator will be using garbage values to prune its search).

To clarify the use of SE_Hierarchy_Select_Parameters, consider the following example. Suppose that a search is being created to retrieve only the geometry and features associated with trees (specifically, with the EDCS Classification Codes represented by ECC_TREE and ECC_FOREST). The selection parameters would be created as follows.

SE_Hierarchy_Select_Parameters  select_param;
SE_General_Hierarchy_Select    *mask_ptr = NULL;
SE_Classification_Parameters   *cl_ptr = NULL;

SE_InitializeHierarchySelectParameters(&select_param);

mask_ptr = &select_param.general_hierarchy_mask

mask_ptr->classification_related = SE_TRUE;

mask_ptr->union_of_features = SE_TRUE;

mask_ptr->union_of_geometry_hierarchies = SE_TRUE;

mask_ptr->union_of_geometry_primitives = SE_TRUE;

Now fill out the _branches fields that we've indicated we'd use - in this case, just classification_branches.


cl_ptr = &select_param.classification_branches;

cl_ptr->classification_count = 2;

cl_ptr->classification_array = (SE_Classification_Data_Fields *)

calloc(SE_Classification_Data_Fields, 2);

cl_ptr->classification_array[0].tag = ECC_TREE;

cl_ptr->classification_array[0].tag = ECC_FOREST;

Consider the result when this select_param is passed to SE_InitializeComponentIterator().

2.3.2.4 Component Iterators: Ordering Parameters

SE_InitializeComponentIterator()'s SE_Hierarchy_Order_Parameters argument (traversal_order_parameters_ptr) can be used to control the order in which the branches of an aggregate will be searched. If the parameter is "NULL", it is ignored. If it is non-NULL, then it is used to determine the traversal order of the branches when an <Aggregate Feature> or <Aggregate Geometry> instance is encountered.

The SE_Hierarchy_Order_Parameters type is defined in se_extract_types.h in the Transmittal Access Level 0 API. To use this structure:

  • Use SE_InitializeHierarchyOrderParameters() (a helper function) to initialize this structure, or initialize it yourself.

  • Fill out the field values in general_hierarchy_mask to specify which of the rest of the fields will be used. All of the fields must be initialized.

  • For each of the fields within the general_hierarchy_mask, the corresponding _traversal_order field MUST be initialized. (Otherwise, the iterator will be using garbage values to determine the traversal order).

2.3.2.5 Component Iterators: Search Boundaries

A component iterator, unlike the other types of iterators, may use spatial search boundaries to restrict the scope of its search. To use such a boundary, several extra steps are required before initializing the iterator.

  • Specify the search bounds in an SE_Search_Bounds variable. The search bounds must be specified using the SRF that will be in effect when the iterator is in use.

  • Pass the search bounds to SE_CreateSpatialSearchBoundary(), which will allocate and create the search boundary itself. (This boundary must be freed later by SE_FreeSpatialSearchBoundary()).

  • Pass the search boundary to SE_InitializeComponentIterator() for any iterator that should be restricted to search only that search boundary's specified spatial area. Once the iterator has been created, the search boundary may be freed safely, since the iterator maintains a copy of the search boundary in the API's memory space.

  • While search bounds are in use, do NOT reset the API's SRF parameters.

See Search Bounds for more details.

2.3.3 Level 0 Read API: Search Bounds
2.3.3.1 Overview

A search boundary has several characteristics that must be determined before the search is performed:

  1. the area (or volume) to be searched,

  2. whether the boundary of the search area is part of the search area,

  3. whether to include objects that touch the boundary of the search area,

  4. the dimensionality of the search (2-D or Surface vs. 3D), and

  5. how an object is to be approximated.

The area (or volume) to be searched is specified via the SE_Search_Bounds structure, and by specifying whether the search area's boundary is part of the search area. The SE_Search_Bounds structure specifies the ranges of the coordinates to be considered in the search. (To create unlimited search areas, use SE_NEGATIVE_INFINITY and SE_POSITIVE_INFINITY, as appropriate.)

Given the search bounds, SEDRIS allows for two possibilities via SE_Search_Bounds_Closure. The search bounds can be either fully or partly closed. Fully closed search bounds are topologically closed, so that the entire boundary is included. This is appropriate for finding all objects in an area of interest, since the objects on the border may affect the interior of the search bounds. Partly closed search bounds, on the other hand, are topologically half-closed; that is, only the lower endpoints of the coordinate ranges are in the search bounds. This is useful if a large area is being tessellated, so that each point will belong to exactly one tile of the tesselation.

Apart from specifying whether the boundary of the search bounds is included within the bounds, the user must specify how to handle boundary cases (see SE_Object_Inclusion). An object can be fully contained within the search bounds (i.e., the intersection of the search bounds and the object equals the object), or partly contained within the search bounds (i.e., the intersection of the object and the search bounds is non-empty). Full inclusion implies partial inclusion. Consequently, a search specified with SE_OBJINCL_PARTIALLY_INCLUDED is less restrictive than a search specified with SE_OBJINCL_FULLY_INCLUDED. (To check whether an object includes the search bounds, see SE_DetermineSpatialInclusion()).

Search dimensionality (specified by SE_Search_Dimensionality) determines whether the search considers 2D objects only, 3D objects only, or both. Note that SE_SEARCH_DIM_TWO_DIMENSIONAL_OR_SURFACE is only valid for SRFs that support <Location 2D>s or <Location Surface>s, such as Celestiodetic (CD). For a 2D or Surface search, the height / elevation portion of both the search bounds and of any 3D objects encountered will be ignored.

Finally, the user must specify how objects are to be approximated during the search (see SE_Search_Type). SEDRIS provides two kinds of approximation: point searches and bounding box searches. (A third type of search, exact search, is described in the interface, but is not yet available; this type would use the exact representation of the object rather than an approximation).

The following classes of objects can be approximated for a spatial search:

For details on the specific functions involved in specifying and using search bounds, see SE_CreateSpatialSearchBoundary(), SE_DetermineSpatialInclusion().

2.3.3.2 Point Searching

For point searching, a non-aggregate SEDRIS object is approximated by a single point. In general, this search point is the average of the <Location> components of the object being represented. This type of search is fast, but may not be sufficiently accurate. (Note that for point searches, full and partial inclusion have the same meaning.)

For an aggregate object whose components are all of the same dimensionality, the search point is constructed as the average of the component search points. For an aggregate object containing both 2D, Surface, and 3D components, however, three unions are maintained, one for each of the component types.

For point searches, coordinate system transformations introduce no distortion; this is not the case for bounding boxes.

2.3.3.3 Bounding Box Searching

Bounding box searches are more accurate than point searches. To construct the bounding box for a non-aggregate object, the extrema for each coordinate in the object's native SRF are computed, and used to create a 2D or 3D box. This box then represents the object in the search.

For an aggregate object whose components are all of the same dimensionality, the bounding box is constructed as the union of the component bounding boxes. For an aggregate object containing both 2D, Surface, and 3D components, however, three unions are maintained, one for each of the component types.

Bounding box searches introduce complications when coordinate system transformations are involved. The faces and edges of a bounding box can be distorted by the transformation (e.g., a straight line in one SRF may be a curve in a different SRF).

The rest of this segment describes the algorithms used for bounding box searches.

In an attempt to overcome the distortion issue, the ideas of inscribed and circumscribed boxes were introduced. For an arbitrary shape, an inscribed box is the largest box that fits inside the shape, while a circumscribed box is the smallest box that contains the shape.

In the algorithms below, "the search succeeds" means that the iterator will pass the object as being inside the boundary; "the search fails" means that the object will be rejected.

For a full inclusion bounding box search, first find the object's bounding box in its native SRF, then convert the vertices of the box to the user's SRF. If any vertex is outside the search boundary, reject this object. If the search continues, compute the circumscribed bounding box of the object; if it is within the search bounds, this object is accepted by the search. Otherwise, compute the inscribed search box of the search bounds in the object's SRF; if the object's bounding box is inside the inscribed search boundary, the search succeeds for the object; otherwise, it fails.

For a partial inclusion bounding box search, first find the object's bounding box in its native SRF, then convert the vertices of the box to the user's SRF. If any vertex is inside the search boundary, the search succeeds for this object. If the search continues, compute the inscribed bounding box of the object; if it intersects the search bounds, this object is accepted by the search. Otherwise, compute the circumscribed search box of the search bounds in the object's SRF; if the object's bounding box intersects the circumscribed search boundary, the search succeeds for the object; otherwise, it fails.

2.3.4 Transmittal Access Level 0 API Functions and Data Structures

To create a transmittal using the Transmittal Access API, you must pay careful attention to the order of operations. First, create the transmittal with a call to SE_OpenTransmittalByLocation() or SE_OpenTransmittalByName():

status = SE_OpenTransmittalByLocation
         ( filename_location, SE_ENCODING_SRF, SE_AM_CREATE, &transmittal );

This call creates a transmittal containing no objects using the API implmentation indicated by the impl_id. The <Transmittal Root>, like any object, must be created before it can have any component objects attached. Consequently, the first task is to finish creating the <Transmittal Root>.

status = SE_CreateObject(transmittal, &transmittal_root);

if (status == SE_STATCODE_SUCCESS)
    status = SE_PutFields(transmittalroot, &new_fields);

where new_fields is an SE_DRM_Class_Fields variable containing the fields for the new <Transmittal Root>.

For any object, to change its field values from the default values for an object of that object's class, call SE_PutFields() for that object.

So for any transmittal, the correct sequence of function calls is:

for the <Transmittal Root>, then create any components for the <Transmittal Root>.

For any object, SE_CreateObject() must initially be called to create the object. The correct sequence of function calls is:

  • SE_CreateObject
  • SE_PutFields (optional; only if you don't want the default fields values)
  • Add the object as a component of some object that's already in the transmittal (except for <Transmittal Root>, which is passed to SE_SetRootObject())
  • (optional) add any associations between this object and objects that are already in the transmittal, if there are any

For an <Image> instance, there is an extra step. Once the <Image> has been added to the transmittal, you must call SE_PutImageData() to add the pixels / texels of the <Image> to the object.

The most complicated operation is to add a <Data Table> to a transmittal. Here the sequence is:

2.4 Transmittal Access Level 1 API Functions and Data Structures
The Transmittal Access Level 1 API functions and data structures are listed alphabetically in Appendix C.


Return to: Top of this Page, Table of Contents