-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtxt
More file actions
520 lines (362 loc) · 29 KB
/
txt
File metadata and controls
520 lines (362 loc) · 29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
MAPSOFT2
======================================================================
Что это такое. Системные требования.
mapsoft2 - это набор библиотек и небольших программ для работы с
геоданными, растровыми и векторными картами и т.п. Никакой общей
концепции пока не существует, поэтому библиотеки и программы весьма
разрозненны.
Мы не ставим себе цель сделать серьезную ГИС-систему, а хотим
решать простые бытовые задачи: смотреть треки и точки на картах,
делать векторные карты для туристского ориентироваия и т.п.
Для компиляции программ нужны следующие библиотеки:
* boost/spirit - синтаксический разбор разных файлов и пр.
* boost/operators
* boost/lexical_cast
* libusb - связь с gps-приемником через usb
* libproj - преобразование геодезических координат
* libyaml - разбор конфигурационного файла для векторных карт
* gtkmm-2.0 - графический интерфейс
* libtiff, libjpeg, libpng - чтение соответствующих форматов.
Мы пишем программы под Linux. Кажется, их удавалось скомпилировать
и под MacOS. Можно ли скомпилировать их под Windows - неясно, никаких
теоретических препятствий тому, кажется, нет.
======================================================================
Библиотека jeeps (директория jeeps).
Геодезические преобразования, а также связь с gps-приемниками через usb
и rs232 происходит с помощью замечательной библиотеки jeeps. К
сожалению, в интернете ее удалось найти только как часть пакета
gpsbabel. Используется немного модифицированная версия библиотеки,
которая хранится в директории /jeeps.
Напрямую к jeeps мы (почти никогда) не обращаемся.
Обмен данными с gps-приемником описан в geo_io/io.h
а преобразование геоданных - в geo_io/geo_conv.h
======================================================================
Программа mapsoft_convert - преобразования геоданных
mapsoft_convert <infile1> .. <infileN> -p <par1> .. -p <parN> -o <outfile>
Программа читает геоданные из файлов <infile1> .. <infileN> и
записывает их в файл <outfile>. Параметры <par1> .. <parN> влияют на
стандартное поведение программы.
Мы работаем с тремя типами данных: набор точек, трек, привязка карты.
К привязке карты обычно прилагается еще и растровая картинка, которую.
мы тоже можем как-то преобразовывать.
Формат читаемого файла обычно определяется по его содержимому, формат
записываемого файла - по его расширению.
Если файл - символьное устройство или если имя файла - "usb:",
программа читает или пишет данные в gps-приемник Garmin через это
устройство или через libusb.
Чтение производится из файлов следующих форматов:
Описание поддерживаемых форматов и параметров, которые влияют на
чтение и запись данных:
* xml: собственный xml-подобный формат, позволяющий хранить
данные без потери информации по сравнению с внутренним
представлением. При записи используется расширение ".xml".
* OziExplorer: широко распространенный формат. В одном файле хранится
ровно один трек, один набор точек или одна привязка карты. Набор.
параметров, описывающих геоданные, почти совпадает с нашим
внутренним представлением.
* geofig: специальное расширение формата fig, очень для нас важное.
Ниже есть более подробное описание этого формата.
* garmin-utils: достаточно бедный формат, включенный сюда по
историческим соображениям. Поддерживается чтение и запись треков и
точек.
* kml: поддерживается только запись треков и точек.
* Приемники Garmin: с помощью библиотеки jeeps поддерживается чтение.
и запись через USB и RS232 (работа через RS232, кажется, пока не.
проверена).
При преобразовании карт мы понимаем растроевые форматы jpeg, tiff.
(частично?), png (на чтение - кроме interlaced, на запись -.
только 24bpp) и векторные форматы xfig и mp.
======================================================================
Геодезические допущения и соглашения.
Все координаты хранятся в градусах WGS84. При этом, во всех объектах
данных x - это долгота, а у - широта!
Высота при преобразованиях СК не преобразуется.
Сейчас при работе с картами поддерживаются практически только
две проекции: равноугольная поперечно-цилиндрическая проекция
Гаусса-Крюгера (transverse mercator, tmerc) и проекции Lon/Lat (lonlat).
При этом мы не храним информацию о системе координат в которой была
нарисована карта и о параметрах проекции ГК, считая, что замена этих
параметров с хорошей точностью является линейным преобразованием!
Таким образом, любое преобразование карты сводится к преобразованию
latlon->tmerc или обратному (если это нужно) и линейному преобразованию,
которое находится по точкам привязки (минимизацией среднеквадратичного
отклонения, если точек более трех). Координаты точек привязки конечно
же, следует преобразовывать честно, заботясь о правильных
преобразованиях систем координат и построений правильных проекций!
Замечательность такого подхода заключается в том, что для карт,
нарисованных в проекции Гаусса-Крюгера (а в большинстве случаев мы с
такими и работаем) нам необходимы только линейные
преобразования, что сильно убыстряет дело!
(написать про величину искажений из-за такого допущения)
(хорошо бы упорядочить работу с параметрами проекций, добавить
другие типы проекций)
======================================================================
Базовые типы данных (директория utils)
В директории utils описаны следующие типы данных и функции для работы
с ними:
-------------------
Point<T> -- Точка на двумерной плоскости. Координаты типа T.
Определено сложение/вычитание, умножение/деление на число, сравнение...
T p.x, p.y -- координаты точки
-------------------
Rect<T> -- Прямоугольник на двумерной плоскости. Координаты типа T.
Координаты отсчитываются вправо-вниз. Левая и верхняя стороны включаются
в прямоугольник, а правая и нижняя (обычно :)) - нет.
Определены операторы умножения/деления на число, прибавления/вычитания
точки.
T r.x, r.y, r.w, r.h -- координаты верхнего-левого угла
(минимальные координаты), ширина, высота
Point<T> TLC(), TRC(), BRC(), BLC() -- углы
bool empty() -- проверка на нулевую площадь
Rect<T> rect_pump (Rect<T> const & R, T val)
расширить прямоугольник на величину val во все стороны
Rect<T> rect_pump (Rect<T> const & R, Rect<T> bounds)
???
Rect<T> rect_intersect (Rect<T> const & R1, Rect<T> const & R2)
пересечение прямоугольников
Rect<T> rect_bounding_box (Rect<T> const & R1, Rect<T> const & R2)
наименьший прямоугольник, включающий два данных.
void clip_point_to_rect (Point<T> & p, const Rect<T> & r)
Подвинуть p в ближайшую точку, входящую в прямоугольник
(Здесь вот правая и нижняя сторона считается принадлежащей
прямоугольнику! Исправить ли?)
void clip_rect_to_rect (Rect<T> & r1, const Rect<T> & r2)
подрезать r1, так, чтобы он целиком входил в r2
bool point_in_rect (const Point<T> & p, const Rect<T> & r)
проверка, лежит ли точка в прямоугольнике
Для прямоугольников с целыми координатами есть такие функции:
Rect<int> tiles_on_rect(const Rect<int> & r, int tsize);
диапазон плиток, накрывающих данный прямоугольник
Rect<int> tiles_in_rect(const Rect<int> & r, int tsize);
диапазон плиток, лежащих внутри данного прямоугольника
void transform_rect(const Rect<int> & src,
const Rect<int> & dst, Rect<int> & r);
Два прямоугольника задают преобразование. Функция соответствующим
образом сдвигает и растягивает третий прямоугольник
void clip_rects_for_image_loader(
const Rect<int> & src_img, Rect<int> & src,
const Rect<int> & dst_img, Rect<int> & dst);
Функция, нужная для загрузчика картинок.
Правильное подрезание краев, выходящих за пределы картинки
-------------------
Image<T> -- Картинка -- двумерный массив элементов типа T с
настоящим memory management'ом!.
При присвоении и инициализации из другой картинки массив данных не
копируется! (устроен счетчик ссылок на массив, когда ссылок не
остается - массив удаляется)
int image.w, image.h
Rect<T> image.range() -- размеры картинки
image.empty() -- проверка на нулевую площадь
Копирование картинки и данных: image1 = image.copy()
Доступ к точкам картинки:
image.get(x,y), image.set(x,y,c),
image.get(p), image.set(p,c) -- без проверки границ
image.safe_get и image.safe_get -- с проверкой границ
image.set_a, image.safe_set_a -- с обработкой прозрачности
image.set_na, image.get_na,
image.safe_set_na, image.safe_get_na,
-- с отрезанием информации о прозрачности
Компилировать с ключом -O1 (скорость возрастет раза в три)
-------------------
image_brez.h -- алгоритмы Брезенхема -- рисование линий и кругов на
картинке Image<int>.
!Хочется отказаться от использования этого и перейти на библиотеку gd.
-------------------
image_draw.h -- интерфейс к библиотеке gd.
рисование текста, линий и т.п. на Image<int>
-------------------
image_gdk.h -- интерфейс между image и gdk-pixbuf
Glib::RefPtr<Gdk::Pixbuf> make_pixbuf_from_image
(const Image<int> & image)
Данные не копируются!
-------------------
Options -- это std::map<std::string,std::string>
с возможностью извлекать параметры разных типов
-------------------
Cache<K,V> Кэш элементов типа V с поиском по ключу типа K.
Интерфейс довольно очевиден:
Cache (int _capacity)
void erase(K const & key)
int size()
int add (K const & key, V const & value)
bool contains (K const & key)
V & get (K const & key)
int space_remains ()
void clear ()
======================================================================
Представление геоданных
Геоданные - это треки, точки и привязнные карты, примерно в том смысле,
как они понимаются в OziExplorer. В файле geo_io/geo_data.h описаны следующие
структуры данных:
* g_point - точка, содержащая две вещественных координаты, Point<double>
* g_waypoint - waypoint, поля примерно те же, что и в формате OziExplorer
* g_trackpoint - точка трека, поля примерно те же, что и в формате OziExplorer
* g_refpoint - точка привязки карты: g_point + две целочисленные координаты
* g_waypoint_list - список точек
* g_track - Трек. список g_trackpoint + информация о треке, как в OziExplorer
* g_map - Привязка карты. Список g_refpoint + информация о карте
...
======================================================================
Работа с геоданными. Проекция, система координат
...
======================================================================
Ввод-вывод геоданных. Разные форматы
...
======================================================================
mapsoft_convert
...
======================================================================
Чтение и запись формата FIG3.2
FIG - удобный для многих целей текстовый формат векторной графики. Мы
используем его, в частности, для рисования векторных карт и схем.
В файле geo_io/fig.h описаны структуры данных fig_object и fig_world
и процедуры чтения и записи их в файл.
======================================================================
geofig
Чтобы хранить, смотреть и редактировать геоданные в формате fig было
придумано специальное расширение формата. Чтение и запись геоданных
в объект fig_world описана в файле geo_io/geofig.h
В комментарии к файлу может быть указан тип проекции, например
"proj: lonlat" По умолчанию проекция - tmerc
Специальные объекты: первая строка комментария содержит следующее:
* REF <x> <y>
* WPT <name>
* TRK <comment>
* MAP <comment>
* BRD <comment>
...
======================================================================
Привязка карт -- номенклатурные карты
Если карта номенклатурная - все сильно упрощается. Нам достаточно знать
название листа и координаты углов на картинке. Из этого можно сделать привязку,
причем с аккуратной границей.
Для этого предназначена программа mapsoft_mkmap.
Она получает файл, в каждой строке которого содержится информация об одном листе:
<имя файла> <8 чисел - координаты углов> <комментарий>
Например:
O35-29.jpg 93 211 2461 159 2552 3119 139 3174 Опочка, 1985, изд.1987
O35-30.jpg 142 211 3693 193 3744 4586 131 4606 Новоржев, изд.1967
P35-23-24.jpg 148 202 6328 201 6402 4590 87 4595 Савонлинна, 1974-87, изд.1991
P35-29-30.jpg 218 141 6535 218 6548 4609 99 4533 Выборг, 1970-87, изд.1991
Из названия файла извлекается название номенклатурного листа (Обратите
внимание на нули! O35-1 - это пятикилометровка, O35-01 -
двухкилометровка, O35-001 - километровка).
На выходе получаются файлы геоданных в любом допустимом формате.
mapsoft_mkmap list.txt -o list.xml
Для удобного изготовление входных файлов (записи координат углов карты)
сделан plugin к gimp'y: utils/gimp/map-helper.py
======================================================================
Привязка карт - вручную, с помощью xfig
Пусть есть карта и мы знаем координаты некоторых точек на ней.
Загружаем картинку с картой в xfig. В комментарии к картинке пишем
"MAP <название>".
Обводим (это не обязательно) границу карты многоугольником, в
комментарии к нему пишем "BRD <название>". Название должно быть то же,
что и у картинки.
Если карта нарисована не в проекции Гаусса-Крюгера - в комментарии к
файлу пишем "proj: <название проекции>".
Ставим точки привязки. В комментарии к ним пишем "REF <координаты>".
По умолчанию координаты - долгота и широта (в таком порядке!) в
системе координат WGS84. Мы можем использовать другие СК и другие
координаты. Тогда комментарий должен выглядеть так:
REF 400000 6450000
datum: pulkovo
proj: tmerc
lon0: 39
После этого мы можем преобразовать полученный fig-файл, например в map-файл
OziExlorer'a:
mapsoft_convert mymap.fig -o mymap.map
======================================================================
Привязка карт - по геоданным
Пусть есть карта и есть какие-то точки и треки.
Загружаем карту в fig-файл, пишем комментарий к картинке, если надо -
указываем границу карты и ее проекцию.
Рисуем на карте треки и точки - в тех же местах и с теми же
названиями, что и уже существующие треки и точки.
Удобно, например, открыть два xfig'a: один с привязанной картой, а
другой с непривязанной. Затем нарисовать в них ломаные линии,
проходящие через соответствующие точки и написать к ним одинаковые
комментарии: "TRK <название>"
После этого применить программу:
mapsoft_ref <геоданные> -o <fig-файл, в который надо добавить привязку>
Программа, кстати, поступает очень просто: она преобразует точки и
треки в точки привязки, приписывая им геодезические координаты из
входного файла.
======================================================================
Техническое: определение размеров текста в fig-файле
geo_io/gs_bbx.h
======================================================================
Объекты, относящиеся к графическому интерфейсу.
Описание текущего состояния.
layer -- объект, умеющий рисовать некоторые данные на плоскости с
целочисленными координатами и отдавать картинку Image<int>. Layer
может кэшировать какие-то свои исходные данные (типа загружаемых карт).
Layer может быть интерактивным.
geo_layer - потомок layer', понимающий про геодезические системы
координат. Плоскость для рисования задается привязкой (такой же, как
для растровых карт), на ней могут изображаться различные геоданные.
У нас сейчас есть два geo_layer'а: layer_geodata.h -- он показывает
(пока довольно криво) точки и треки, и layer_geomap.h -- показывает
набор привязанных карт в нужной проекции.
workplain - объект, собирающие несколько layer'ов в нужном порядке на
одной картинке.
Мысли:
1. А не должен ли workplain быть на самом деле (geo_)layer'ом? Его
функции совершенно такие же! Впрочем, с дальнейшими идеями про
интерактивные layer'ы это немного конфликтует, кажется.
2. Сейчас workplain создает пустую картинку и по очереди подсовывает
ее для заполнения layer'ам. Надо, чтобы каждый layer отдавал новую
картинку, а workplain их кэшировал! Иначе, при изменении данных одного
layer'a, приходится перерисовывать и все остальные. Глупо -- двигаешь
точку трека, а приходится пересчитывать все карты...
viewer -- объект, показывающий workplane на экране. Устроен в виде
двух потоков, один из которых работает с пользователем, а второй
поставляет для него нужные плитки изображения (256x256 точек). Кэш
плиток -- это видимые на экране плитки + область еще на две плитки во
все стороны. Сначала заполняются видимые на экране, потом, если делать
нечего -- остальные.
======================================================================-
Интерактивные layer'ы
Как хочется сделать (часть уже сделано :)).
layer умеет отдавать список своих режимов с помощью функции
std::vector<std::string> action_names ().
Например, для показа и редактирования геоданных могут быть следующие режимы:
New point, New track, Delete, Copy, Move, Move trackpoint, Add trackpoint.
viewer позволяет пользователю выбрать активный layer и режим для него.
Во вьюере имеется странный объект (ActionData), который состоит из
списка "событий" и списка "резиновых хвостов". "Событие" - это номер
текущего режима + координаты, куда втыкнули мышью. "Резиновый хвост" -
это отрезок, перерисовывающийся при движении мыши. Координаты его
концов могут измеряться относительно положения мыши или относительно
workplane (то есть, отрезок может быть привязан к мыши, к workplain'у
или растягиваться вслед за мышью).
При втыкивании мышью в некоторую точку (левую кнопку мыши использовать
для такого рода втыков, а правую - для скроллирования экрана, так?),
viewer добавляет в ActionData "событие" - номер режима и координаты
втыка. Ссылка на ActionData передается нужному Layer'у.
Layer изучает объект ActionData и в зависимости от него совершает
действия, добавляет хвосты, очищает списки хвостов и событий...
Если действие требует более одного втыка, layer не совершает его и не
обнуляет список событий, в надежде, что там когда-нибудь накопится
нужное количество записей, но зато добавляет хвосты, чтобы пользователю
было видно, что происходит.
Пример - layer_geodata, режим "Move trackpoint":
* Втыкнули мышью в точку.
* Layer получает ссылку на ActionData.
* Событие там одно, это недостаточно для действия.
* Если в точке события нет точки трека - обнулим список событий.
(все вернется к исходнному состоянию)
* Если в точке события есть точка трека - Добавим "резиновые хвосты",
соединяющие мышь и соседние с перемещаемой точки.
* Теперь у нас все, как в исходном состонии, но за мышью тянутся
два хвоста, а в списке событий уже есть одни координаты.
* При повторном втыкивании мышью, в списке событий появятся еще одни
координаты.
* Теперь у Layer'а есть координаты двух точек, что достаточно для
действия.
* Переносим нужную точку трека (придется заново ее найти),
Очищаем списки событий и хвостов.
Еще нужны события, связанные с редактированием данных. В этом случае
layer должен отдавать объект options - видимо, надо его тоже встроить
в ActionData...
======================================================================-