SASGIS

Веб-картография и навигация

lonlatarr="ADDdJAaB...b8EQAAwX" ...

Форум для обсуждения деталей разработки программы SAS.Планета

Модераторы: vdemidov, Tolik

В продолжение данной темы...

Сообщение Cheetos » 30 мар 2015, 20:32

Несмотря на обильное наличие ответов, вопрос так до конца и не решен.
Дано: Координаты точки TDoublePoint
Необходимо: Получить строку типа string, соответствующую значению lonlatarr в Marks.sml
Решение совсем близко: например, надо "APh9sCqGnJgEQAAYK/XdS4XUBEA4+BIA", имею "APB9sCqGnJgEQAAYK/XdS4XUBEA=".
Пишу на Delphi, любитель. Решив данную задачу обязуюсь выложить ответ здесь на форуме.
Для решения требуются ответы на следующие вопросы:
1. Как и/или где определен тип TExtendedPoint?
2. Какие функции/процедуры используются при кодировании Base64? Свои или сторонние? Если свои, то где их найти?
Аватара пользователя
Cheetos
Новичок
 
Сообщения: 11
Зарегистрирован: 30 мар 2015, 20:03
Откуда: РФ, Тула
Благодарил (а): 2 раз.
Поблагодарили: 0 раз.

Re: lonlatarr="ADDdJAaB...b8EQAAwX" ...

Сообщение zed » 31 мар 2015, 01:29

Cheetos писал(а):Несмотря на обильное наличие ответов, вопрос так до конца и не решен.

Уже по-моему всё давно разложено по полочкам и вопросов быть не должно.
Cheetos писал(а):1. Как и/или где определен тип TExtendedPoint?
2. Какие функции/процедуры используются при кодировании Base64? Свои или сторонние? Если свои, то где их найти?

1. В юните src\MarksDB\SML\t_GeometryPointSML.pas
2. Стандартный base64, например, можете взять отсюда Includes\EDBase64.pas
zed
Гуру
 
Сообщения: 2888
Зарегистрирован: 16 авг 2008, 20:21
Благодарил (а): 89 раз.
Поблагодарили: 568 раз.

Re: lonlatarr="ADDdJAaB...b8EQAAwX" ...

Сообщение Cheetos » 31 мар 2015, 22:31

1. Создаю новый файл.
2. Кидаю на форму Button1 и Memo1.
3. Подключаю в uses EDBase64.
4. Описываю следующие типы:
Код: Выделить всё
type
  TGeometryPointSML = packed record
    X: Extended;
    Y: Extended;
    Reserved: LongWord; // proper record aligment for backward compatibility
  end;
  PGeometryPointSML = ^TGeometryPointSML;

type
  TDoublePoint = record
    X: Double;
    Y: Double;
  end;


5. Создаю процедуру преобразования координат в строку lonlatarr:
Код: Выделить всё
procedure TForm1.Decode64( const DblPoint: TDoublePoint);
var
  AStream: TMemoryStream;
  VPoint: TGeometryPointSML;
  S: AnsiString;
begin
  try
    AStream:=TMemoryStream.Create;
    VPoint.X := DblPoint.X;
    VPoint.Y := DblPoint.Y;
    AStream.Write(VPoint, SizeOf(VPoint));
    AStream.Position:=0;
    SetString(S, PAnsiChar(AStream.Memory), AStream.Size);
    Memo1.Lines.Add(Base64Encode(S));
    Memo1.Lines.Add('AMDOiYxwf5YEQABQ1XmjuP/eBEA4+BIA');
  finally
    AStream.Free;
  end;
end;

6. В Button1 прописываю:
Код: Выделить всё
procedure TForm1.Button1Click(Sender: TObject);
var
  DP: TDoublePoint;
begin
  DP.X:=37.62445277777778;
  DP.Y:=55.74972777777778;
  Decode64(DP);
end;

7. В итоге:
Должно быть:
AMDOiYxwf5YEQABQ1XmjuP/eBEA4+BIA
Получилось:
AMjOiYxwf5YEQABQ1XmjuP/eBEAAYx4C

Очень похоже, но не то. Где собака зарыта?
Аватара пользователя
Cheetos
Новичок
 
Сообщения: 11
Зарегистрирован: 30 мар 2015, 20:03
Откуда: РФ, Тула
Благодарил (а): 2 раз.
Поблагодарили: 0 раз.

Re: lonlatarr="ADDdJAaB...b8EQAAwX" ...

