Описание структуры нового формата файлов для слоёв карты MapCad. Основные проблемы, которые требуется решить с введением нового формата. При большом количестве слоёв в карте получается слишком много файлов, что существенно замедляет работу программы, особенно в сети, усложняет процесс администрирования данных. Поэтому решено отказаться от концепции один слой – один набор файлов и перейти к системе, когда один набор файлов может содержать произвольное количество слоёв карты. Также на практике бывает неудобным ограничение, когда в каждом слое могут содержаться только объекты одного типа. Поэтому в данной версии в слое можно хранить объекты любых типов, а ограничение на типы объектов, которые оператор может сохранить в слое, задавать на уровне администрирования при создании структуры карты. Основные типы поддерживаемых объектов: 1. Контур с произвольным числом точек. 2. Линия с произвольным числом точек. 3. Геометрическая фигура (круг, эллипс, квадрат, прямоугольник, правильные многоугольники, звёзды). 4. Символ (масштабируемый или внемасштабный). 5. Текст (однострочный, многострочный, со сложной траекторией). 6. Текст из базы данных. 7. Геодезические точки. 8. Топологический контур (опирающийся на геодезические точки). 9. Топологическая линия (опирающаяся на топологические точки). 10. Топологический символ (стоящий на геодезической точке). 11. Размерная линия. 12. Растровое изображение (из стандартного файла bmp, jpg, tiff). 13. Составной объект, является объединением нескольких примитивов, в том числе разных типов. При этом в зависимости от настроек примитивы разных типов могут, например, отображаться при разных масштабах. В частности, дороги или реки могут при крупных масштабах быть площадными объектами, а при мелких преобразовываться в линейные. Объединение их в один объект позволяет иметь для них в данных карты один единый идентификатор для связи с внешними базами данных. Слои, объединённые в одном файле носят название «покрытие». Внутри покрытия все объекты имеют собственный уникальный номер int64, который используется для внутренних операций, а также уникальный GUID, который используется для операций синхронизации данных. Счётчик объектов хранится в списке базовых параметров покрытия и при необходимости может быть пересчитан (выставлен по максимальному значению в покрытии). Общая структура файлов покрытия. *.mdp – базовые параметры покрытия. В начале следует блок с базовыми параметрами покрытия, потом список слоёв покрытия, потом все необходимые служебные таблицы и данные для данного покрытия, в том числе таблица пользователей и организаций, таблицы типов и т.п. Все эти данные меняются достаточно редко, поэтому загружаются и сохраняются через обычный поток друг за другом целиком. Прямой доступ к отдельным элементам данного файла не используется. При работе в сети в случае изменения каких-то базовых данных слоя на данный файл ставится блокировка по записи. Если установить блокировку по записи не удалось, то это означает, что какие-то из основных данных находятся в состоянии редактирования другим пользователем, поэтому процедура начала редактирования прерывается и пользователю выдаётся соответствующее сообщение. *.mdi – записи индекса блоков. Все записи имеют фиксированный размер и жёсткую структуру, поэтому и при чтении, и при записи возможен прямой доступ к конкретной записи файла. Индексные записи относящиеся к одному слою идут друг за другом одной цепочкой. Чтение файла производится целиком, запись только изменившихся индексных записей. Поскольку в индексной записи сохраняется информация о моменте последней корректировки блока, то запись индексных записей может происходить достаточно часто (фактически после каждой операции внесения изменений в покрытие). Поэтому перезаписывать весь индексный файл целиком нежелательно. Перед началом разделения блока данных делается попытка заблокировать файл индекса для записи. Если это удалось, то производится соответствующая операция. Если установить блокировку не удалось, то считаем, что данная операция уже выполняется кем-то из пользователей. После выполнения нескольких попыток установить блокировку, между которыми случайным образом задаются паузы от 0.05 до 0.2 сек, пользователю выдаётся сообщение о том, что в данный момент операция невыполнима и предлагается либо её повторить, либо отменить. *.mdd – файл блоков данных, в которых содержится информация об объектах. При работе всегда считывается или записывается только конкретный блок данных. Поскольку запись с основными параметрами объекта имеет фиксированную длину, то и блоки данных также имеют фиксированную длину, что необходимо для организации прямого доступа к блокам в файле без дополнительных накладных расходов. Позиция блока в файле всегда легко вычисляется путём умножения порядкового номера блока на размер блока с добавлением длины служебного заголовка файла. *.mdb – файл расширения данных объектов, в которых содержится дополнительная информация об объектах. *.mda – архив старых копий объектов (журнал изменения данных покрытия). *.mpl – файл списка покрытий, обычный текстовый файл, в котором каждая строка хранит ссылку на файл .mdp покрытия карты. В случае использования относительного задания пути к файлам покрытий в качестве базового берётся путь, где находится сам файл списка. Все записи в файле mdd имеют фиксированный размер для объектов любых типов. При этом в данном файле хранится только самая основная информация об объекте, которая нужна при выполнении массовых операций отрисовки и поиска данных, и на основании которой можно определить нужно ли подгружать остальные данные по данному объекту. Хранение расширенных данных организуется по принципу хранения blob объектов в базах данных, когда данные об объекте занимают некоторое количество байт в одном или нескольких смежных блоках данных специального блочного файла, у которого все блоки имеют фиксированную длину. Файл mdi хранит базовые параметры покрытия, таблицы пользователей и организаций, которые работали с данным покрытием, прочие служебные редко изменяемые таблицы и записи, после которых хранится список слоёв покрытия, к каждому из которых прикрепляется список его блоков данных (пространственный индекс). Данный файл всегда загружается целиком, а записываться может по частям, в зависимости от того, что изменилось. Частичная запись используется только при изменении данных какого-либо пространственного блока, которые не привели к его разделению на два новых. В этом случае в файл записываются только данные об этом блоке. Во всех остальных случаях происходит полная перезапись данного файла. В записи о блоке данных кроме пространственных индексов также хранится информация о его последнем изменении. Благодаря этому можно узнать какие из блоков данных в основном файле данных изменились, и загружать не все данные файла, а только изменившиеся блоки. Это в целом должно уменьшить объём данных, который необходимо будет загрузить при изменении данных покрытия другим пользователем, даже не смотря на то, что размер самого индексного файла увеличится. Структура записи в индексе 3D: CenterBox: TMapBox; ObjectBox: TMapBox; EditDate: TDateTime; Структура записи в индексе 2D: CenterBox: TMapFrame; ObjectBox: TMapFrame; EditDate: TDateTime; - область центров объектов, по которой и строится индекс. - область размещения объектов блока (у разных блоков могут перекрываться) - дата и время последнего изменения данных в блоке. - область центров объектов, по которой и строится индекс. - область размещения объектов блока (у разных блоков могут перекрываться) - дата и время последнего изменения данных в блоке. При этом для любого из слоёв можно выбрать режим построения индексов 2D или 3D. Базовый набор параметров для объекта: OID: int64 DBLink: int64 ObjType: int32 State: int32 ObjBlock: int32; ObjPos: int32; ObjSize: int32; StyleCode: int32; - идентификатор объекта в покрытии 64 - код связи с базой данных 64 - тип объекта 32 - флаги состояния объекта 32 - номер блока с расширенными параметрами объекта 32 - смещение объекта в блоке 32 - размер данных объекта 32 - код стиля объекта 32 Поле ObjType делится на две части. Первые 4 бита указывают базовый тип объекта, а остальные производный тип объекта, который порождается от базового. Это позволяет без лишних операций определить как данный тип объектов можно обрабатывать. При этом в системе может быть определена система типов объектов с наследованием свойств. Для того, чтобы получить дополнительную информацию о полях объекта данного типа, а также о типе класса-предка, нужно обращаться к справочнику типов объектов. Система типов объектов внутри ГИС строится следующим образом. В основе всех типов объектов лежит тип MapObject, в котором описываются встроенные в систему свойства объекта, которые хранятся непосредственно в самом файле данных слоя. От MapObject унаследованы 13 базовых типов объектов, из которых уже в принципе можно строить изображения и простые карты, в том числе при конвертировании данных из других систем, когда точный тип объектов нам не известен, и будет определён позднее. Далее, от базовых типов объектов мы порождаем внутреннюю иерархию типов, используемых внутри системы, которые отличаются от базовых тем, что для них на уровне типов задаются правила рисования на картах, а также дополнительные семантические поля данных, необходимых для объектов данного типа. Эти дополнительные поля данных могут храниться как во внутреннем формате в наборе файлов данных покрытия, так и во внешней базе данных, в том числе на SQL сервере, с которыми система умеет работать (BDE, ADO, IB/FB). При наследовании типов в соответствующих таблицах будут содержаться только те поля, которые отсутствуют у предков. Унаследованные поля сохраняются в таблицах данных, созданных для типов-предков (при этом, видимо, нужно сделать служебное поле, которое бы указывало в таблице типа предка на то, является ли данная запись конечной для объекта данного типа, или является наследуемой для типа-наследника). Для ускорения работы для некоторых объектов часть данных хранится в наборе базовых параметров: Для символов Angle: double Scale: double X, Y, Z: double ClassPoint: int32 - угол поворота - масштаб знака - координата точки знака - тип точки 64 64 3*64 = 192 32 Для линий и контуров MaxX, MaxY, MaxZ MinX, MinY, MinZ: double - обрамляющий прямоугольник объекта 6*64 = 256 CountPoint: int32 32 X1, Y1, Z1: double 3*64 = 192 ClassPoint1: int32 32 X2, Y2, Z2: double 3*64 = 192 ClassPoint2: int32 32 X3, Y3, Z3: double 3*64 = 192 ClassPoint3: int32 32 X4, Y4, Z4: double 3*64 = 192 ClassPoint4: int32 32 Если точек больше 4-х, то остальные хранятся в блоке расширенных параметров, либо если к первым точкам имеются дополнительные параметры, то тогда вставляется специальный флаг в поле State и все точки загружаются из блока дополнительных параметров. Поле ClassPoint: int32 хранит класс точности точки, который позволяет определить с какой точностью и на основании чего данная точка была получена. При этом первые 4 бита хранят тип координирования точки, следующие 4 бит хранят класс работ по плановому положению, далее 4 бита хранят класс нивелирования, и следующие 4 бита – тип центра, далее 8 бит – масштаб плана при камеральном координировании, масштаб снимка для ортофотоплана или класс точности космического снимка в закодированном виде (итого 24 бит, остальные пока свободны). Типы координирования точек: 0. Не определено 1. Точка GPS 2. Точка, полученная способом геодезических засечек 3. Теодолитная точка 4. Пункт полигонометрии ГГС 5. Стенной пункт полигонометрии ГГС 6. Пункт триангуляции ГГС 7. Точка, полученная графически с топоплана 8. Точка, полученная с ортофотоплана 9. Точка, полученная с космического снимка 10. Высотный репер 11. Точка, полученная путём проектных расчётов Класс работ по плановому положению: 0. Не определено 1. Триангуляция 1 класса 2. Триангуляция 2 класса 3. Триангуляция 3 класса 4. Триангуляция 4 класса 5. Полигонометрия I разряда 6. Полигонометрия II разряда 7. Полигонометрия III разряда 8. Полигонометрия IV разряда 9. Теодолитный ход 10. ТХ повышенной точности 11. Геодезическая засечка 12. Геодезическая засечка (нитяной дальномер) 13. Камеральное координирование (масштаб плана) 14. GPS совместные наблюдения 15. GPS автономный режим Класс нивелирования: 0. Не определено 1. Техническое нивелирование 2. Геодезическое нивелирование 3. Нивелирование I класса 4. Нивелирование II класса 5. Нивелирование III класса 6. Нивелирование IV класса 7. GPS совместные наблюдения 8. GPS автономный режим 9. Камеральное вычисление (по плану) 10. DES файл 11. Аналитическое построение Тип центра: 0. Не определено 1. Не закреплённая точка 2. Расчётная точка 3. гл. промерзания менее 1.5 м 4. гл. промерзания свыше 1.5 м 5. Многолетняя мерзлота 6. Сезонное промерзание грунта 7. Сезонное промерзание грунта 8. Стенной знак 9. Знак долговременного закрепления 10. Знак временного закрепления Для текстов Angle: double; Size: double; X, Y, Z: double dX, dY, dZ: double - координата базовой точки - смещение от базовой точки при отображении линии выноски TextAlign: int32 LenStr: int32; Text: array[0..174] of WideChar 64 64 3*64 = 192 3*64 = 192 32 32 Если текст не входит в 174 символа, то остальной загружаем из блока дополнительных параметров. GUID: TGUID - глобальный идентификатор объекта CreateDate: TDateTime – дата создания объекта CreateUser: int32; - номер создавшего пользователя EditDate: TDateTime - дата последнего изменения объекта EditUser: int32; - номер пользователя изменившего объект ArchivPos: int64; - позиция в архиве предпоследней версии объекта, либо -1 для нового. Внутри объектов хранятся номера пользователей, которые ссылаются на запись в таблице пользователей покрытия. В данную таблицу заносятся все пользователи, которые когдалибо редактировали данное покрытие (на практике их бывает не так уж много). В таблице хранится вся необходимая информация о пользователе и об организации, где он работает. При этом данная информация не может быть введена или исправлена пользователями средствами программы, а заполняется и корректируется автоматически. Для каждого покрытия ведётся журнал изменения данных, который представляет собой специальный служебный файл в наборе файлов покрытия. Каждый объект внутри расширенного блока данных имеет ссылку на предпоследнюю копию данного объекта в архиве или -1, если объект только что создан. Ссылка хранится как int64, что в принципе позволяет использовать для архива файлы размером больше 2 Gb. Все новые записи всегда добавляются в конец файла архива. Если у объекта существует несколько версий, то их можно собрать из архива последовательно загружая версии объектов и считывая из них ссылку на предыдущую версию. Внутри архива все записи имеют заголовок, который указывает на тип выполнявшейся операции, а также размер записи о данной операции в архиве. На данный момент в архиве сохраняется следующая информация: 1. Изменение данных объекта, в архиве храним полную копию данных объекта. 2. Изменение заголовка объекта, например, при изменении связи с базой данных, когда метрики объекта не изменяются. В архиве храним только полный заголовок объекта, но не сохраняем остальные данные. 3. Перенос объекта из одного слоя в другой. Храним номера старого и нового слоёв. Если объект переносится между слоями разных покрытий, то в одном из покрытий это будет восприниматься как удаление, а в другом, как создание нового объекта, но при этом в записи об удалении желательно как-то зафиксировать, что было не просто удаление, а именно перенос в другое покрытие. 4. Удаление объекта. В архиве храним сначала ссылку на предыдущую запись об удалении объекта, а затем полную копию данных удалённого объекта. При этом в заголовке архива храним указатель на последнюю запись об удалении объекта. За счёт этого мы всегда можем собрать все записи об удалении объектов в покрытии начиная с последней. 5. Информация о восстановлении данных из архива (вроде как это тоже неплохо бы фиксировать).