Pull to refresh

04. The Gold-Bug

Reading time15 min
Views3.2K

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.

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

Объекты:

  • маяк в Хеле - 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.

Традиционно, продолжение анализа - в следующей статье.

Tags:
Hubs:
+6
Comments4

Articles