Сообщение zed » 31 мар 2015, 23:02

А с чего вы решили, что у координат именно столько знаков после запятой? По-моему, тут проблема с точностью округления.
zed
Гуру
 
Сообщения: 2888
Зарегистрирован: 16 авг 2008, 20:21
Благодарил (а): 89 раз.
Поблагодарили: 568 раз.

Re: lonlatarr="ADDdJAaB...b8EQAAwX" ...

Сообщение vdemidov » 31 мар 2015, 23:13

А еще не забываем о 4-х байтах мусора, которые появляются из-за неупакованности структуры.
Чтобы понять программу, вы должны стать одновременно и машиной, и программой.
Аватара пользователя
vdemidov
Гуру
 
Сообщения: 1687
Зарегистрирован: 12 дек 2008, 13:10
Откуда: Киев
Благодарил (а): 191 раз.
Поблагодарили: 157 раз.

Re: lonlatarr="ADDdJAaB...b8EQAAwX" ...

Сообщение Cheetos » 31 мар 2015, 23:37

zed писал(а):А с чего вы решили, что у координат именно столько знаков после запятой? По-моему, тут проблема с точностью округления.

1. Координаты взяты из файла marks.sml (для точки lonL=LonR, latT=LatB)
2. Если бы была проблема с округлением, то в результате я бы получил точку с координатами отличными от заданной, но близкую к ней.
Аватара пользователя
Cheetos
Новичок
 
Сообщения: 11
Зарегистрирован: 30 мар 2015, 20:03
Откуда: РФ, Тула
Благодарил (а): 2 раз.
Поблагодарили: 0 раз.

Re: lonlatarr="ADDdJAaB...b8EQAAwX" ...

Сообщение Cheetos » 31 мар 2015, 23:39

vdemidov писал(а):А еще не забываем о 4-х байтах мусора, которые появляются из-за неупакованности структуры.

1. Про какую неупакованную структуру идет речь?
2. Sas.Планета как-то умудряется прочитать эти данные
Аватара пользователя
Cheetos
Новичок
 
Сообщения: 11
Зарегистрирован: 30 мар 2015, 20:03
Откуда: РФ, Тула
Благодарил (а): 2 раз.
Поблагодарили: 0 раз.

Re: lonlatarr="ADDdJAaB...b8EQAAwX" ...

Сообщение zed » 31 мар 2015, 23:58

Cheetos писал(а):Координаты взяты из файла marks.sml (для точки lonL=LonR, latT=LatB)

Там текстовое округлённое представление, а в lonlatarr лежит сырая бинарь. Вы уж если хотите сравнить, то создавайте в SAS тестовую точку вручную, т.е. координаты вводите сами, с клавиатуры, а затем эти же координаты из строки преобразуйте в Double и сравнивайте с тем, что вышло в sml.
Cheetos писал(а):Про какую неупакованную структуру идет речь?

Вы забыли проинициализировать поле VPoint.Reserved и там мусор. Точно так же и SAS его не инициализирует, так что хвост и не сойдётся.

Плюс, не забывайте про директиву {$A4}, а то Delphi XE, по-умолчанию уже выравнивает по 8 байт, а не по 4 как D2007.

P.S. Коль вы юзаете Delphi, то мне вообще не понятны ваши движения - почему не взять DataSet и работать с sml так же как и SAS, без ненужной ручной мороки с парсингом и кодированием.

За это сообщение автора zed поблагодарил:
Cheetos (03 апр 2015, 20:04)
Рейтинг: 5.26%
 
zed
Гуру
 
Сообщения: 2888
Зарегистрирован: 16 авг 2008, 20:21
Благодарил (а): 89 раз.
Поблагодарили: 568 раз.

Re: lonlatarr="ADDdJAaB...b8EQAAwX" ...

Сообщение Banzay » 03 апр 2015, 00:33

RGF писал(а):гуру, приведите пожалуйста если не алгоритм, то хотя бы кусок кода, в котором происходит кодирование из широты/долготы в lonlatarr


