\documentclass{report}
\usepackage[koi8-r]{inputenc}
\usepackage[russian]{babel}
\newcommand{\function}[1]{\par\smallskip{\raggedright\setlength{\hangindent}{2cm}\noindent\tt #1\par} \smallskip}
\newcommand{\var}[2]{\par\smallskip{\setlength{\hangindent}{1cm}\noindent\tt#1\rm~--- #2\par} \smallskip}
\textwidth=6.3in
\oddsidemargin=0.5in
\title{Библиотека для работы\\ с файлами EPPL7.\\Руководство программиста}
\author{В.Б.~Вагнер}
\makeindex
\begin{document}
\maketitle
\tableofcontents
\chapter*{Введение}\addcontentsline{toc}{chapter}{Введение}

Библиотека предназначена для работы с векторными и растровыми
файлами EPPL7 из программ на языке C. Библиотека расчитана на 
работу в 32-разрядной среде. В принципе небольшие программы
способны работать и в 16-разрядной среде, но поскольку специальных
мер для этого не принималось, работа в моделях памяти Compact, Large
и Huge не гарантируется.

В библиотеках предусмотрен код для работы на процессорах с 
прямым порядком байт (MSB First), хотя он пока не оттестирован.

Тем не менее не рекомендуется использовать читать файлы EPPL7 непосредственно,
так как при этом придется обращать внимание на порядок байтов в слове.

Поскольку оригинальный пакет EPPL7 работал под DOS, все его файлы,
хранящиеся в двоичном формате используют порядок байтов процессора Intel~---
младший байт первым. 


Библиотеки не используют системных вызовов и библиотечных функций,
специфичных для UNIX\footnote{За исключением утилит для работы с файловой
системой и обрабоки прерывания программы. Эти функции локализованы
в модуле {\tt file\_utils.c} и описаны в главе \ref{utils}}.

При разработке библиотек предполагалось, что тип {\tt long int} имеет
размер 32 бита, {\tt short int}~--- 16 бит и {\tt double}~--- 64 бита,
в формате, удовлетворяющем стандарту IEEE.

\chapter{Логическая модель файлов данных}
\section{Растр}

Теоретически, растровая карта является функцией от координат,
значения которой являются числами или элементами некоторого
конечного множества, определенной внутри некоторой геометрической
фигуры на декартовой координатной площади.

Практически как область определения, так и область значений
квантуются, а целью  библиотеки является возможность
произвольным образом задавать значения растра в любой нужной точке.


Поэтому, растровые файлы EPPL7 (epp) рассматриваются как большая матрица
коротких (16-бит) целых. Размеры матрицы могут достигать $30000\times30000$
а значения - принимать любое значение, допустимое для типа
{\tt unsigned short} т.е. 0--65535.

Соответствие между вещественными картографическими
координатами и целочислеными индексами в матрице (рядами и колонками)
хранится в заголовке epp-файла, и в библиотеке предусмотрены функции 
для пересчета одних в другие. 

Номера рядов/колонок в файле не обязательно начинаются с единицы.
Для них допустимы любые значения в диапазоне -32767--+32767.

Это позволяет в файле, покрывающем небольшной участок территории иметь
совместимость с б\`ольшим файлом не только по альтернативным координатам,
но и по номерам рядов/колонок. 

Обычно ячейки в файле квадратные, хотя все процедуры библиотеки
будут корректно работать с файлами, в которых размеры ячейки по вертикали
и по горизонтали различны.

Значение площади ячейки вместе с размерностью (квадратные метры, футы,
гектары и др.) хранится в заголовке файла. Линейные единицы альтернтативных
координат нигде не описываются, но для совместимости не рекомендуется
использовать никаких единиц кроме метров или градусов (если файл хранится
в географических координатах). 

При этом площадь ячейки можно перевести в другие единицы, соответственно
изменив код размерности, для того чтобы получить более удобочитаемые числа
при рассчете площадей.

Преобразование множества значений карты в множество значений (классов)
epp-файла традиционно считается функцией легенды, и находится за
пределами формата epp, а, следовательно и данной библиотеки.

Поскольку матрица всегда прямоугольна, а область определения карты~---
не всегда, предусмотрено специальное значение, называемое offsite.

При чтении epp-файла предполагается, что размеры его бесконечны, но
попытка прочитать его элемент за прелами реального файла приводит
к возврату значения offsite, которое означает, что в данной точке 
значение не определено. Значение offsite свое для каждой карты. Оно 
хранится в заголовке файла и в соответствующем поле структуры данных,
используемой для доступа к файлу.

Большая часть алгоритмов, используемых для генерации растровых карт или
для их анализа, не зависит от порядка обхода ячеек карты, хотя и требует
полного их перебора. В этих случаях рекомендуется использовать 
{\it последовательный}
перебор по строкам, как самый экономичный по памяти.

Другой распространенный вариант доступа, это когда при обработки конкретной
ячейки растра требуется информация о значениях, содержащихся в соседних ячейках.

В этом режиме доступ к каждой строке растра требуется несколько раз~---
сначала при обработке одной или нескольких предыдущих строк, потом при обработке
самой этой строки, и наконец, при обработке следующих строк. Для таких ситуаций
реализован {\it кэшированный} доступ, при котором несколько строк в 
распакованном виде хранятся в памяти.

И, наконец, для таких приложений как интерактивные редакторы, реализован 
{\it прямой доступ на чтение/запись}. Этот способ доступа существенно медленнее,
чем кэшированный, поскольку строки epp-файла хранятся в памяти в
упакованном виде.

Для упрощения последовательного перебора ячеек существуют функции-% 
{\it итераторы}, которые получают в качестве параметра функцию и
последовательно применяют ее ко всем ячейкам файла.


\section{Вектор}

Векторные файлы EPPL7 (dgt) представляют собой последовательнсть 
объектов~--- линий и точек. 

Установление соотвествия между объектом и координатами представляет 
для них существенно более сложную задачу, чем установление 
соответствия между координатами и классами растра.

Для большинства способов обрабоки векторных файлов тоже достаточно
последовательного перебора объектов. 

В тех случаях, когда требуется, например, перебор всех пар объектов,
можно загрузить векторный файл в память.

Для векторных файлов тоже существуют итераторы, позволяющие применить
переданную функцию ко всем линиям, ко всем точкам или вообще ко
всем объектам в файле.

Логическая структура векторного файла EPPL7 такова:

В его заголовке определяется прямоугольная область в картографических
(альтернативных) координатах, которую покрывает файл.

Внутри файла все координаты хранятся во внутренних единицах, для которых
всегда нижняя и левая границы файла имеют значение $-32767$, а верхняя
и правая~--- $+32767$. Это обеспечивает максимально возможную точность
и компактность, но приводит к проблемам при расчете расстояний и углов, 
и применении других функций аналитической геометрии, поскольку величина
единичного отрезка по оси $X$ и по оси $Y$ не совпадают. 

Все объекты в векторном файле имеют целочисленный (32-битный) идентификатор.
Недопустимым значением для идентификатора объекта является 0, поскольку
точка с нулевыми координатами и нулевым идентификатором используется как
признак конца файла.

Векторный файл EPPL7 может хранить два типа объектов: линии и точки.

Точка имеет две координаты и идентификатор.

Для линии хранится идентификатор, координаты минимального прямоугольника,
заключающего в себе линию, число точек и набор пар координат.

Количество точек в линии не может превышать 500.

Функции библиотеки заботятся о том, чтобы координаты объемлющего 
прямоугольника (маски линии) были корректными.

\chapter{Функции доступа к epp файлам}

Функции переменные, необходимые для доступа к epp-файлам
описаны в файле {\tt epp.h}. В нем описана структура данных
{\tt EPP}, хранящая информацию об открытом epp-файле. 

Внутреннее устройство этой структуры описано в разделе \ref{EPP-internals}.

Практически лезть внутрь этой структуры при программировании
прикладных задач нет необходимости. Вся работа с ней производится
с помощью функций библиотеки.

Эта структура данных хранит наиболее существенную информацию из
заголовка EPP-файла. Значения тех полей которые не хранятся в этой
структуре, можно прочитать или изменить с помощью функций, описанных
в разделе \ref{EPPHEADER}.

\section{Поля структуры EPP}\index{EPP}
В этом разделе описаны только те поля, значения которых достаточно
часто используются при реализации прикладных алгоритмов. Остальные
поля и принципы работы с ними описаны в разделе \ref{EPP-internals}. 
Все поля, про которые не сказано обратного, можно только читать.
Для их изменения используйте соответствующие функции, поскольку
изменение значений некоторых полей может потребовать согласованного
изменения каких-то других.

\index{EPP!fc}\index{EPP!lc}\index{EPP!fr}\index{EPP!lr}%
\index{fr}\index{lr}\index{fc}\index{lc}\index{shift_epp}%
\var{int fr,lr,fc,lc}{ Номера первой строки и колонки, а также значения
на единицу больше последней строки и колонки. На единицу больше они
потому, что в этом случае конструкции {\tt lc $-$ fc} и {\tt lr $-$ fr}
дают количество строк и колонок в файле. Для изменения используется функция
{\tt shift\_epp} }

\index{EPP!Xleft}\index{EPP!YBottom}\index{EPP!XRight}\index{EPP!YTop}
\index{XLeft!EPP}\index{YBottom!EPP}\index{XRight!EPP}\index{YTop!EPP}
\var{double Xleft,YBottom,XRight,YTop}{ альтернативные (карографические)
координаты границ карты.}

\index{EPP!offsite}\index{offsite}%
\var{int offsite}{ значение offsite. Если файл открыт на запись, это
 значение можно менять.}

\index{EPP!cell_area}\index{cell_area}%
\var{double cell\_area}{ площадь ячейки в единицах, указанных в заголовке
файла. См. раздел \ref{EPPHEADER}.}

\index{EPP!kind}\index{kind}%
\var{int kind}{ количество бит на ячейку. Может принимать значения 8 или 16.}


Информация из заголовка файла epp, которая не попала в вышеперечисленный
список полей, в структуре EPP не хранится. Для работы с ней нужно
получить полную копию заголовка файла с помощью функции {\tt get\_epp\_header}%
\index{get_epp_header}.

\index{mode}\index{EPP!mode}\index{MAP_INPUT}\index{MAP_OUTPUT}
\var{int mode}{ режим, в котором открыт файл. Комбинация констант
{\tt MAP\_INPUT} и {\tt MAP\_OUTPUT}.}


\section{Открытие файла и режимы доступа}

\index{open_epp}%
\function{EPP* open\_epp( char *filename );}

Открывает файл с именем {\tt filename} и возвращает указатель на
структуру EPP.  Файл должен существовать. Режим открытия устанавливается
в {\tt MAP\_INPUT}.

\index{fopen_epp}%
\function{EPP* fopen\_epp(FILE *f);}
Открывает файл аналогично {\tt open\_epp}, но получает в качестве 
парамера не имя, а указатель на структуру FILE. Это позволяет 
использовать для передачи растровой информации не только именованные 
файлы на диске. Дескриптор файла f должен допускать позиционирование.

\index{creat_epp}
\function{% 
EPP *creat\_epp(char *pathname, int first\_col, int first\_row, int last\_col,
               int last\_row, double AXLeft,double AYTop, double AXRight, 
               double AYBottom, int scale, int base, int offsite);}


Создает новый epp-файл и открывает его в режиме {\tt MAP\_OUTPUT}.

При создании файла указываются его размеры, альтернативные координаты,
значение offsite и значения полей заголовка {\tt base} и {\tt scale}.

Для создания еpp файле в уже открытом файле UNIX существует функция
{\tt fcreat\_epp}\index{fcreat_epp}, которой вместо имени файла передается указатель
на структуру {\tt FILE}.

Во многих случаях нужно создать выходной файл того же размера и с той
же координатной системой, что и входной. Для упрощения этого процесса
существует функция 
\index{creat_epp_as}%
\function{EPP *creat\_epp\_as(char *filename,EPP *pattern);}

которая создает файл, копируя большую часть информации заголовка из
уже существующего файла, указатель на который передается в
параметре {\tt pattern}. Для нее также существует аналог, получающий
не имя, а открытый файл.

Все эти функции по умолчанию создают 8-битный epp-файл. Точнее,
количество бит определяется значением глобальной переменной
{\tt Create16bit}, которой нужно присвоить ненулевое значение для
того, чтобы создавались 16-битные файлы

\section{Изменение режимов доступа}

При использовании данной библиотеки существующий файл всегда открывается
в режиме последовательного чтения, а новый~--- в режиме последовательной
записи. Для того, чтобы получить другой режим доступа нужно менять режим
доступа явным образом.

\index{reset_epp}%
\function{void reset\_epp(EPP *epp)}

Переокрывает для чтения файл, открытый для последовательной записи.
Все незаполненные строки заполняются offsite.

\index{set_epp_cache}%
\function{int set\_epp\_cache(EPP* epp, int lines);}

Создает кэш на {\tt lines} строк. Если кэш ранее существовал,
изменяет его размер. {\tt set\_epp\_cache(epp,0);} отменяет кэширование
совсем.

\index{load_epp}
\function{int load\_epp(EPP* epp);}

Загружает файл, открытый для чтения в память и меняет его режим
на {\tt MAP\_INPUT\verb!|!MAP\_OUTPUT}\index{MAP_INPUT},\index{MAP_OUTPUT}, т.е. прямое чтение-запись.

\index{save_epp}
\function{int save\_epp(EPP *epp);}

сохраняет изменения в загруженном файле на диск. Старое содержимое
при этом теряется.

\index{save_epp_as}
\function{int save\_epp\_as(EPP *epp,char *newname);}

Сохраняет файл под новым именем. Старый файл при этом закрывается.

\index{fsave_epp_as}
\function{int fsave\_epp\_as(EPP *epp,FILE *f);}
Аналогична {\tt save\_epp\_as}, но получает не имя файла, а уже открытый файл.

Функции сохранения не меняют режима файла. Просто больше некуда было их поместить.

Все функции сохранения и функция {\tt load\_epp} после обработки каждой
строки вызывают функцию, на которую указывает глобальная переменная \index{EndLineProc}%
{\tt EndLineProc} (см.~раздел \ref{iterators}). Ее результат они игнорируют.

\index{close_epp}
\function{void close\_epp(EPP *epp);}

Закрывает файл. Если файл был открыт на последовательную запись, все изменения
сохраняются, а незаполненные строки заполняются offsite.

Если файл был загружен в память, автоматического сохранения {\bf не} производится.
\section{Доступ к ячейкам файла}
Основным способом доступа к ячейкам файла являются функции

\index{epp_get|textbf}%
\function{int epp\_get(EPP* epp,int x,int y);}

и

\index{epp_put|textbf}%
\function{void epp\_put(EPP*epp,int x,int y,int value);}

Функция {\tt epp\_get} возвращает значение ячейки с указанными координатами
(во внутренних единицах файла) или offsite, если данная точка находится
за пределами карты. 

Ее нельзя применить к файлу, открытому для последовательной записи, при
этом возникает ошибка {\tt ME\_INVALID\_MODE}.

При работе с файлом открытым для последовательного чтения, опрашивать
ячейки можно в любом порядке, но обращение к другой строке 
требует ее считывания с диска и распаковки.

При работе с кэшированным или загруженным в память файлом, тем более
можно обращаться к его ячейкам в произвольном порядке.

Функция {\tt epp\_put} при попытке изменить ячейку за пределами
файла генерирует ошибку {\tt ME\_POINT\_OUTSIDE}.

В режиме последовательной записи можно записывать точки внутри текущей
строки в произвольном порядке, а при обращении к строке с номером, 
большим чем текущая, текущая строка сбрасывается на диск, и дальнейшая
запись в нее невозможна. Если произошла запись в строку с номером $n$,
в то время как предыдущая текущая строка имела номер $m$, где $m<n-1$,
все строки от $m+1$ до $n-1$ заполняются offsite.

Попытка записи в строку, уже записанную на диск, вызывает ошибку
{\tt ME\_INVALID\_PUT}.

В файлах, загруженных в память, то есть доступных и для чтения и для
записи, этих ограничений нет.

Для специальных целей, преимущественно для целей визуализации,
реализована функция

\index{epp_get_line}%
\function{unsigned short *epp\_get\_line(int x,int y);}

возвращающая текущую строку как массив {\tt unsigned short int}.
Она возвращает указатель на внутренний буфер структуры EPP, поэтому
освобождать память не нужно. 

Количество элементов массива, имеющих осмысленные значения, вызывающая
программа должна определить сама.

Попытка вызвать эту функцию при {\tt x $<$ epp-$>$fc} приводит к ошибке
{\tt ME\_POINT\_OUTSIDE}, а функция возвращает {\tt NULL}.

\section{Итераторы}
\label{iterators}

\index{for_each_cell}%
\function{int for\_each\_cell(EPP *epp,EPP\_ITER\_PROC action);}

Выполняет функцию {\tt action} для всех не-offsite ячеек существующего
файла или для всех ячеек файла, открытого для последовательной записи.
Возвращает 0, если выполнение завершено успешно, -1 если выполнение
прервано в результате ненулевого результата {\tt EndLineProc}\index{EndLineProc} и -2,
если выполнение прервано самой функцией {\tt action}

Тип {\tt EPP\_ITER\_PROC} определен как


{\tt typedef int (*EPP\_ITER\_PROC)(int col, int row, int value);}


Параметры {\tt col} и {\tt row} передают координаты текущей точки,
параметр {\tt value} содержит класс точки для существующего файла и
offsite для создаваемого.

Возвращаемое значение в случае создаваемого файла записывается в ячейку.

В случае существующего файла нулевое значение соответствует нормальному
состоянию, а ненулевое приводит к прекращению работы.

\function{long count\_cells(EPP *epp, EPP\_ITER\_PROC condition);}

Возвращает количество ячеек в существующем файле, удовлетворяющих
заданному условию. Функция {\tt condition} должна возвращать 0 для
ячеек, которые условию не удовлетворяют и ненулевое значение для
тех, которые удовлетворяют. В случае прерывания работы, возвращает те же
коды, что и {\tt for\_each\_cell}

Параметры, передаваемые функции {\tt condition} такие же, как
у {\tt action} в случае существующего файла.

Итераторы выполняются достаточно долго, поэтому для них предусмотрен
способ выдачи прогресс-индикатора. Если глобальной переменной {\tt EndLineProc}%
\index{EndLineProc|textbf}
присвоено значение, отличное от {\tt NULL}, то функция, на которую она 
указывает, вызывается после окончания обработки каждой строки файла.

Функция получает три целых параметра:
\var{row}{ номер строки epp-файла, с учетом значения поля {\tt fr}}
\var{seqno}{ порядковый номер строки (от 1 до {\tt lr-fr})}
\var{total}{ общее число строк в файле.}

Если эта функция возвращает ненулевое значение, обработка файла прерывается.

Таким образом простейшая функция индикации прогресса (дающая результат,
очень похожий на поведение EPPL7 в аналогичной ситуации), выглядит так:

\begin{verbatim}
int show_progress(int row,int seqno,int total)
{
 fprintf(stderr,"\rProcessing row %d of %d",seqno,total);
 fflush(stderr);
 return 0;
}
\end{verbatim}
 
\section{Работа с заголовком epp-файла}
\label{EPPHEADER}\index{EPPHEADER}
Заголовок epp-файла имеет следующую структуру:
\begin{verbatim}
typedef struct EPPHEADER
 { short int fr,lr,fc,lc;
   double fry,lry,fcx,lcx;
   short int kind;
   short int base,scale;
   unsigned short int offsite; 
   double sfact;
   long access_ptr;
   unsigned short  minclass, maxclass;
   char area_unit;
   char coord_sys;                                   
   char Reserved[6];
   char date[16], time[8];
   char comment[32];
 } EPPHEADER;
\end{verbatim}

Семантика этих полей следующая:
\var{fr,lr,fc,lc}{ совпадают с соответствующими полями структуры {\tt EPP}}
\var{fry,lry,fcx,lcx}{ альтернативные координаты}
\var{kind}{ количество бит на ячейку}
\var{base,scale}{ тайна, покрытая мраком. Я так и не смог уяснить из
  документации к EPPL7, что эти поля делают, и не знаю, какие команды EPPL,
 не считая {\tt HEADER} с ними работают.}
\var{offsite}{ хранит значение offsite.}
\var{sfact}{ площадь ячейки. См. также поле {\tt area\_unit}}
\var{access\_ptr}{ смещение (в 128 байтовых блоках) таблицы длин строк в файле}
\var{minclass,maxclass}{ минимальный и максимальный класс в файле (не считая
offsite}
\var{area\_unit}{ Единица измерения площади.}\index{area_unit}
\var{coord\_sys}{ тип проекции}\index{coord_sys}\index{EPPHEADER!coord_sys} 
\var{date,time}{ дата и время создания файла в текстовом формате.}
\var{comment}{ краткий комментарий к файлу (заголовок карты). Значение
этого поля EPPL7 запрашивает у пользователя при создании каждого файла как
``Description''} 

Возможные значения единиц площади и типов проекции описаны как константы в файле {\tt eppl.h},
который включается и файлом {\tt epp.h} и файлом {\tt dgt.h}
\begin{description}
\item[AREA\_NO\_UNIT] Площадь ячейки не определена; 
\item[AREA\_SQ\_FOOT] Квадратные футы; 
\item[AREA\_SQ\_METER] Квадратные метры; 
\item[AREA\_SQ\_KM] Квадратные километры; 
\item[AREA\_SQ\_MILE] Квадратные мили (видимо, имеются в виду английские, а не
морские);
\item[AREA\_HECTARE] Гектары; 
\item[AREA\_ACRE] Акры. 
\end{description}

Как видите, миллиметры не предусмотрены.

Количество типов проекций очень невелико и показывает ориентацию EPPL7 на
крупномасштабное картографирование. 
\begin{description}
\item[COORD\_NONE] Координатная система не определена (свежее сканерное
изображение);
\item[COORD\_UTM] Американская топографическая карта. (от Гаусса-Крюгера
отличается на 0.2\%, но отличается.
\item[COORD\_STPLANE] Проекция State Plane. Используется, судя по названию,
для карт штатов США. 
\item[COORD\_LL] Географические координаты.   
\end{description}

Для работы с этой информацией предусмотрены следующие функции:
\function{void get\_epp\_header(EPP* epp, EPPHEADER *h);}\index{get_epp_header|textbf}
Возвращает заголовок файла в виде вышеописанной структуры. Используется
для чтения полей, к которым нет более удобного доступа.

\function{char *getcomment(EPP *epp);}\index{getcomment}

Возвращает комментарий, обрезая концевые пробелы. Возвращает
указатель на статический буфер, который будет перезаписан при
следующем обращении к этой функции.

\function{void change\_epp\_header(EPP* epp,EPPHEADER h);}\index{change_epp_header}

Копирует в заголовок файла поля {\tt fcx, lcx, fry, lry, base, scale, offsite,
sfact, coord\_sys, area\_unit, comment} из переданной структуры. Остальные
поля оставляет как были.

\function{void setcomment(EPP *epp,char *comment);}\index{set_comment}

Изменяет только комментарий. В отличие от {\tt change\_epp\_header} не
требует, чтобы строка была дополнена пробелами до 32 символов.
Ее аругумент~--- обычная NULL-terminated строка.



\function{int shift\_epp(EPP* epp, int new\_fr, int new\_fc);}\index{shift_epp}

Изменяет значения полей {\tt fr} и {\tt fc}, и соответственно {\tt lr} и 
{\tt lc}, поскольку число строк и колонок в файле поменять нельзя.

Возвращает ненулевое значение в случае успеха или 0, если какие-то
координаты при изменении вылезли за допустимый диапазон.

\section{Пересчет координат}
\function{int epp\_row(EPP *epp,double y);}\index{epp_row}
Возвращает номер строки по альтернативному y.
\function{int epp\_col(EPP *epp,double x);}\index{epp_col}
Cоответственно, номер колонки по альтернативному x.
\function{double alt\_x(EPP *epp,int col);}\index{alt_x}
Возвращает альтертантивный x указанной колонки.
\function{double alt\_y(EPP *epp,int row);}\index{alt_y}
И альтернативный y указанной строки.

Эти две функции возвращают альтернативные координаты правого верхнего
угла ячейки. Для получения координат центра ячейки предназначены функции:

\function{double alt\_xc(EPP *epp,int col);}\index{alt_xc}
\function{double alt\_yc(EPP *epp,int row);}\index{alt_yc}



\section{Наложение файлов}

Растровые файлы легко накладывать друг на друга, если у них
совпадает размер ячейки и координатная система.

Для проверки этих условий в библиотеку включена функция

\function{int compare\_cell\_size(EPP *f1,EPP *f2);}\index{compare_cell_size}

она возвращает 0 если размеры ячеек несовместимы и ненулевой код,
если файлы можно накладывать.

Второе условие, которое необходимо проверить~--- совпадает ли
правило пересчета альтернативных координат в ряды/колонки.

Для этого предназначена функция 
\function {int is\_aligned(EPP *f1,EPP *f2);}\index{is_aligned}

Она возвращает ненулевой код, если обращение к {\tt epp\_get}\index{epp_get} 
с одинаковыми аргументами для обоих файлов возвращает
информацию об одной и той же точке. 

Несовпадение рамки файлов не играет никакой роли, поскольку {\tt epp\_get}
корректно обрабатывает обращение к точке за пределами файла.

Если {\tt compare\_cell\_size} возвращает TRUE, а {\tt is\_aligned}~---
FALSE, то файлы могут быть выравнены путем изменения номера первой
строки и/или колонки с помощью функции {\tt shift\_epp}.

Если же необходимо совместно использовать файлы, у которых не
совпадает размер ячейки, то необходимо расчитать и использовать 
пересчетные коэффициенты.

Для этого используются функции:

\function{EPP\_LINK link\_epp(EPP *base,EPP *overlay);}\index{link_epp}\index{EPP_LINK}

Устанавливает связь между файлами. Возвращает указатель на 
структуру {\tt LINK\_BUFFER}, содержащую набор пересчетных 
коэффициентов или {\tt NULL}, если связь установить невозможно
(координатные системы двух файлов имеют противоположно направленные
оси).

Функции:

\function{int linked\_row(EPP\_LINK link,int row);}\index{linked_row}
\function{int linked\_col(EPP\_LINK link,int col);}\index{linked_col}

пересчитывают ряды/колонки файла {\tt base} в ряды/колонки файла {\tt overlay} 


\section{Прочее}
В некоторых случаях до окончания создания файла нельзя определить,
есть ли в нем классы свыше 255. В этих случаях приходится всегда 
создавать 16-битный файл, а по окончании работы, если $\mbox{max}\le 255$ конвертировать
его в восьмибитный. Для упрощения (и ускорения) этой операции, 
в библиотеку включена функция: 

\function{int fast\_convert\_to\_8bit(EPP *source, char *filename);}%
\index{fast_convert_to_8bit}

Эта функция получает открытый на последовательную запись файл, 
переоткрывает его на чтение и копирует в новый файл, указанный как filename.

Функция работает
так быстро, как только возможно, поскольку использует напрямую
некоторые возможности библиотеки, не вынесенные в интерфейс.

Для сведения читателей, имеющих опыт работы с epp-файлом на низком
уровне, укажем, что запаковки она не выполняет вообще, а распаковывает
только младшие 8 бит 16-битного файла.

По окончании работы закрывает оба файла.

\section{Обработка ошибок}

Большая часть функций библиотеки корректно обрабатывает 
ошибочные ситуации. Достаточно часто индикатором ошибки служит
результат, возвращаемый функцией.

Существует глобальная переменная {\tt map\_error}\index{map_error} в которую помещается
код ошибки. Возможные коды определены как константы в файле {\tt epp\_err.h}.

\var{ME\_OK}{операция завершилась успешно.}
\var{ME\_POINT\_OUTSIDE}{Попытка сделать {\tt epp\_put} за пределами файла.}
\var{ME\_INVALID\_MODE}{Попытка читать карту, открытую в режиме последовательной
записи, или модифицировать карту, открытую в режиме последовательного чтения}
\var{ME\_READ\_ERROR}{Ошибка системного вызова или библиотечной функции при
чтении с диска. Рекомендуется посмотреть значение глобальной перемнной {\tt errno}.}
\var{ME\_WRITE\_ERROR}{То же самое, но при записи.}
\var{ME\_INVALID\_PUT}{Попытка изменить ячейку, уже записанную на диск.}
\var{ME\_OUT\_OF\_MEMORY}{Не хватило памяти для размещения необходимых структур данных}
\var{ME\_ACCESS\_DENIED}{Не удалось открыть файл в требуемом режиме}
\var{ME\_NO\_FILE}{Файл с указанным именем не существует}
\var{ME\_INVALID\_FILE}{Файл не является правильным epp или dgt файлом}
\var{ME\_CREATE\_ERROR}{Ошибка создания файла, не существует путь или
 недопустимое имя. Подробности~--- в системной переменной {\tt errno}.}
Если при какой-то операции {\tt map\_error} установлена в ненулевое значение,
то имеет смысл перед продолжением работы ее обнулить. Некоторые функции 
библиотеки проверяют его после вызова других функций, а обнуление после
успешной операции не гарантировано.

\chapter{Функции доступа к векторным файлам}

Функции доступа к векторным файлам описаны в заголовочном файле {\tt dgt.h}.
В нем определена структура {\tt DGT}, аналогичная по смыслу структуре {\tt EPP}.

Поскольку в заголовке векторных файлов никакой информации кроме альтернативных
координат и типа проекции не хранится, способов прямой работы с заголовком
не предусмотрено.

\section{Tипы данных}

В отличие от растровых файлов, значением которых в точке является 
обычное целое число, векторные файлы хранят довольно сложные объекты.

Приведем их описания:
\index{POINT}%
\var{POINT}{Структура для хранения координат точки. Имеет два поля
типа {\tt signed short int}~--- {\tt x} и {\tt y}. Несмотря на схожесть
описания со структурой {\tt XPoint}, попытка передать массив точек типа
{\tt POINT} в функцию {tt XDrawLine} почему-то к успеху не приводит}
\index{DGT_ITEM}
\var{DGT\_ITEM}{Структура для хранения информации о векторном объекте~---
полилинии или точке (не путать точку-объект с точкой-узлом линии)).
Ее поля:}
\var{long int ID}{ 32-битный идентификатор объекта.}
\var{short int x1,y1}{ левый нижний угол маски, если объект~--- полилиния или координаты
точки, если объект~--- точка.}
\var{short int x2,y2}{ правый верхний угол маски. У точек~--- не используется.}
\var{shott int npoints}{ число узлов в линии. Если 0, то объект~--- точка, 
от 2 до 500~--- полилиния, остальные значения недопустимы.}
\var{POINT s[500]}{ массив координат точек полилинии.}

Структура {\tt DGT}\index{DGT|textbf} хранит всю информацию, необходимую при работе с векторным
файлом. Программисту имеет смысл непосредственно читать следующие ее поля:
\index{DGT!XLeft}\index{DGT!XRight}\index{DGT!YTop}\index{DGT!YBottom}%
\index{XLeft!DGT}\index{XRight!DGT}\index{YTop!DGT}\index{YBottom!DGT}%
\var{double XLeft,YBottom,XRight,YTop}{ альтернативные координаты границ
файла.}
\index{mode!DGT}%
\var{int mode}{ Режим доступа. Комбинация тех же констант {\tt MAP\_INPUT} и
{\tt MAP\_OUTPUT}, что и поле {\tt mode} структуры {\tt EPP}.}
\index{projection!DGT}%
\var{unsigned char projection}{Тип координатной системы. Возможные значения
описаны в разделе \ref{EPPHEADER}.}
\var{DGT\_ITEM *buffer}{ Буфер для хранения текущего объекта. Обычно доступ
к этой структуре выполняется с помощью макросов, описанных в разделе \ref{ItemMacros}.}
\var{int eof}{ Флаг, выставляемый в ненулевое значение, если прочитан признак
конца файла.}
\var{int item\_no}{ Порядковый номер текущего объекта}
\section{Открытие файла}

\index{open_dgt}%
\function{DGT *open\_dgt(char *filename)}
\index{fopen_dgt}%
\function{DGT *fopen\_dgt(FILE *f)}

Открывают существующий файл для последовательного чтения. Возвращают указадель
на структуру {\tt DGT} или {\tt NULL}, если произошла ошибка.

\index{creat_dgt}%
\function{DGT *creat\_dgt(char *filename, double x\_left, double y\_bottom,
double x\_right, double y\_top, unsigned char proj)}
\index{fcreat_dgt}%
\function{DGT *fcreat\_dgt(FILE *f,  double x\_left, double y\_bottom,
double x\_right, double y\_top, unsigned char proj)}
\index{mcreat_dgt}%
\function{DGT *mcreat\_dgt(double x\_left, double y\_bottom,
double x\_right, double y\_top, unsigned char proj)}

Создают файл для последовательной записи. Получают в качестве параметров
пределы альтернативных координат и тип проекции. 

Особое внимание стоит обратить на функцию {\tt mcreat\_dgt}, которая
создает пустой файл в памяти, открытый на чтение-запись. Подробнее
работа с файлами в памяти описана в разделе \ref{dgtInMemory}

Для открытия файлов с пределами и проекцией аналогичными уже существующему
файлу, существуют функции:

\index{creat_dgt_as}%
\function{DGT *creat\_dgt\_as(char *filename,DGT *pattern);}
\index{fcreat_dgt_as}%
\function{DGT *fcreat\_dgt\_as(FILE *f,DGT *pattern);}
\index{mcreat_dgt_as}%
\function{DGT *mcreat\_dgt\_as(DGT *pattern);}

\section{Смена режима и закрытие файла}

Функция
\index{reset_dgt}%
\function{void reset\_dgt(DGT* dgt);}
делает текущим объектом первый. Если файл до этого был открыт в режиме
последовательной записи, он переоткрывается в режиме последовательного
чтения. 

\index{load_dgt}%
\function{int load\_dgt(DGT *dgt);}
Загружает файл в память, делая возможным прямой доступ на чтение-запись
и многие другие возможности, описанные в разделе \ref{dgtInMemory}

\index{close_dgt}%
\function{void close\_dgt(DGT *dgt);}
Закрывает файл, освобождая всю выделенную под него память. Изменения
в последнем объекте сохраняются, если файл был открыт для последовательной
записи.

\section{Чтение и запись объектов}

После открытия существующего файла, первый объект автоматически считывается
в буфер, и доступен с помощью макросов для работы с буфером (см. раздел
\ref{ItemMacros}).

Для перехода к следующему объекту вызывается макрос
\index{dgt_next}%
\function{void dgt\_next(DGT *dgt);}

В некоторых случаях удобно работать с копиями объектов, размещенных
в динамической памяти. В этом случае можно использовать
функцию

\index{dgt_get}%
\function{DGT\_ITEM *dgt\_get(DGT *dgt)}

Которая размещает текущий объект в памяти, перемещается на следующий
объект и возвращает указатель копию объекта, бывший текущим до ее вызова.

Для записи в файл можно использовать либо функцию

\index{dgt_put}%
\function{void dgt\_put(DGT *dgt, DGT\_ITEM *item);}
либо модифицировать текущий объект с помощью макросов и функций для
работы с текущей записью и затем вызвать {\tt dgt\_next} для перехода
к новому объекту.
\section{Итераторы}
Поскольку векторные файлы хранят два типа объектов~--- линии и точки,
и только очень немногие алгоритмы (в основном трансформация координат)
затрагивают и те и другие, определены итераторы:
\index{for_each_line}%
\function{for\_each\_line(DGT *dgt,void (*item\_proc)(DGT *dgt),
 DGT *outstream);}
\index{for_each_point}%
\function{for\_each\_point(DGT *dgt,void (*item\_proc)(DGT *dgt),
DGT *outstream);}

Они вызывают указанную функцию только для линий или точек файла.
Функции передается указатель на структуру {\tt DGT} в буфере
которой находится объект.

Эти итераторы можно применять к файлам, открытым для последовательного
чтения или к файлам загруженным в память.

Если параметр {\tt outstream} не {\tt NULL}, то в файл, на который
он указывает, копируются все объекты, которые не обрабатывались и
обработанные объекты.

То есть, например, в случае {\tt for\_each\_line} точка будет скопирована
без обработки, а линия записана после того, как ее изменит {\tt item\_proc}.

Кроме этого, существует итератор
\index{for_each_item}
\function{void for\_each\_item(DGT *dgt,void (*item\_proc)(DGT *dgt));}

который выполняет переданную функцию и для линий и для точек. 
У него нет параметра {\tt outstream}, так что если нужно записать
измененные объекты, {\tt item\_proc} должна об этом позаботиться сама.
\section{Работа с текущим объектом}
\label{ItemMacros}
Итак, нужная вам линия (точка) находится в буфере структуры {\tt DGT}.
Что можно делать с ней дальше?

Во-первых, нужно уметь определять, линия это или точка.
Для этого определены макросы:
\index{dgt_is_line}%
\function{int dgt\_is\_line(DGT *dgt)}
\index{dgt_is_point}%
\function{int dgt\_is\_point(DGT *dgt)}
Во-вторых, независимо от того, линия это или точка,
нам может потребоваться идентификатор объекта.
\index{dgt_id}%
\function{long int dgt\_id(DGT *dgt)}
В-третьих, координаты точки:
\index{dgt_pointx}%
\function{short int dgt\_pointx(DGT *dgt)}
\index{dgt_pointy}%
\function{short int dgt\_pointy(DGT *dgt)}
или координаты прямоугольника-маски линии:
\index{dgt_xl}%
\function{short int dgt\_xl(DGT *dgt)}
\index{dgt_yb}%
\function{short int dgt\_yb(DGT *dgt)}
\index{dgt_xr}%
\function{short int dgt\_xr(DGT *dgt)}
\index{dgt_yt}%
\function{short int dgt\_yt(DGT *dgt)}
В-четвертых, количество точект в линии:
\index{dgt_line_len}%
\function{short int dgt\_line\_len(DGT *dgt)}
И, наконец, координаты $i$-того узла линии:
\index{dgt_nx}%
\function{short int dgt\_nx(DGT *dgt,int i)}
\index{dgt_ny}%
\function{short int dgt\_ny(DGT *dgt,int i)}
Обратите внимание, что первая точка линии имеет номер 0, а последняя~---
{\tt dgt\_line\_len(dgt)-1}.

Кроме того, иногда удобнее получить ссылку на точку или узел линии,
как на структуру {\tt POINT}. Для этого предназначены макросы:
\index{dgt_node}%
\function{POINT dgt\_node(DGT *dgt,int i)}
\index{dgt_point}%
\function{POINT dgt\_point(DGT *dgt)}

Если вам по каким-то причинам нужно работать не со структурой {\tt DGT},
а с отдельным объектом, размещенным в динамической памяти, то замените
префикс {\tt dgt\_} на префикс {\tt item\_} и в качестве первого 
параметра указывайте указатель на структуру {\tt DGT\_ITEM}\index{DGT_ITEM}.

Теперь мы можем прочитать любой параметр объекта. Вопрос в том, как его
изменить.

Ответ первый, парадоксальный: любой из вышеприведенных макросов (кроме
{\tt dgt\_is\_line}\index{dgt_is_line} и {\tt dgt\_is\_point})\index{dgt_is_point} можно использовать в {\bf левой}
части оператора присваивания. 

Ведь это не функции, а макросы, дающие доступ к некоторым полям структуры
{\tt DGT\_ITEM}.

Однако так поступать не рекомендуется. Дело в том, что в таком случае
нигде не будет отмечено, что объект изменен, поэтому изменения могут быть
не сохранены. Если уж очень хочется, то после прямого изменения полей
объекта, используйте функцию

\function{void dgt\_touch(DGT *dgt)}\index{dgt_touch}
чтобы пометить объект как измененный.

Но лучше пользоваться следующими функциями:

\function{void dgt\_set\_id(DGT *dgt,long int newID);}\index{dgt_set_id}
Устанавливает идентификатор объекта.
\function{void dgt\_set\_point(DGT *dgt, int x, int y);}\index{dgt_set_point}
Задает координаты точки и делает объект точкой. Если вы хотите сохранить
одну координату, вместо нее передайте константу {\tt KEEP\_OLD\_VALUE}%
\index{KEEP_OLD_VALUE}
\function{int dgt\_add\_node(DGT *dgt,int x,int y);}\index{dgt_add_node}
Добавляет точку в конец линии (или создает первую точку и помечает 
объект как линию. Возвращает 0 в случае, если все в порядке или
ненулевое значение, если точку добавить невозможно (уже есть 500 точек).
\function{int dgt\_ins\_node(DGT *dgt,int index,int x,int y)}\index{dgt_ins_node}
Вставляет точку в линию перед узлом номер {\tt index}. Возвращает 0,
если все в порядке, 1 если число точек превысит 500, и 2 если узла с таким
номером нет. 
\function{int dgt\_mv\_node(DGT *dgt,int index,int x,int y)}\index{dgt_mv_node}
Меняет координаты указаннокго узла на заданные. Возвращает 0, если
все в порядке и ненулевое значение, если такого узла нет.

Если вам надо изменить только одну координату, передайте вместо второй
константу {\tt KEEP\_OLD\_VALUE}\index{KEEP_OLD_VALUE}.

\function{int dgt\_rm\_node(DGT *dgt,int index)}\index{dgt_rm_node}
Удаляет указанный узел. Ошибка возникает, если это был предпоследний узел
в линии. (Корректная линия должна содержать не менее двух узлов).

И, наконец, разрезание линий.

\function{int dgt\_split(DGT *dgt,int index)}\index{dgt_split}
Разрезает линию в указанном узле. При этом 
узел дублируется и попадает в обе половинки. Текущим становится хвост линии,
а начало, если файл был открыт в режиме последовательной записи, сбрасывается
на диск. Этой функцией нельзя пользоваться из итератора {\tt for\_each\_line}\index{for_each_line},
если он применяется к файлу, открытому в режиме последовательного чтения.

\function{int dgt\_cut\_segment(DGT *dgt,int index)}\index{dgt_cut_segment}
Разрезает линию, удаляя отрезок от $n-1$ до $n$-ного узла. Все остальное
в точности как в предыдущем случае.

Процедура {\tt dgt\_next}\index{dgt_next} позаботится о некоторых из ошибок, которые
можно допустить при редактировании объекта.
\begin{enumerate}
\item Маска линии будет скорректирована.
\item Из двух соседних узлов с совпадающими координатами будет оставлен один.
\item Линии из одной точки будут выброшены.
\item Точка с нулевым идентификатором будет выброшена.
\end{enumerate}

\section{DGT-файлы в памяти}

\label{dgtInMemory}
Векторный файл, загруженный в память, можно рассматривать как массив
объектов. Последовательный доступ продолжает работать, но существуют
функции, позволяющие сделать текущим объектом любой, независимо
от порядка и обращаться к объектам, не делая их текущими.

Последний способ имеет существенный недостаток~--- если вы хотите
изменить длину линии, то вам придется самому позаботиться о выделении
памяти под новую линию.

Итак, процедуры, специфичные для файла в памяти:

\function{int dgt\_seek(DGT *dgt,int index);}\index{dgt_seek}

Делает текущим объект с указанным номером. (после чего можно
спокойно его менять с помощью любых функций и макросов предыдущего раздела.)

\function{DGT\_ITEM* dgt\_item\_ptr(DGT *dgt,int index);}\index{dgt_item_ptr}
%
Возвращает указатель на объект с указанным номером. Для доступа
к этому объекту можно использовать макросы {\tt item\_\dots}.

Этот способ доступа рекомендуется использовать как доступ только для чтения.
В крайнем случае, если необходимо изменить не текущую линию, воспользуйтесь
функцией:
\function{int dgt\_replace(DGT *dgt,int index,DGT\_ITEM* new\_item);}\index{dgt_replace}

Эта функция получает указатель на динамически размещенный объект
и помещает его в файл. Для того, чтобы сэкономить память, 
размещайте объект с помощью функции 
\function{DGT\_ITEM *alloc\_item(DGT\_ITEM *item);}\index{alloc_item}
Эта функция получает указатель на объект, выделяет памяти ровно столько,
сколько нужно, чтобы разместить этот объект, и копирует его туда.

Функции вставки объекта в указанном месте в библиотеке нет.
При добавлении объекта с помощью {\tt dgt\_put}\index{dgt_put} его место определяется
в соответствии с текущим порядком сортировки. Если порядок сортировки
вообще не задавался, то это будет следующий объект после текущего,
и он станет текущим.

Порядок сортировки dgt файла в памяти можно изменить при 
помощи функции 
\function{void dgt\_sort(DGT *dgt,int (*compare\_func)(DGT\_ITEM* i1, DGT\_ITEM * i2));}\index{dgt_sort}

Функция {\tt compare\_func} должна возвращать 1, если i1 надо разместить
позже, чем i2, -1 если наоборот, и 0, если по данному критерию сортировки
эти объекты равны.

Изменение критерия сортировки при уже загруженном файле приводит
к его пересортировке с помощью функции {\tt qsort}.

Поэтому лучше вызывать {\tt dgt\_sort} {\bf непосредственно перед}{\tt load\_dgt}.


Вызов {\tt dgt\_sort(dgt,NULL)} приводит к отмене сортировки. 
Порядок расположения существующих объектов не меняется, но новые добавляемые
объекты будут располагаться без учета функции сортировки.
 
\section{Работа с координатами}
Для пересчета координат из внутреннего представления dgt-файла в 
альтернативные и обратно применяются функции:
\function{int dgt\_x(DGT* dgt,double x);}\index{dgt_x}
\function{int dgt\_y(DGT* dgt,double y);}\index{dgt_y}

\function{double real\_x(DGT* dgt,int x);}\index{real_x}
\function{double real\_y(DGT* dgt,int y);}\index{real_y}
Достаточно часто возникает задача пересчета интервалов, которая может
быть выполнена с меньшим числом операций, чем вычисление собственно
координат.
Для этого определены функции
\function{double real\_dx(DGT *dgt,int dx);}\index{real_dx}
\function{double real\_dy(DGT *dgt,int dy);}\index{real_dy}
И, наконец, вычисление расстояний.
\function{double dgt\_dist(DGT *dgt, int x1, int y1, int x2, int y2);}\index{dgt_dist}
Вычисляет расстояние между двумя точками заданными парами внутренних
координат в единицах альтернативных координат.

Для вычисления расстояния между двумя узлами линии можно
использовать функцию
\function{double dgt\_pdist(DGT *dgt, POINT p1, POINT p2);}\index{dgt_pdist}

Во многих алгоритмах расстояния используются только для сравнения
между собой (и, возможно, с некоторыми константами).
В этих случаях можно избежать не только перевода расстояния в осмысленные
единицы, но и извлечения корня и свести все вычисления к операциям с
целыми.

Для этого предназначены функции 
\function{long int sq\_dist(DGT *dgt,int x1, int y1, int x2, int y2);}\index{sq_dist}
\function{long int sq\_pdist(DGT *dgt,POINT p1, POINT p2);}\index{sq_pdist}
вычисляющие квадрат расстояния между точками в некоторых условных единицах.
(известно, что квадрат при $x>=0$~--- функция монотонная).

Для того, чтобы иметь возможность сравнивать результаты функции {\tt sq\_dist}
с константами, заданными изначально как расстояния в альтернативных координатах,
используется функция 
\function{long int sq\_const(DGT *dgt,double dist);}\index{sq_const}
Она переводит расстояние, заданное в единицах альтернативных координат,
в условное значение, аналогичное возвращаемым {\tt sq\_dist}

Кроме расстояний, несоответствие единиц координат по осям влияет и на 
 на вычисление углов. Поэтому в библиотеку включена функция

\function{double dgt\_atan2(DGT *dgt,int dy, int dx);}\index{dgt_atan2}

вычисляющая арктангенс отношения {$\Delta y\over \Delta x$} с поправкой на координатную
систему указанного файла. В остальном ее действие полностью эквивалентно
библиотечной функции {\tt atan2}.

\chapter{Функции-утилиты}
\label{utils}
В библиотеку включен ряд функций, которые не работают непосредственно
с файлами EPPL7, но полезны при написании маленьких командно-строчных
программ для работы с этими файлами.

В отличие от остальной библиотеки, эти функции системно-зависимы.
При переносе библиотеки в другую операционную среду их требуется
переписать полностью и может быть даже изменить их набор.

Например, стандартные библиотеки большинства компиляторов C под DOS
и Windows не включают функции {\tt getopt}, которая активно
используется в Environmental Planning Utilities.

Функции этой группы описаны в заголовочном файле {\tt <eppl\_ut.h>}.

\section{Обработка имен файлов}

\function{char *default\_ext(const char *name, const char *ext);}%
\index{default_ext}

проверяет, оканчивается ли имя файла на указанное расширение. 
Если нет, то добавляет его. 

{\small \it Очевидно, что в файловых системах FAT и HPFS, где несколько
расширений недопустимы, эта функция должна проверять наличие не данного,
а любого расширения.}

\function{char *force\_ext(const char *name, const char *ext);}
\index{force_ext}

заменяет последнее расширение на указанное, или добавляет это расширение,
если имя файла не содержало точки.

\function{char *last\_ext(const char *name);}
\index{last_ext}
 Возвращает последнее расширение файла или {\tt NULL}, если у файла
нет расширения.

Все эти три функции возвращают указатель на статический буфер, 
который будет перезаписан при следующем обращении к ним.

\function{FILE *lookup\_file(const~char~*name, const~char~*sufux,
const~char~*dir);}
\index{lookup_file}

Ищет файл с указанными именем в текущей директории, в ее поддиректории
{\tt dir}, если таковая имеется и в поддиректории {\tt dir} 
системной библиотечной директории {\tt /usr/local/lib/fgis}.

Возвращает указатель на структуру {\tt FILE}. Файл открывается 
только для чтения.

Функция предназначена для поиска вспомогательных файлов, таких как
палитры и штриховки.

\section{Прогресс-индикатор}

В библиотеку также включена стандартная функция вывода сообщения
{\tt Processing row ... of ...}.

\function{int show\_progress(int rowno,int seqno,int nrows);}
\index{show_progress}%
Она выводит это сакраментальное сообщение на {\tt stderr}.
Кроме этого, данная функция обрабатывает {\tt SIGINT}, 
поэтому прерывание программы по Ctrl-C приводит к корректному 
ее завершению.

Она имеет аналог

\function{int show\_percent(int rowno,int seqno,int nrows);}\index{show_percent}
который выводит сообщение вида {\tt 99.9\% completed}.
В отличие от {\tt show\_progress} эта функция может 
использоваться и для dgt-файлов. Кроме того, на больших файлах
она несколько экономит время, поскольку пытается вызывать
медленные операции ввода-вывода, только если есть прогресс 
по крайней мере на 0.1\%, что может составлять до 30 строк.

В то же время даже на файлах максимального размера большая
часть операций будет выполняться с такой скоростью, что за
0.1\% времени работы пользователь не успеет решить, что программа зависла.


Кроме того, существует функция
\function{int check\_int(int rowno,int seqno,int nrows);}
\index{check_int}%
которая ничего не выводит, а только проверяет сигнал.
Рекомендуется всегда иметь возможность использовать ее в качестве
альтернативы {\tt show\_percent}, например для фонового выполнения.

Устанавливается одна из этих функций с помощью функции

\function{void install\_progress\_indicator(int (*func)(int,int,int));}%
\index{install_progress_indicator}

Функция 

\function{int clear\_progress(int result);}\index{clear_progress}
Стирает сообщение, сделанное функцией~--- прогресс-индикатором и выводит
сообщение {\tt Done}, если {\tt result = 0} или {\tt Aborted}.

Возвращает {\tt result}, поэтому логичный способ ее применения
такой:

\begin{verbatim}
install_progress_indicator(show_percent);
if ((res=clear_progress(for_each_cell 
                          (infile,my_iterator_func))))
  unlink(out_file_name);
close_epp(infile);
close_epp(outfile);
return res;
\end{verbatim}

\chapter{Внутреннее устройство библиотеки}
\section{Устройство растрового файла EPPL7}
\section{Работа объекта EPP}
\label{EPP-internals}
\section{Векторные объекты}
\section{Векторные файлы в памяти}
\catcode`\_=11
\input lib_doc.ind
\end{document}


