|
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:
the area (or volume) to be searched,
whether the boundary of the search area is part of the search area,
whether to include objects that touch the boundary of the
search area,
the dimensionality of the search (2-D or Surface vs. 3D),
and
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:
|
|