A good glass in the bishop’s hostel in the devil’s seat forty-one degrees and thirteen minutes northeast and by north main branch seventh limb east side shoot from the left eye of the death’s-head a bee line from the tree through the shot fifty feet out.
Edgar Allan Poe.
Предыдущие статьи цикла о реверсе данных автомобильных навигаторов Siemens/VDO Dayton CARMiN:
01 Сшей красное с красным, желтое с желтым, белое с белым. Наверняка будет хорошо
02. Я уже даже не вижу код. Я вижу блондинку, брюнетку и рыжую
Тип блоков 0x10. POI.
Добавляю в en_BL_TYPE POI = 10h, // POI
, в switch функции block() case 0x10: BT_0x10 block_0x10 <comment="POIs">;
, и описываю структуру BT_0x10 для блока 0x10.
Заголовок размером 30h и массив BRIF_0x10 - всё так же, как и в блоках стран, городов и улиц/дорог. Определяю первый попавшийся блок типа 0x10 block(FindBlockByType(0x10));
, и сразу ещё один - с адресом block_0x10.next_0x10.offset
- из заголовка первого. В BRIF_0x10 описание more_info закомментировано - эта структура пока не известна.
//{BT_0x10;
//{BRIF_0x10 - main data, roads and streets
typedef struct{
local ushort size <format=hex, hidden=true> = head.addr.size * 0x800; // size of this block
local uint offset <hidden=true> = head.addr.offset; // absolute block offset
PSTR pstr_name <bgcolor=cLtAqua>; // ptr to zero-ended str
ubyte place_bitemask<bgcolor=cDkGreen, fgcolor=cYellow>;// 0xA={0,1}, 0xC={0,1,2,10,12}
en_LANG en_lang; // language code
PSTR pstr_region<bgcolor=cLtAqua>; // ushort always 0 in 0xA
PTR p_moreinfo <hidden=true>; // ptr to item of LIST pl_all_moreinfo;
// jump to MORE_INFO
local uint retur_here <hidden=true> = FTell();
FSeek(p_moreinfo.ptr + offset);
//MORE_INFO_0xE more_info;
FSeek(retur_here);
}BRIF_0x10<read=Read_BRIF_0x10>;
string Read_BRIF_0x10(BRIF_0x10 &a){
local string s;
SPrintf(s, "%02X %s (%s) . Lang: %s",
a.place_bitemask,
a.pstr_name.str, a.pstr_region.str,
EnumToString(a.en_lang)
);
return s;
}//}BRIF_0x10
typedef struct{
BL_HEAD head; // заголовок
local ushort size <format=hex, hidden=true> = head.addr.size * 0x800; // size of this block
local uint offset <hidden=true> = head.addr.offset; // absolute block offset
LIST pl_all_pois; // brief geo info
LIST pl_all_moreinfo;// more info, ptrs from briefs
// next eight uints seems like in hex from rel offset 10h
//0xA: CONST_I zero(0); LIST pl_all_POIs; CONST_I zero(0); CONST_I zero(0);
//0xA: CONST_I zero(0); CONST_I zero(0); CONST_I zero(0); CONST_I zero(0);
//0xC: CONST_I zero(0); LIST pl_all_cat_POIs; CONST_I zero(0); LIST pl_new_list_ofic;
//0xC: CONST_I zero(0); LIST pl_new_list_ru; BL_ADDR next_0xC; BL_ADDR prev_0xC;
CONST_I zero(0); CONST_I zero(0); LIST pl_all_cat_POIs; CONST_I zero(0);
CONST_I zero(0); CONST_I zero(0); BL_ADDR next_0x10; BL_ADDR prev_0x10;
BRIF_0x10 poi[pl_all_pois.cnt] <optimize=false>; // main data
}BT_0x10;
//}BT_0x10;
#include "inc_blocks.bt"
// --- Template -----------------------------
// block(FindBlockByType(0x0A));
block(FindBlockByType(0x10)); // Get the first block with type = 0x10
block(block_0x10.next_0x10.offset); // BL_ADDR next_0x10 in block(FindBlockByType(0x10));
Блок 0x10. More info
Размер данных структуры MORE_INFO_0x10 очевиден по голубым следам PTR: 28h байт. Создаю структуру, поглядывая на hex, предполагая те или иные типы данных, имена переменных пока что просто чтобы были.
//{MORE_INFO_0x10;
typedef struct{
uint ia;
uint ib;
uint ic;
CONST_S sz(0);
PTR ptr2_unk;
PTR pa;
PTR pb;
PTR pc;
ushort sa;
CONST_I iz1(0);
PTR pd;
PTR pe;
CONST_S sz(0);
PTR ptr2_unk1;
CONST_I iz2(0);
}MORE_INFO_0x10;
//}MORE_INFO_0x10;
Раскрывать каждый раз poi - more_info - PTR - here, чтобы кликом перебраться на место, которое указывает PTR утомительно. Но можно добавить в структуру BRIF_Ox10 навигационную переменную anchor с тем же местонахождением anchor(more_info.ptr2_unk.ptr + offset);
.
void anchor(uint offset){
local uint ret_here <hidden=true> = FTell();
FSeek(offset);
ubyte anchor <bgcolor=cPurple, fgcolor=cAqua>;
FSeek(ret_here);
}
Выходит, PTR ptr2_unk - указатель на строку, PSTR name. И PTR pa, pb, pc, pd - тоже PSTR.
//{MORE_INFO_0x10;
typedef struct{
local ushort size <format=hex, hidden=true> = head.addr.size * 0x800; // size of this block
local uint offset <hidden=true> = head.addr.offset; // absolute block offset
uint ia;
uint ib;
uint ic;
ushort s_ex_z;
PSTR str_street;
PSTR str_city;
PSTR str_region;
PSTR str_country;
ushort sa;
CONST_S sz(0);
ushort sb;
ushort sc;
ushort se;
PSTR str_phone_num;
PSTR str_zip_code;
CONST_I izero(0);
}MORE_INFO_0x10;
У нас уже есть список ссылок на блоки 0x10 города Хеля.
Вкратце напомню. Определить список стран block(FindBlockByType(0x0A));
в carindb_ee. Последовательно раскрываю Poland, moreinfo, city[7] == h [8]> a e i l o r u y
, дочерний элемент struct CH_IDX childs[1]
которого имеет значение block(0x0C1108); //of:608C78 'e' [38]>0c()>, hebdow, hecznarowice, hedwizyn, hejdyk, hel
. И строку с этим блоком - в темплейт, применить.
И блок с первым попавшимся POI Хеля - в темплейт: block(0x1B47D06); //of:DA3E830 's' [1]>10(POI)>, stacja paliw
.
Не удобно всё же сравнивать соседние more_info, меняю функцию Read_MORE_INFO_0x10, чтобы та во вкладке Variables выводила значения первых трёх uint: SPrintf(s, "ia ib ic: %08X %08X %08X", a.ia, a.ib, a.ic);
А в функции, отображающей в Variables значение BRIF_0x10, ставлю вывод значения дочерней MORE_INFO. SPrintf(s, "%s: %s", Read_MORE_INFO_0x10(a.more_info), a.pstr_name.str);
Координаты в carindb
Значения ia и ib у POI в Хеле (на скрине в фиолетовой и зеленой рамке) - не уникальны, но очень близки по значениям с соседями. Это первые два значения в структуре MORE_INFO блока, содержащего информацию о POI.
Спроси меня кто, я бы сказал, что наиболее важная характеристика точки интереса - её географические координаты.
По структурам данных carindb уже можно сделать вывод, что они рассчитаны на обработку процессорами 30-40 летней давности, с 16/32-битными регистрами данных (угу, а еще BigEndian, короче, что ли привет, Motorola 68k? Вау, это что, я могу катриджи СегиМегаДрайв запускать на своём автонавигаторе, чуть его допилив? )
Географические координаты строятся по принципу сферических, значение обычно варьируется от 0 до 360 градусов. Окружность большого круга 40075,017 км, т.е. на каждый градус достается 111319.492 м. Естественно, работая с градусами и долями напрямую, процессор должен обмолачивать массу операций с числами с плавающей запятой. Процессор 30-летней давности и плавающая запятая? Или перевести в целые числа, домножив значения координат на какой-то коэффициент, и далее внутри работать с целочисленным представлением, а если изредка понадобится вывести для мясных людишек привычные им значения градусов - разделить на этот же коэффициент.
Пора его вычислить.
Предполагаю, у меня есть координаты в виде двух 32-битных целых чисел для точек carindb, географические координаты которых я могу увидеть в GoogleMaps, или в OpenStreetMaps.
Самое приятное, у меня есть возможность сопоставить значения из этих двух миров для каких-то конкретных объектов.
Объекты:
маяк в Хеле - Latarnia,
ia ib ic: 1029E480 12147BC0 16400001: latarnia morska hel
тюлений питомник в Хеле - Fokarium,
ia ib ic: 1028C700 12152180 16300001: fokarium
Баптистская христианская церковь в Мальброке - Kościół Chrześcijan Baptystów,
ia ib ic: 103C4840 11E44B40 28A00001: chrzescijan baptystow
В SMath Studio завожу 4 варианта расчета: значение первого 16-тиричного числа делю на реальную долготу, потом на широту, потом то же самое проделываю со вторым числом. И так для всех трёх мест. Искомый коэффициент должен быть константой. У фокариума и латарни в Хеле почти все значения близки друг другу - но и точки близко расположены друг к другу, а третья точка - церковь в другом городе, Мальброке, достаточно удалена, чтобы не волноваться о неточности снятых координат.
Сначала я хотел использовать рыцарский замок в Мальброке, но он слишком большой по размеру, не было бы ясно, какую часть замка описывают значения координат. Нужен был уникальный объект с минимальными размерами, и присутствующий в обеих мирах.
В синем прямоугольнике - данные из OpenStreetMaps, в желтом - из carindb. Увы, встроенных средств работы с 16-ричными значениями в матстудии нет, поэтому перевод в десятичные и обратно - ручные. В зеленом кружке - одинаковое (почти) значение, вероятно, коэффициент, который я присваиваю вверху зеленой переменной CoordInit и еще раз напротив каждого объекта синим цветом уже делю координаты из carindb на этот коэффициент. Second - latitude (ожидаемо, константу то я из него и получил), а first - отличается от реальной longtitude на 30 (красная точечная стрелка).
Значение CoordInit=5555565 - среднее арифметическое по трём точкам. Физически это число представляет собой дискретность представления одного градуса большого круга. Размер младшего разряда 111319.492 м. / 5555565 = 0.02м, или 2 см.
Какое на самом деле значение коэффициента в потрошках навигатора - мне доподлинно неизвестно, но когда все пятёрки, смотрится, по-моему, симпатичнее. Разница со средним по трём точкам - треть метра на каждый градус в минус, для 60 градусов будет - 18 метров, что уже должно быть заметно. Константу проверять на соответствие расчетных координат реальным следует на максимально доступных значениях градусов - для официальных карт это carindb_ee, Эстония, Нарва.
Всё, как с Хелем и Мальброком, только для Нарвы. В блоке стран беру адрес блока городов на n, с Нарвой. В блоке том беру адрес блока с нарвскими POI.
// --- Template -----------------------------
block(FindBlockByType(0x0A));
// Estonia - more_info - 'n'
// block(0x075F06); //of:3AF938 'n' [84]>0c(CITY)>, naage, naartse, nabala, nadalama
block(0x075F06); //of:3AF938 'n' [84]>0c(CITY)>, naage, naartse, nabala, nadalama
// struct FAR_LIST POIs block(0x150DC08); // [5] tp:CH_idx_11(11) of:A870A84 3B0D48h 8h Fg: Bg:
// block(0x1829608); //of:C14B488 'j' [1]>10(POI)>, jumalaema narva phakuju kirik
block(0x1829608); //of:C14B488 'j' [1]>10(POI)>, jumalaema narva phakuju kirik
Примечание: 010Editor неровно дышит к non-ascii символам в комментариях. Поэтому приходится вычищать буквы с умляутами в копипасте, например, в block(0x1829608); //of:C14B488 'j' [1]>10(POI)>, jumalaema narva pühakuju kirik
. И по той же причине надо стараться не использовать русский язык, иначе весьма возможна (но не обязательно) *ERROR Line 639(73): Syntax error.
на неприятной 010Editor-у строке. Нет, редактор 010 (в 11 версии) в юникод не умеет.
Заскочу на страницу вперёд: там будет описана структура LON_LAT, обеспечивающая копипаст URI из закладки Variables для браузера в виде ссылки на gmaps или openstreetmaps на координаты POI
// https://www.google.com/maps/@59.385708,28.192745,19z
https://www.openstreetmap.org/#map=19/59.385708/28.192745
Использовал в первую очередь категорию Church по той причине, что церкви - не магазины, которые появляются и исчезают. Эти объекты обычно наносимые на OpenStreetMap, выделяющиеся на спутниковых фотографиях и имеющие сравнительно небольшие размеры. И имеющие узнаваемое по латинским корням названия даже в совершенно незнакомых языках. Есть у меня потаенное опасение, что, читая эстонские географические названия вслух, случайно как-то не так произнесу слово, и рядом откроется портал.
jumalaema narva pühakuju kirik - на дороге, на 40 метров южнее "ORTHODOX Narva Narva Icon of Our Lady Church" (59.38604, 28.19252)
narva aleksandri kirik - на дороге, на 30 метров южнее "Александровская лютеранская церковь"(59.37075, 28.20161)
õigeusu ülestõusmise katedraal - на дороге, на 30м западнее и на 30м севернее "Нарвский Воскресенский Кафедральный собор" (59.37118, 28.19365)
kindralmaj j. orasmaa mälestm - какой-то памятник (Architecture), расчетные координаты carindb указывают на автостоянку метров на 120м севернее, и на 90м восточнее.
mcdonald's - на автодорогу на 10м западнее и 10м севернее МакДональдса.
swedbank - автостоянка на 40м южнее большого торгового центра Fama Keskus, в котором таки есть и отделение Swedbank
Выводы:
внезапно, координаты POI в carindb хранят значения для целей автомобильной навигации, а не содержат координаты собственно точек интереса.
значение коэффициента перевода градусов во внутренние координаты carindb 5555555 (или 0x54C563 или ) можно использовать, как имеющее достаточно приемлемое расхождение с реальными координатами.
В компанию глобальных констант в начале "inc_common.bt" добавляю еще одну, MULCOORD:
// сonstant max allowed addr
const int MAX_FILE_ADDR <hidden=true> = FileSize();
// only here i find different between 0A addinfo fields count
// 1 - new, 2 - old, non-crypted, not-compressed
local ubyte IS_OFICIAL_MAP <hidden=true> = ( ReadUShort(0x2e) == 1);
// Multiply coefficient for traslate float degrees to int
local uint MULCOORD = 0x54C563; // dec 5555555;
На руках - формулы перевода координат внутреннего представления carindb в реальные географические и обратно.
Переоценить значение нахождения внутреннего формата хранения координат очень сложно.
hex_lat = lat * MULCOORD;
hex_lon = (lon + 30.0) * MULCOORD;
lat = 1.0f * hlat / MULCOORD;
lon = ((1.0f * hlon )/ MULCOORD) - 30.0;
Структура LON_LAT
В отличие от привычных нам координат, последовательность хранения в carindb обратная - сначала hex значения долготы, а вторым - hex широты.
В инклюд файл "inc_common.bt", в кирпичики, добавляю новую структуру.
//{LON_LAT
typedef struct{
uint hlon <fgcolor=cYellow>;
uint hlat <fgcolor=cWhite>;
local double lon = ((1.0f * hlon )/ MULCOORD) - 30.0;
local double lat = 1.0f * hlat / MULCOORD;
}LON_LAT <bgcolor=cDkGreen, read=Read_LON_LAT, comment=Comment_LON_LAT>;
string Read_LON_LAT(LON_LAT &a){
local string s;
SPrintf( s, "%06f%s, %06f%s",
Abs(a.lat), (a.lat>0)?"N":"S",
Abs(a.lon), ((a.lon>=0)?"E":"W") );
return s;
}
string Comment_LON_LAT(LON_LAT &a){
local string s;
local ubyte zoom = 19; // web view map zoom level
// like https://www.google.com/maps/@54.6184459,18.8018413,15z
//SPrintf(s, "https://www.google.com/maps/@%06f,%06f,%iz",
// a.lat, a.lon, zoom);
// https://www.openstreetmap.org/#map=17/59.37247/28.18919
SPrintf(s, "https://www.openstreetmap.org/#map=%i/%06f/%06f",
zoom, a.lat, a.lon );
return s;
}
//}LON_LAT
Впервые использовал назначение атрибуту comment, Comment_LON_LAT - вывод значения в колонке Comment в закладке Variables аналогично функции Read_LON_LAT, выводящей значение в колонке Value. Формируется URI, ведущий к координатам POI на OpenStreeMaps (или на GoogleMaps), копипаст в строку браузера и алга.
В структуре MORE_INFO_0x10заменяю uint-ы ia и ib новой структурой LON_LAT coord;
и вывожу в Variables значения нераспознанных переменных.
Добавляю атрибутивную функцию comment и для основной структуры BRIF_0x10, чтобы не лезть вглубь для получения координат.
string Comment_BRIF_0x10(BRIF_0x10 &a){
// ret comment of more-info coordinates
return Comment_LON_LAT(a.more_info.coord);
}
Колонка Comment - в зеленой рамке - URI на место на google maps. В колонке Variables - желтая рамка - у переменной ic первые 16бит оканчиваются то на 0, то на 8, а вторые - практически все имеют значения 1 - очень похоже на LIST. Переопределяю как LIST pl_ic, а в BRIF_0x10 вызываю функцию anchor(more_info.pl_ic.offset), добавляющую переменную в месте, куда указывает смещение LIST - посмотреть, и что у нас тут?
А тут у нас, оказывается, находиятся FAR_LIST на блоки типа 0x00. И обнаруживается проблема - некоторые LIST в этих структурах адресуют дальше, чем размер блока. Куча красного и неприятных записей о варнингах/ошибках в логе.
FAR_LIST.BL_ADDR.r_size
Дело в том, что некоторые блоки в официальных картах - сжаты, архивированы. Причем, заголовок блока с LISTами на данные - не сжимается, а вот тело - да. Признак того, что блок сжат - значение 1 седьмого байта от начала блока, в структуре BL_HEAD uchar is_compressed. Следующий байт - uchar uncompressed_size - содержит размер разархивированного блока в 0x800 байтных сегментах.
Структура же FAR_LIST сейчас исходит при самопроверке из того, что размер блока хранится в последнем байте BL_ADDR.
Добавляю получение реального размера блока r_size для BL_ADDR, и добавляю красный фон последнего байта для архивированных блоков. Переделываю FAR_LIST, чтобы учитывался r_size.
typedef struct{
BL_ADDR far_block; // link to block type 0xd
local ushort size <hidden=false> = far_block.r_size;
local uint offset <hidden=true> = far_block.offset;
LIST pl_data <bgcolor=0x22BFFF>; // far pointer list, BL_ADDR+LIST
}FAR_LIST <read=Read_FAR_LIST>;
Блок 0x10. Ссылки на 0x00
Первоначально названный в заголовке LIST pl_all_cat_POIs - на самом деле ведёт на FAR_LISTы с ссылками на объекты в блоках типа 0x00, и почти все - со сжатой информацией.
Переименовываю pl_all_cat_POIs в pl_all_bl0x00.
Структура блоков типа 0x0 неизвестна, пока можно покапитанить-поочевидничать:
Почти все блоки 0x00, на информацию в которых идут ссылки - сжаты (красный фон четвертого байта BL_ADDR)
Вспомнить, что блоков этого типа (наряду с 0x16) в carindb максимальное количество
Встречаются нулевые ссылки-списки: смещение есть, но счетчик =0
Фиолетовыми рамками выделил пару не упакованных блоков 0x00
Объявленный ushort s_ex_z - это PSTR str_title, просто у многих элементов пустой
CONST_S sa(0); - в блоке POI Хеля (block(0x1B47D06)) везде ноль. А вот в блоке POI с Мальброком (block(0x1B46508);) это поле иногда = 1.
Вывод: sa - это не short, а 2 байта - нулевая константа для выравнивания данных и байт признака того, что структура POI описывает синоним на ином (для данной местности) языке.
Следующий ushort sb → это PSTR str_building, а Карл Маркс - это не два человека... я хотел сказать, что ushort sc и ushort sd - это uint id, уникальный номер POI.
Окончательный вид структуры MORE_INFO_0x10:
//{MORE_INFO_0x10;
typedef struct{
local ushort size <format=hex, hidden=true> = head.addr.size * 0x800; // size of this block
local uint offset <hidden=true> = head.addr.offset; // absolute block offset
LON_LAT coord;
LIST pl_far_block00;
if(IS_OFICIAL_MAP){
PSTR str_title;
PSTR str_street;
PSTR str_city;
PSTR str_region;
PSTR str_country;
CONST_B zb_aligment(0);
en_TYPE_ADDR is_alias; // oficial carindb - or 1 or 0
CONST_S sz__aligment(0);
PSTR str_building;
uint id <bgcolor=cLtBlue, fgcolor=cYellow>;
PSTR str_phone_num;
PSTR str_zip_code;
CONST_I izero(0);
}else{ // russian carindb
CONST_S str_title(0);
PSTR str_street;
PSTR str_city;
PSTR str_region;
PSTR str_country;
CONST_B zb_aligment(0);
en_TYPE_ADDR is_alias; // always 1 in rus carindb
CONST_S sz__aligment(0);
CONST_S str_building(0);
CONST_I id(0);
CONST_S str_phone_num(0);
CONST_S str_zip_code(0);
CONST_I izero(0);
}
// make block_0x00 far pointers
local uint ret_here <hidden=true> = FTell();
FSeek(pl_far_block00.offset);
FAR_LIST block_0x00;
FSeek(ret_here);
}MORE_INFO_0x10 <read=Read_MORE_INFO_0x10>;
string Read_MORE_INFO_0x10(MORE_INFO_0x10 &a){
local string s;
if(IS_OFICIAL_MAP){
SPrintf(s, "is_alias: %02X id:%04X ",
a.is_alias, a.id );
}else{
SPrintf(s, "is_alias: %02X id:%04X ",
a.is_alias, a.id.value );
}
return s;
}
//}MORE_INFO_0x10;
Иллюстрация данных, отображаемых структурой, на примере Шато де Мальброк.
Разбор структуры блока 0x10 окончил, иных данных в блоке нет.
KML.
Вывод всех POI любого блока типа 0x10 в kml файл - POI2kml(block_0x10).
Функция на самом деле малополезная: категории POI хранятся не в блоке 0x10, а в структурах категории POI городов или стран. Т.е. из 0x10 точки в kml вывести можем, а чтобы назначить им правильные иконки - это надо было обрабатывать не блоки 0x10, а блоки 0xA и 0xC, по одному смотреть города, для каждого объявлять-парсить 0x10, и только затем перебирать наличествующие категории и POI в них в каждом городе. Алгоритм тривиален, но необходимости его реализации в рамках статей не вижу. А вот глянуть сколько POI влазит в блок, и как группируются по местности, и нормально ли по точности получилось - пожалуй, стоит.
Для разового использования, "грязная", непричесанная, неверно подсвечиваемая редактором, по-харконенски, в лоб, быстро и грубо. Короче, я её сам стесняюсь, но и не показать, как получил kml, методически неверно. Но так писать код некрасиво, не повторяйте за мной.
Еще у меня она крашит 010Editor v11 при попытке при раскомментировании строки //FileClose();
// --- Template -----------------------------
// block(FindBlockByType(0x0A));
//Poland,'h'-'e'- Hel
// block(0x0C1108); //of:608C78 'e' [38]>0c()>, hebdow, hecznarowice, hedwizyn, hejdyk, hel
// struct BRIF_0xC city[141] 00 hel (powiat pucki) . Lang: _Polish 608C98h 8h Fg: Bg:
block(0x1B47D06); //of:DA3E830 's' [1]>10(POI)>, stacja paliw
POI2kml(block_0x10);
uint POI2kml(BT_0x10 &a){
local ushort i;
local int fout; // file
local string s, stmp, filename;
if(a.head.addr.type != 0x10) return 1; // only POI type
//create file
fout = FileNew("Text", true);
SPrintf(filename, "c:\\Work\\VDO_Dayton\\010_bt\\habr\\kml\\0x10_POI_0x%08X.kml",
a.head.addr.raw);
//SPrintf(stmp
FPrintf(fout,"%s", kml_header()); // klm header
FPrintf(fout,"
<name>Carindb POI(type 0x10) block 0x%08X</name>
<open>1</open>
<description>Full content of block without POI categories division</description>
",
a.head.addr.raw
);
FPrintf(fout,"\t<Folder>
<name>Placemarks</name>
<description>These are just some of the different kinds of placemarks with
which you can mark your favorite places</description>
<LookAt>
<longitude>%06f</longitude>
<latitude>%06f</latitude>
<altitude>3100</altitude>
<heading>0</heading>
<tilt>40</tilt>
<range>500</range>
</LookAt>
",
a.poi[8].more_info.coord.lon, a.poi[8].more_info.coord.lat); //[8] - focarium
for(i=0; i<a.pl_all_pois.cnt;i++){
//for(i=0; i<3;i++){
// str coord value in temp str
SPrintf(stmp, "%06f,%06f",
a.poi[i].more_info.coord.lon, a.poi[i].more_info.coord.lat);
FPrintf(fout,"
<Placemark>
<name>%s</name>
<description>%s, %s, %s, %s</description>
<Point>
<coordinates>%s,0</coordinates>
</Point>
</Placemark>
",
a.poi[i].pstr_name.str,
a.poi[i].more_info.str_country.str, a.poi[i].more_info.str_region.str,
a.poi[i].more_info.str_city.str, a.poi[i].more_info.str_street.str,
stmp);
}
FPrintf(fout, "\t</Folder>\n");
FPrintf(fout, "%s", kml_footer()); // kml footer
// save and close file
FileSelect(fout);
if( FileSave( filename ) < 0 )
{
MessageBox( idOk, "Save POI", "An error occured writing file '%s' of size %Ld.", filename );
return -1;
}
// crash when uncommenting //FileClose();
return 0;
}
string kml_header(void){
local string s;
s="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
SPrintf(s, "%s<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n", s);
SPrintf(s, "%s\t<Document>\n", s);
return s;
}
string kml_footer(void){
local string s;
s="\t</Document>\n";
SPrintf(s, "%s</kml>", s);
return s;
А вот результат её работы, KML со всеми POI блока, содержащего и POI города Хель на pastebin - уже и не гадко смотрится, 31000 байт.
В конкретном блоке - 116 точек интереса, рассыпаны по Хельской косе в Польше, сосредоточены в поселениях.
Проблемой было сделать скриншот: из-за подписей к пинам, сами пины под ними не особо видны были. Во время перемещения мышкой поверхности в googleearth надписи не рефрешатся, и когда есть еще одна, третья, рука (<fn>
и <prt scr>
на клавиатуре нотника оставшейся рукой не достать) можно сделать попытку снятия изображения. Пришлось просить помощи дочки - выражаю благодарность за помощь.
Итоги.
Полностью разобрана структура блоков типа 0x10, содержащих информацию о точках интереса, POI.
BRIF - название POI, язык, название местности, ссылка на расширенную информацию more_info
MORE_INFO - географические координаты; ссылка на данные в дальнем блоке block_0x00; строковые значения вывеска, улица, город, область, страна, номер дома, телефонный номер, почтовый индекс; уникальный ID; указатель на список доп. информации в блоках типа 0x00.
Иной информации о POI в блоке не содержится: нет возможности понять ни тип POI, ни к какому блоку городов (есть строковые наименования местности) эта точка относится. Традиционное одностороннее информирование - для полной информации требуется информация из вышестоящего блока.
Формирование и вывод в комментариях Variables URI на точку на гугл картах соответствующей POI
Важно! Новая структура-кирпичик LON_LAT, описывающая представление географических координат в carindb.
Функция экспорта всех POI блока типа 0x10 в kml.
Традиционно, продолжение анализа - в следующей статье.
singeorange
Потрясающе! Всякий раз с нетерпением жду следующей серии.