Так получилось, что по работе потребовалось формировать sml-файлы из большой базы с координатами. Но, в силу специфики предприятия, средств для разработки серьезного ПО нет, а если и удастся разработать ПО на стороне, то по работе использовать его не будет возможности из-за политик безопасности. Для реализации задачи использовал "костыль" - VBA, встроенный в MS Excel.
Т.к. в самом VBA не имеется встроенного типа данных с плавающей точкой, аналога Extended, пришлось изобретать его... но прежде, пришлось постигнуть теорию представления чисел с плавающей точкой в машинном коде, которую напрочь забыл с институтской скамьи.
Результатом стал алгоритм, который описывает подробное преобразование из переменной типа Double, в код lonlatarr, который может стать интересным, осваивающим этот алгоритм с нуля. Естественно, никаких претензий на оптимальность алгоритма нет, над этим буду работать в перспективе, поэтому прошу меня не критиковать за быдлокод медленную работу алгоритма.

Итак, алгоритм состоит из нескольких этапов:
    1. Разложение числа с плавающей точкой в массив 80-байт, каждый байт которого является битом в бинарном представлении числа с плавающей точкой формата Extended;
    2. Объединение двух битовых массивов конвертированных координат в один 192-байтовый массив;
    3. Преобразование 192-байтового массива в строку lonlatarr

Собственно код:
Код: Выделить всё
Option Explicit

Private Sub DblToByteArray(ByVal Dig As Double, ByRef Data() As Byte)
'=========================================================
' Процедура преобразования Double-числа в битовый массив
'=========================================================
Const CNT = 80

Dim tDATA(1 To CNT) As Byte
Dim D As Double
Dim i As Long
Dim j As Long
Dim A As Long
Dim Bt As Byte
Dim B As Double
Dim S As String
Dim Exp As Long
   
'===== Проверка знака числа =====
If Dig < 0 Then
    Data(1) = 1
    D = Abs(Dig)
Else
    D = Dig
End If

'===== Проверка размерности числа =====
If D > 1 Then
    '== Представление числа |X| > 1 ==
    '== Разложение на биты целой части числа ==
    A = Fix(D)
    i = 1
   
    Do While A > 1
        tDATA(i) = A Mod 2
        A = A \ 2
        i = i + 1
    Loop
    tDATA(i) = 1
   
    For j = 1 To i
        Data(17 + i - j) = tDATA(j)
    Next j
   
    '== Вычисление значения экспоненты и разложение его на биты ==
    Exp = 16384 + i - 2
   
    i = 17 + i
   
    j = 16
    Do While Exp > 1 And j > 1
        Data(j) = Exp Mod 2
        Exp = Exp \ 2
        j = j - 1
    Loop
    Data(j) = 1
   
    '== Разложение на биты дробной части числа ==
    A = Round((D - Fix(D)) * 1000000, 0)
   
    Do While i <= 80
        A = A * 2
        If A > 1000000 Then
            Data(i) = 1
            A = Fix(A - 1000000)
        Else
            Data(i) = 0
        End If
        i = i + 1
    Loop
ElseIf Dig <= 0.000001 Then
    '== Представление числа |X|=0 (условно |X| <= 0.000001 )==
    For i = 1 To 80
        Data(i) = 0
    Next i
Else
    '== Представление числа 0<|X|<1 ==
   
    '== Вычисление значения экспоненты и разложение его на биты ==
   
    '== Вычисление смещения ==
    A = Round((D - Fix(D)) * 1000000, 0)
    j = 1
    Do While A < 1000000
        A = A * 2
        j = j + 1
    Loop
    Data(17) = 1
    A = Fix(A - 1000000)
    '=========================
           
    Exp = 16384 - (j + 1)
   
    j = 16
    Do While Exp > 1 And j > 1
        Data(j) = Exp Mod 2
        Exp = Exp \ 2
        j = j - 1
    Loop
    Data(j) = 1
   
    '== Разложение на биты дробной части числа ==
    i = 18
    Do While i <= 80
        A = A * 2
        If A > 1000000 Then
            Data(i) = 1
            A = Fix(A - 1000000)
        Else
            Data(i) = 0
        End If
        i = i + 1
    Loop
   
End If

'== инверсия байтов ==
For i = 0 To 4
    For j = 1 To 8
        tDATA(j) = Data(i * 8 + j)
    Next j
    For j = 1 To 8
        Data(i * 8 + j) = Data(79 - (i + 1) * 8 + j)
    Next j
    For j = 1 To 8
        Data(80 - (i + 1) * 8 + j) = tDATA(j)
    Next j
Next i

End Sub

Private Function ByteArrToBase64(ByRef XCoord() As Byte, ByRef YCoord() As Byte) As String
'============================================================
'     Формирование строки lonlatarr из битовых массивов
'============================================================
Dim BinDATA(1 To 192) As Byte
Dim tB64()
Dim i As Long
Dim j As Long
Dim B64 As String
Dim A As Byte

