04. The Gold-Bug

от автора

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:

Тип блоков 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.

Самое приятное, у меня есть возможность сопоставить значения из этих двух миров для каких-то конкретных объектов.

Объекты:

В 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *