Rolf is the systems manager for LINNET Graphics International Inc. and can be reached at 600-191 Broadway, Winnepeg, MB R3C 378, or at techmgr@LINNET.ca.
Spatial data, such as that derived from digital satellite and orthophoto images, provides us with facts about measurements and observations, and information about location and relationship with respect to other entities on this planet. This information is typically stored in databases that use geographic information systems (GIS) to perform complex spatial queries and data manipulation. Toward this end, large organizations that acquire hardware and software to support GIS systems must convert existing analog data to any number of often-incompatible digital formats.
Databases that don't provide access to such foreign datasets can severely restrict an organization. Standards have been defined for hardware components, network protocols, and user interfaces for maximum interoperability between computer systems, but problems remain. Many data-exchange related standards aren't keeping pace with emerging hardware/software technology. Some industry standards (SAIF in Canada, DIGEST and SDTS in the U.S.) are often too complex for the average user and not yet widely supported by vendors, while other standards based on popular GIS packages (usually derived from early CAD-based systems) are de facto standards. Governments, utilities, and private companies must therefore find a mechanism for sharing spatial data.
Figure 1 is an example of graphically represented information, along with corresponding attribute information. The graphic component defines the object(s) in terms of its spatial characteristics--shape, size, coordinates, color, line style, and the like. This information is usually stored in a proprietary file format that's viewed and manipulated with CAD or GIS software. The attribute component describes the object(s) in terms of its measured characteristics. The attribute data is generally stored in tabular format in a relational database, then viewed and manipulated with SQL (Structured Query Language).
Complete exchange of data between GISs requires that both the graphic and attribute components be transferred. Figure 1 also shows the datastream describing the graphic and attribute data being transferred. The data stream consists of:
Most GISs employ a proprietary internal file structure controlled by the GIS engine. These systems generally provide a mechanism to import and export a growing number of de facto standard formats, allowing for limited movement of data between foreign, albeit similar, systems. Without a correct import or export format, however, data transfer to a foreign system is cumbersome. Exchanging spatial data for which a direct exchange format isn't available involves a programming project, and more than one translation is unachievable for most data-processing shops.
If a common exchange format doesn't exist, then a customized translator must be written; see Figure 2. The number of translators required to facilitate data exchange between all formats is n*(n--1). As Figure 2 illustrates, data exchange between four systems employing different data formats would require 12 translators. A better approach is to combine existing data-exchange formats with custom-developed formats to reduce the number of translators. It's also possible to move data through a multistep translation process; see Figure 3.
The ideal solution to the data-exchange dilemma is a single, neutral data format--one that translates all other formats to and from the neutral format. As Figure 4 shows, the number of translators would be n*2, and the development effort would be shifted to the vendors of the original data format, not the end users.
This solution is difficult because it requires support from all developers and vendors. Progress is being made in this direction with the acceptance of industry-standard formats such as SAIF and DIGEST. For now, however, the best solution is to use existing standards where possible, combined with custom-developed translation mechanisms.
The Manitoba Land Related Information System (MLRIS) is an agreement between Manitoba's government departments and utility companies to collect land-based information to a specified standard, and provide this data to an Information Utility (IU) to be stored and distributed on demand to authorized users. A common base map is used as the geodetic control base for all other land-based data.
In addition to providing the data-exchange facility to end users, the IU will exist as a single source for land-related data. Users won't need to search for foreign datasets, whose existence is often unknown, thus avoiding duplicate collections of land-based information. A streamlined data-exchange process allows users to concentrate on acquiring foreign data and incorporating it into analysis and decision-making processes. Collecting data according to provincially accepted standards ensures that it is accurate and of high quality.
The IU is a massive database that stores spatial data as vector, attribute, and image data. The database is managed by a spatial database manager and Oracle relational database management system (RDBMS), along with inhouse-developed system utilities for importing, exporting, and cataloging the data.
Figure 5 shows the IU system architecture, which consists of a number of interconnected modules that provide data indexing, transfer, viewing, and analysis functions.
The host system is an RS/6000-based processor running AIX with a large amount of online storage. All vector and attribute data are stored online in the RDBMS controlled by the GIS engine; all image data are stored on CD-ROM.
The IU database is actually a composite of many databases, including the enterprise database, which contains the following: the data the IU is capable of serving, an observation database containing a subset of data that end users can view directly, and a thematic database used for custom mapping and analysis.
Data is moved in and out of the enterprise database through an interchange facility which interprets an incoming datastream and translates it to a neutral format to be stored on the enterprise database. Conversely, it can translate data destined for a foreign system to the proper exchange format.
The data-directory module acts as the catalog and administration component of the IU. Popkin Software's System Architect is used to build and store data profiles in a data dictionary. User profiles and user privileges are maintained to assist in the routing of export data and preventing unauthorized access to proprietary or sensitive information. An embedded accounting module provides statistics required for usage billing and royalty disbursements. An e-mail subsystem enables communication with the IU system administrator for dataset-retrieval requests and/or data profile and metadata exchange. Various administration modules assist the IU administrator in processing data-retrieval requests and maintaining the data directory.
A communications facility connects the IU to the outside world for dial-up access to the IU via 14.4 Kbps lines. Once connected to the IU, the end user can access the data directory for an index to the IU database contents, as well as metadata for any available datasets. By connecting to the Observation database through an interface program called IUACCESS, the user can view graphics, key maps, or other data classified as "high-demand" data. This permits the user to make graphic queries on selected features or view the IU database through a visual interface.
Requirements for spatial-data exchange are often based on the user's need to know the location and identification of specific features, and maybe come characteristics about those features. Therefore, it isn't necessary to translate and transfer all of the original data from one spatial database to another. In fact, spatially oriented data can be represented as vector information (points, lines, polygons), raster data, and attribute data (tabular data). By minimizing the amount and type of information to be moved from one system to another, less effort is needed to create the translator. This minimizing process should begin at the source system of the data to ensure that the correct level of detail is abstracted from the source database. The agency responsible for the data is best equipped to make this decision.
The considerations in data-transfer mechanism development between the IU and foreign databases are as follows:
Importing data into the IU database is a multistep process involving a database administrator. Two issues are important during data preparation: The data must be properly identified and described to maintain a meaningful data directory, and the source data must be reorganized into a neutral format for storage and future distribution.
Figure 6 shows the graphic representation of data to be imported and attribute information maintained by the data provider. In this example a subdivision survey plan will be imported, along with key information (primary table) that identifies each subdivision parcel, and owner and assessment data for each parcel (indirect table). The indirect table contains information that doesn't directly describe the feature object, but contains measurements that may or may not exist for the feature objects. A primary table, on the other hand, must contain only one identifier record for each feature object.
The import procedure begins with the preparation of the data model; see Figure 7. To create the model, enough information must be received from the data provider about the dataset(s) to be imported. Using a CASE tool, the data model is constructed, which defines the entities for which the data provider is supplying data. Data structures are built for each entity, defining all of the data elements, and metadata that describes each entity is captured. Metadata capture follows a standard that sufficiently describes the data entities within the IU database. The data model describes the features comprising the subdivision survey plan and associated attribute data.
The purpose for creating a data model is twofold: to export a schema definition in the form of data definition language (DDL) to be used later for loading data into the IU database; and to capture information required by the data directory informing users as to available data. When the data model is complete, the metadata and the schema definition are exported and used as input to a load procedure that populates the data directory.
The graphic-data import process involves importing a data file that describes the spatial constructs of the features. The data provider extracts from the corporate database a graphic data file, usually in a proprietary format, and sends it to the IU administrator. A translation process is determined and format-translation parameters are fed into the translation program along with the source input file. Under the MLRIS initiative, custom translation programs were developed for the most common: AutoCAD (DXF), GDS, and Intergraph. The translation parameters that the administrator creates depend on the features described by the input dataset. For example, feature codes determined by the data provider are usually reclassified to adhere to standard developed by the IU administrator. The translation process produces a data file in a neutral format that describes the spatial orientation of each feature and its primary identifier or database key. In our example, the graphic data file consists of point, line, and centroid information describing the spatial composition of the subdivision survey plan, along with the feature code associated with the survey parcels and the parcel identifier attached to each parcel centroid. For more details on the translation process, see the accompanying text box entitled, "Translating Geographical Data."
Next, a load-preprocessor program is executed, which ensures that the source input file is acceptable for loading into the IU database. This process verifies that the source input file does not contain any unwanted or undefined features, as described in the data directory in the prior steps. If exceptions are found, the load process can't continue until the data model is revised to include the missing features, or the input file is filtered to remove unwanted features. The preprocessor also attaches a standard header to the input file to ensure that each dataset is loaded with the same coordinate-projection information and other key database load parameters.
Finally, the preprocessor program collects statistics and counts of features and creates an area-coverage index of each dataset processed. An area-coverage index is created by matching the coordinate information for each feature against a master tile grid (see Figure 8), where each tile is predetermined in size and location and varies from 100 to 10,000 sq. km. An area-coverage index file is created that identifies each tile within Manitoba for which feature data is present in the dataset being processed. The resulting dataset area-coverage index is loaded into the data directory so users can view graphically the area coverage of a dataset and retrieve data by a given tile number. The area-coverage index is also used for maintenance and retrieval of data from the database on a tile basis.
The final procedure in loading information into the IU database may involve the import of attribute data. Indirect attribute information is data that describes a measurement or occurrence of data associated with a feature. This information may or may not exist for each feature and is therefore stored and maintained separately from the primary table.
The data provider typically delivers the indirect attribute data file in fixed-length record ASCII format which is easily manipulated through SQL and reformatted in preparation for load into the IU database. (Reformatting is necessary only where MLRIS standards haven't been maintained by the data provider, or where key information is reorganized to improve storage and retrieval.)
The corresponding schema definitions for tables to be created in the IU database are obtained from the previously constructed data model. An automatic process generates an import data file consisting of: comma-delimited records, a load-control file containing the SQL instructions for inserting records into a relational-database table, and a DDL for creating the database objects necessary to store the indirect attribute information. The load process for the indirect attribute data is queued and executed in batch mode.
To ensure a successful data-load procedure, the IU administrator reviews the log files maintained during the load procedure, uses the GIS engine to display the graphic data, and queries the attached database records. Once verified, the data directory is updated one last time to indicate that the loaded datasets are available for distribution.
Exporting data from the IU is initiated by an end user who, by viewing the data directory on a local PC, has determined that the IU database contains needed information. The user makes this determination. When required, the IU host system downloads a fresh copy of the directory to ensure that the user is getting current information. The user can query the data directory either through a tabular mechanism or thematic display which identifies the areas of coverage for each dataset theme. After querying the data directory and selecting a dataset, the IU host system is connected via modem, and the request is queued on the host system for processing.
In processing the dataset-retrieval request, the IU administrator must first extract the data from the IU enterprise database. This extract file is in a neutral format and must be translated to the format required by the destination system. The user profile, as stored in the data directory, contains the information and parameters required to deliver the dataset in the correct format. The database schema that corresponds to the dataset being delivered is exported from the data dictionary and integrated with the dataset. The final dataset can be delivered via modem or put on tape, diskette, or CD-ROM.
The Information Utility is Manitoba's answer to the data-exchange dilemma. By using only one format for land-related data, government officials and private businesses can concentrate on using data to make business decisions instead of struggling to convert data into workable formats.
To illustrate the mechanics of translating graphic and attribute data from a foreign system to the Information Utility's neutral format, I'll examine a portion of the process used to translate data from a typical geographical information system called graphic Data System (GDS).
The translation process is initiated by a user with a GDS subsystem developed to assist in the interchange of data between GDS and the Information Utility (IU). The first step in the process is to identify the map sheet and area to be extracted, giving the minimum and maximum coordinate extents. The translator process is then initiated. It first creates a header for the translated file, then retrieves all of the feature objects defined in the GDS database for the given area.
A translation table (see Listing One, page 194) verifies that each feature has been predefined, and that proper feature coding has been assigned. Some feature objects are defined as complex objects and require further parsing to translate the elementary objects that make up the complex object. Each feature object found in the specified extract area is translated according to its object type: line, circle, text, or attributes (see Listing Two, Page 104).
The only real anomaly in the process is translating circle features. Circles are defined differently in different GDS systems; we chose to describe the circle as a series of line segments. In the translation program, each chord segment is 0.5 meters in length. This length is variable and can be set according to the level of resolution required. Sample output from the translator is available electronically; see "Availability," page 3.
--R.O.
[LISTING ONE] **************************************************************************** ; GDS OCD to IU feature code two-way translation table ;*************************************************************************** ; This table provides the translation of GIS Object Code Descriptors to their ; respective feature code, layer, network, and feature type assignments. The ; explode flag indicates whether or not the GDS object is a complex object that ; requires parsing into elementary objects. ; Format must follow this structure: ; GDS OCD (or portion of) = feature code,layer,network,feature type,explode ;*************************************************************************** ;Water FM entities ;*************************************************************************** VALVE:WATER = wr_main_valv, 301, 301, n, N HYDRTEE:WATER = wr_hydr_tee, 301, 301, n, N HYDRANT:WATER = wr_hydrant, 301, 301, n, N HYDRBRAN:WATER = wr_hydr_brch, 301, 301, l, N HYDRVALV:WATER = wr_hydr_valv, 301, 301, n, N MAINS:WATER = wr_watermain, 301, 301, l, N TEE:WATER = wr_fit_tee, 301, 301, n, N BEND:WATER = wr_fit_bend, 301, 301, n, N CROSS:WATER = wr_fit_cross, 301, 301, n, N PLUG:WATER = wr_fit_plug, 301, 301, n, N JUNCTION:WATER = wr_junction, 301, 301, n, N COUPLER:WATER = wr_fit_cplr, 301, 301, n, N REDUCER:WATER = wr_fit_reduc, 301, 301, n, N CASEMENT:WATER = wr_casement, 301, 301, l, N ANODE:WATER = wr_anode, 301, 302, pt, N PIT:WATER = wr_pit, 301, 301, l, N DEPTH:WATER = wr_depth_mrk, 301, 302, pt, N REPAIR:WATER = wr_repair_mk, 301, 302, pt, N MAINANNO:WATER = wr_anno, 301, 303, pt, Y VALVANNO:WATER = wr_anno, 301, 303, pt, Y STREANNO:WATER = wr_anno, 301, 303, pt, N MISCANNO:WATER = wr_anno, 301, 303, pt, N BLDGANNO:WATER = wr_anno, 301, 303, pt, N ;***************************************************************************[LISTING TWO]
/* ********************************************************************** */
/* IUC_TRANSLATE_TO_IU - Function to translate GDS graphics to the IU */
/* ********************************************************************** */
/* received: *library, *details, *IUDATA, count */
/* returned: *num_transed, TRUE if successful */
/* ********************************************************************** */
#define OBJECT 0 /* GET_ITEM returns 0 to type if object */
#define ITEM 3 /* GET_ITEM returns 3 to type if item */
#define LINE 4 /* GET_ITEM returns 4 to itype if open line */
#define CIRCLE 7 /* GET_ITEM returns 7 to itype if circle */
#define TEXT -1 /* GET_ITEM returns -1 to itype if text block */
/* ****** PROTOTYPE DEFINITIONS *******/
/* TRANSLATE_LINE - Function to translate a line */
void TRANSLATE_LINE(
FILE *IUDATA);
/* TRANSLATE_CIRCLE - Function to translate a circle */
void TRANSLATE_CIRCLE(
FILE *IUDATA);
/* TRANSLATE_TEXT - Function to translate text */
void TRANSLATE_TEXT(
FILE *IUDATA);
int IUC_TRANSLATE_TO_IU(
IUR_TRANS *library, /* translation library structure */
ASR_GLODET *details, /* GDS object details structure */
FILE *IUDATA, /* File handle structure for IUDATA file */
unsigned *num_transed, /* number of objects translated */
int lib_pos) /* library structure position */
{
/* Variable declaration */
IUR_FACILITIES facilities;/* Structure to hold facilities RDB info */
IUR_DEPTH_MRK depth; /* Structure to hold depth RDB info */
ASR_GLEDET other_details; /* GDS object extended details structure */
int block_num; /* block number inside object to explode */
int block_type; /* block type inside object to explode */
char scratch[81]; /* Scratch string writing area */
/* Get extra details of object */
ASC_ITEM_DETAILS(&other_details);
/* Is this graphic an object? */
if(other_details.type == OBJECT)
{
/* Check the explode flag. If it's set, explode object into blocks */
if(library->explode[lib_pos] == TRUE)
{
/* Set up extract loop */
for(block_num = 1; block_num <= details->nblock; block_num++)
{
/* Make this block current and get the type */
ASC_BLOCK(block_num);
ASC_GET_BLOCK_TYPE(&block_type);
/* Is this block a line */
if(block_type == LINE)
{
/* Output the library header. Force type to linear */
*num_transed = *num_transed + 1;
fprintf(IUDATA, "feat %d %s %s %s l xy 0.000000 0.000000 1\n",
*num_transed, library->iu_code[lib_pos][IUDATA_FEAT],
library->iu_code[lib_pos][IUDATA_LAYER],
library->iu_code[lib_pos][IUDATA_NETWORK]);
TRANSLATE_LINE(IUDATA);
}
/* Is this block a circle */
else if(block_type == CIRCLE)
{
/* Output the library header. Force type to linear */
*num_transed = *num_transed + 1;
fprintf(IUDATA, "feat %d %s %s %s l xy 0.000000 0.000000 1\n",
*num_transed, library->iu_code[lib_pos][IUDATA_FEAT],
library->iu_code[lib_pos][IUDATA_LAYER],
library->iu_code[lib_pos][IUDATA_NETWORK]);
TRANSLATE_CIRCLE(IUDATA);
}
/* Is this block a text */
else if(block_type == TEXT)
{
/* Output the library header */
*num_transed = *num_transed + 1;
fprintf(IUDATA, "feat %d %s %s %s %s xy 0.000000 0.000000 1\n",
*num_transed, library->iu_code[lib_pos][IUDATA_FEAT],
library->iu_code[lib_pos][IUDATA_LAYER],
library->iu_code[lib_pos][IUDATA_NETWORK],
library->iu_code[lib_pos][IUDATA_TYPE]);
TRANSLATE_TEXT(IUDATA);
}
else
{
/* This is an unknown block type */
sprintf(scratch, "%d (%s)", other_details.itype, details->ocd);
IUC_ERRORS(5, scratch);
}
}
}
else
{
/* Put in the library information */
*num_transed = *num_transed + 1;
fprintf(IUDATA, "feat %d %s %s %s %s xy 0.000000 0.000000 1\n",
*num_transed, library->iu_code[lib_pos][IUDATA_FEAT],
library->iu_code[lib_pos][IUDATA_LAYER],
library->iu_code[lib_pos][IUDATA_NETWORK],
library->iu_code[lib_pos][IUDATA_TYPE]);
/* Put in the coordinates */
fprintf(IUDATA, "coor %.3lf %.3lf\n", details->pos.x, details->pos.y);
}
}
/* Is this graphic an ITEM */
else if(other_details.type == ITEM)
{
/* Put in the library information */
*num_transed = *num_transed + 1;
fprintf(IUDATA, "feat %d %s %s %s %s xy 0.000000 0.000000 1\n",
*num_transed, library->iu_code[lib_pos][IUDATA_FEAT],
library->iu_code[lib_pos][IUDATA_LAYER],
library->iu_code[lib_pos][IUDATA_NETWORK],
library->iu_code[lib_pos][IUDATA_TYPE]);
/* Is this item a line? */
if(other_details.itype == LINE)
{
TRANSLATE_LINE(IUDATA);
}
else if(other_details.itype == CIRCLE)
{
TRANSLATE_CIRCLE(IUDATA);
}
else if(other_details.itype == TEXT)
{
TRANSLATE_TEXT(IUDATA);
}
else
{
/* This is an unknown item type */
sprintf(scratch, "%d (%s)", other_details.itype, details->ocd);
IUC_ERRORS(5, scratch);
*num_transed = *num_transed - 1;
}
}
else
{
/* This is not an object or an item */
sprintf(scratch, "%d", other_details.type);
IUC_ERRORS(4, scratch);
return FALSE;
}
/* Get the attributes and place them in IUDATA file */
if(_OW)
{
if(IUC_GET_RDB_INFO(details->ocd, &facilities, &depth))
{
/* If this is a depth marker, we are interested in depth */
if(strstr(details->ocd, "DEPTH"))
{
fprintf(IUDATA, "attr \"%s\", \"%s\", %3.2lf, %3.2lf\n",
depth.id, depth.ocd, depth.invert, depth.depth);
}
else
{
fprintf(IUDATA, "attr \"%s\", %2.1lf, \"%s\", \"%s\"\n",
facilities.ocd, facilities.size, facilities.install_date,
facilities.material);
}
}
}
/* Everthing worked OK */
return TRUE;
}
/* TRANSLATE_LINE - Function to translate a line */
/* ********************************************************************** */
void TRANSLATE_LINE(
FILE *IUDATA)
{
/* Variable declaration */
int num_vertices; /* number of vertices in linear object */
int vertex; /* vertex counter */
double bulge; /* bulge factor between vertices */
double vertex_x, vertex_y; /* x and y coords of line vertex */
/* ****************************************************************** */
/* Get the number of vertices in this ine item */
ASC_GET_BLOCK_LENGTH(&num_vertices);
/* Loop through number of vertices until complete */
for(vertex = 1; vertex <= num_vertices; vertex++)
{
/* Get the vertex and print out to the file */
ASC_GET_BLOCK_VERTEX(vertex, &bulge, &vertex_x, &vertex_y);
/* Print out the vertex */
fprintf(IUDATA, "coor %.3lf %.3lf\n", vertex_x, vertex_y);
}
}
/* TRANSLATE_CIRCLE - Function to translate a circle */
/* *********************************************************************** */
void TRANSLATE_CIRCLE(
FILE *IUDATA)
{
/* Variable declaration */
double ctr_x, ctr_y; /* x and y coords of circle centre */
double radius; /* radius of a circle */
double chord_length = 0.5; /* chord length for splitting circle */
double dx, dy; /* delta x and y coords for splitting circle */
double dO; /* delta theta for the angle to split circle */
double total_dO; /* summing variable for dO */
double pi = 3.14159265359; /* value of pi */
double vertex_x, vertex_y; /* x and y coords of line vertex */
/* ******************************************************************** */
/* Get the details of the circle */
ASC_GET_ARC_DETAILS(2, &ctr_x, &ctr_y, &radius);
/* Output the first point to the IUDATA file */
fprintf(IUDATA, "coor %.3lf %.3lf\n", ctr_x + radius, ctr_y);
/* Calculate the delta angle to split based on chord length */
dx = ((2 * pow(radius, 2.0)) - pow(chord_length, 2.0)) / (2 * radius);
dy = sqrt(pow(radius, 2.0) - pow(dx, 2.0));
dO = atan2(dy, dx);
/* Loop through and create a line with the calc'd delta theta */
total_dO = 0.0;
while(total_dO <= (2 * pi))
{
/* Calculate the line vertices */
vertex_x = ctr_x + dx;
vertex_y = ctr_y + dy;
/* Print out the vertex */
fprintf(IUDATA, "coor %.3lf %.3lf\n", vertex_x, vertex_y);
/* Increment the delta theta of the circle angle */
total_dO = total_dO + dO;
/* Re-calc the dx and dy */
dy = radius * sin(total_dO);
dx = radius * cos(total_dO);
}
/* Close the circle by outputting the first point again */
fprintf(IUDATA, "coor %.3lf %.3lf\n", ctr_x + radius, ctr_y);
}
/* TRANSLATE_TEXT - Function to translate text */
/* *********************************************************************** */
void TRANSLATE_TEXT(
FILE *IUDATA)
{
/* Variable declaration */
double justpos_x, justpos_y; /* x and y coords of text position */
double rotation_x, rotation_y;/* sin and cos of text rotation */
double rotate_x, rotate_y; /* x and y coords of text rotation */
char text[241]; /* text to be translated */
/* ********************************************************************* */
/* Get the text position and rotation */
ASC_GET_BLOCK_TEXT_POSITION(&justpos_x, &justpos_y);
ASC_GET_BLOCK_TEXT_ROTATION(&rotation_x, &rotation_y);
/* The rotation supplied is a sine and cosine. Change to xy */
rotate_x = justpos_x + rotation_x;
rotate_y = justpos_y + rotation_y;
/* Put in the coordinates */
fprintf(IUDATA, "coor %.3lf %.3lf %.3lf %.3lf\n", justpos_x, justpos_y,
rotate_x, rotate_y);
/* Put in the text itself */
ASC_GET_BLOCK_TEXT(text);
fprintf(IUDATA, "text \"%s\"\n", text);
}
[LISTING THREE]udb-feature feat 1 cow_surv_lin 901 904 l xy 0.000000 0.000000 1 coor 623153.468 5526056.618 coor 623140.811 5526070.650 feat 2 cow_pid_anno 901 905 pt xy 0.000000 0.000000 1 coor 623156.580 5526071.060 623157.315 5526071.738 text "4-1-16966" feat 3 cow_surv_lin 901 904 l xy 0.000000 0.000000 1 coor 623140.641 5526000.000 coor 623126.927 5526008.687 feat 4 cow_surv_lin 901 904 l xy 0.000000 0.000000 1 coor 623064.398 5526000.000 coor 623065.035 5526011.885 coor 623066.079 5526031.367 coor 623067.123 5526050.850 coor 623069.496 5526095.131 coor 623074.089 5526098.292 feat 5 cow_surv_lin 901 904 l xy 0.000000 0.000000 1 coor 623046.226 5526000.697 coor 623012.741 5526002.458 feat 6 cow_surv_lin 901 904 l xy 0.000000 0.000000 1 coor 623013.857 5526023.146 coor 623012.741 5526002.458 feat 7 cow_pid_anno 901 905 pt xy 0.000000 0.000000 1 coor 623109.960 5526009.726 623110.702 5526010.395 text "11058" feat 8 cow_pid_anno 901 905 pt xy 0.000000 0.000000 1 coor 623077.940 5526000.900 623078.939 5526000.851 text "17-3-11058" feat 9 cow_pid_anno 901 905 pt xy 0.000000 0.000000 1 coor 623115.930 5526002.695 623116.836 5526002.273 text "8-3-11058" feat 10 cow_surv_lin 901 904 l xy 0.000000 0.000000 1 coor 623000.000 5526003.125 coor 623012.741 5526002.458 feat 11 cow_surv_lin 901 904 l xy 0.000000 0.000000 1 coor 623049.517 5526061.554 coor 623048.431 5526041.472 coor 623047.345 5526021.389 coor 623046.226 5526000.697 coor 623046.188 5526000.000 feat 12 cow_surv_lin 901 904 l xy 0.000000 0.000000 1 coor 623012.741 5526002.458 coor 623012.608 5526000.000 feat 13 cow_surv_lin 901 904 l xy 0.000000 0.000000 1 coor 623099.635 5526000.000
Copyright © 1993, Dr. Dobb's Journal