tB64 = Array("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", _
"Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", _
"j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", _
"2", "3", "4", "5", "6", "7", "8", "9", "+", "/")

i = 1

'==== Составление единого массива 192 бит ====
For j = 1 To 80
    BinDATA(i) = XCoord(j)
    i = i + 1
Next j
For j = 1 To 80
    BinDATA(i) = YCoord(j)
    i = i + 1
Next j
'=============================================

'==== формирование строки lonlatarr ====
For i = 1 To 192 Step 6
    A = 32 * BinDATA(i) + 16 * BinDATA(i + 1) + 8 * BinDATA(i + 2) + 4 * BinDATA(i + 3) + 2 * BinDATA(i + 4) + 1 * BinDATA(i + 5)
    B64 = B64 & tB64(A)
Next i
'=======================================

ByteArrToBase64 = B64

End Function

Private Function CoordToBase64(ByVal XCoord As Double, ByVal YCoord As Double) As String
'============================================================
' Формирование кода lonlatarr из двух переменных типа Double
'============================================================
Dim X(1 To 80) As Byte
Dim Y(1 To 80) As Byte

DblToByteArray XCoord, X()
DblToByteArray YCoord, Y()

CoordToBase64 = ByteArrToBase64(X(), Y())

End Function


Private Sub CommandButton2_Click()

With Worksheets(1)
    .Cells(2, 3).NumberFormat = "@"
    .Cells(2, 3) = CoordToBase64(.Cells(2, 1), .Cells(2, 2))
End With

End Sub


В ходе экспериментов с разложением числа с плавающей точкой пришел к выводу, что озвученный в теории метода умножения дробной части числа на 2 до появления "1" в целой части дал огромную погрешность начиная с 40-50 бита в бинарном представлении, что приводило к отклонению от реальных координат почти на 50 метров, в результате реализовал целочисленный механизм преобразования дробной части, который дал точный результат (сравнительный пример см. во вложении, в расчетах формулами, где так же можно наглядно посмотреть разложение формулами кода lonlatarr в битовый массив и рабочий макрос формирования поля lonlatarr).
Единственное, что для меня осталось загадкой по итогам реализации алгоритма, так это то, почему для формирования lonlatarr используется 24 байта данных (4 из которых - мусор), а не 21 (1 байт - мусор), ведь 21 байт дают устойчивое представление в виде BASE64 кода, с меньшим количеством символов.

P.S. В ходе изучения теории преобразования чисел с плавающей точкой, пришел к выводу, что преобразовать из других типов в аналог Extended будет весьма затруднительным в силу того, что у Extended "1" до точки явная, в то время как у других типов она не явная и в битовое представление записывается лишь код дробной части нормализованного числа в бинарном представлении, плюс, характеристика у Extended составляет 15 бит, что значительно больше других типов. Озвученное не дает возможности использовать простое копирование байтов из памяти, например, средствами CopyMemory (что по-началу было опробовано) и добавлением в "хвост" нулевых байтов.
Вложения
ConvertToBase64.zip
Алгоритм конвертации кода lonlatarr в битовый массив средствами MS Excel + макрос по конвертации двух координат в код lonlatarr
(41.22 KiB) Скачиваний: 246
Последний раз редактировалось Banzay 03 апр 2015, 23:53, всего редактировалось 4 раз(а).
Banzay
Новичок
 
Сообщения: 2
ICQ: 238208421
Зарегистрирован: 02 апр 2015, 00:21
Откуда: РФ, Саратов
Благодарил (а): 1 раз.
Поблагодарили: 0 раз.

Re: lonlatarr="ADDdJAaB...b8EQAAwX" ...

Сообщение zed » 03 апр 2015, 00:57

Banzay писал(а):почему для формирования lonlatarr используется 24 байта данных (4 из которых - мусор)

Это было очень неудачное решение на заре развития программы. Писать её начинали студенты-программисты, поэтому вот такое наследие: и мусорные 4 байта и монструозный Extended.

За это сообщение автора zed поблагодарили: 2
Banzay (03 апр 2015, 14:45) • vdemidov (03 апр 2015, 12:39)
Рейтинг: 10.53%
 
zed
Гуру
 
Сообщения: 2888
Зарегистрирован: 16 авг 2008, 20:21
Благодарил (а): 89 раз.
Поблагодарили: 568 раз.

Пред.След.

Вернуться в Раздел для разработчиков программы SAS.Планета

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1