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 https://www.openstreetmap.org/way/260631220 ,
ia ib ic: 1029E480 12147BC0 16400001: latarnia morska hel
-
тюлений питомник в Хеле — Fokarium https://www.openstreetmap.org/way/49300162#map=19/54.60687/18.80012,
ia ib ic: 1028C700 12152180 16300001: fokarium
-
Баптистская христианская церковь в Мальброке — Kościół Chrześcijan Baptystów https://www.openstreetmap.org/way/261512876#map=19/54.03130/19.02970,
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 POIsblock(0x150DC08); // [5] tp:CH_idx_11(11) of:A870A843B0D48h8hFg: 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: _Polish608C98h8hFg: 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.
Традиционно, продолжение анализа — в следующей статье.
ссылка на оригинал статьи https://habr.com/ru/post/645355/
Добавить комментарий