Задача состоит в том, чтобы выяснить, насколько сильно изгибаются дуги, чтобы улучшить их визуальное разрешение.
Вот одно из решений (среди множества возможных). Давайте рассмотрим все дуги, происходящие из общего происхождения. Здесь дуги самые многолюдные. Чтобы разделить их наилучшим образом, давайте расположим их так, чтобы они распределялись под равными углами . Это проблема, если мы рисуем отрезки прямых линий от источника до пунктов назначения, потому что обычно в разных направлениях будут кластеры пунктов назначения. Давайте использовать нашу свободу, чтобы изгибать дуги, чтобы расположить исходящие углы как можно более равномерно.
Для простоты давайте используем круговые дуги на карте. Естественной мерой «изгиба» в дуге от точки y к точке x является разность между ее направлением в y и подшипником непосредственно от y до x . Такая дуга является сектором круга, на котором лежат оба y и x ; элементарная геометрия показывает, что угол изгиба равен половине включенного угла в дуге.
Для описания алгоритма нам понадобится немного больше обозначений. Пусть y будет исходной точкой (как спроецировано на карту), и пусть x_1 , x_2 , ..., x_n будут точками назначения. Определите a_i как отношение от y к x_i , i = 1, 2, ..., n .
В качестве предварительного шага предположим, что подшипники (все от 0 до 360 градусов) расположены в порядке возрастания: для этого необходимо рассчитать подшипники и затем отсортировать их; оба простые задачи.
В идеале мы хотели бы, чтобы подшипники дуг равнялись 360 / n , 2 * 360 / n и т. Д. Относительно некоторого начального подшипника. Таким образом, различия между требуемыми подшипниками и действительными подшипниками равны i * 360 / n - a_i плюс начальный подшипник, a0 . Наибольшая разница - это максимум этих n различий, а наименьшая разница - их минимум. Давайте установим a0 на полпути между максимумом и минимумом; это хороший кандидат на пусковой подшипник, поскольку он сводит к минимуму максимальный изгиб, который может произойти . Следовательно, определить
b_i = i * 360 / n - a0 - a_i:
это изгиб, чтобы использовать .
Элементарной геометрией является рисование дуги окружности от y до x, которая составляет угол 2 b_i, поэтому я пропущу детали и перейду прямо к примеру. Вот иллюстрации решений для 64, 16 и 4 случайных точек, помещенных в прямоугольную карту
Как вы можете видеть, решения, кажется, становятся лучше с увеличением количества пунктов назначения. Решение для n = 4 ясно показывает, как подшипники расположены на одинаковом расстоянии, поскольку в этом случае расстояние равно 360/4 = 90 градусов, и очевидно, что расстояние точно достигнуто.
Это решение не идеально: вы можете определить несколько дуг, которые можно настроить вручную для улучшения графики. Но это не сделает ужасную работу и, кажется, действительно хорошее начало.
Алгоритм также имеет преимущество простоты: самая сложная часть состоит в сортировке пунктов назначения в соответствии с их ориентирами.
кодирование
Я не знаю PostGIS, но, возможно, код, который я использовал для рисования примеров, может служить руководством для реализации этого алгоритма в PostGIS (или любой другой ГИС).
Считайте следующее псевдокодом (но Mathematica выполнит его :-). (Если бы этот сайт поддерживал TeX, как это делают математика, статистика и TCS, я мог бы сделать это намного более читабельным.) Нотация включает в себя:
- Имена переменных и функций чувствительны к регистру.
- [Альфа] - это строчный греческий символ. ([Пи] имеет значение, которое, как вы думаете, должно иметь.)
- x [[i]] является элементом i массива x (индексируется начиная с 1).
- f [a, b] применяет функцию f к аргументам a и b. Функции в собственном случае, такие как «Min» и «Table», определяются системой; функции с начальной строчной буквой, такие как «angles» и «offset», определяются пользователем. Комментарии объясняют любые неясные функции системы (например, «Arg»).
- Таблица [f [i], {i, 1, n}] создает массив {f [1], f [2], ..., f [n]}.
- Круг [o, r, {a, b}] создает дугу окружности с центром в o радиуса r от угла a к углу b (оба в радианах против часовой стрелки от восточного направления).
- Ordering [x] возвращает массив индексов отсортированных элементов x. x [[Ordering [x]]] - отсортированная версия x. Если длина y равна x, y [[Ordering [x]]] сортирует y параллельно с x.
Исполняемая часть кода, к счастью, короткая - менее 20 строк - потому что более половины ее составляют либо декларативные накладные расходы, либо комментарии.
Нарисуй карту
z
это список направлений и y
является источником.
circleMap[z_List, y_] :=
Module[{\[Alpha] = angles[y,z], \[Beta], \[Delta], n},
(* Sort the destinations by bearing *)
\[Beta] = Ordering[\[Alpha]];
x = z[[\[Beta] ]]; (* Destinations, sorted by bearing from y *)
\[Alpha] = \[Alpha][[\[Beta]]]; (* Bearings, in sorted order *)
\[Delta] = offset[\[Alpha]];
n = Length[\[Alpha]];
Graphics[{(* Draw the lines *)
Gray, Table[circle[y, x[[i]],2 \[Pi] i / n + \[Delta] - \[Alpha][[i]]],
{i, 1, Length[\[Alpha]]}],
(* Draw the destination points *)
Red, PointSize[0.02], Table[Point[u], {u, x}]
}]
]
Создайте дугу окружности от точки x
к точке, y
начиная под углом \[Beta]
относительно подшипника x -> y.
circle[x_, y_, \[Beta]_] /; -\[Pi] < \[Beta] < \[Pi] :=
Module[{v, \[Rho], r, o, \[Theta], sign},
If[\[Beta]==0, Return[Line[{x,y}]]];
(* Obtain the vector from x to y in polar coordinates. *)
v = y - x; (* Vector from x to y *)
\[Rho] = Norm[v]; (* Length of v *)
\[Theta] = Arg[Complex @@ v]; (* Bearing from x to y *)
(* Compute the radius and center of the circle.*)
r = \[Rho] / (2 Sin[\[Beta]]); (* Circle radius, up to sign *)
If[r < 0, sign = \[Pi], sign = 0];
o = (x+y)/2 + (r/\[Rho]) Cos[\[Beta]]{v[[2]], -v[[1]]}; (* Circle center *)
(* Create a sector of the circle. *)
Circle[o, Abs[r], {\[Pi]/2 - \[Beta] + \[Theta] + sign, \[Pi] /2 + \[Beta] + \[Theta] + sign}]
]
Вычислить подшипники от начала координат до списка точек.
angles[origin_, x_] := Arg[Complex@@(#-origin)] & /@ x;
Вычислите средние значения остатков набора подшипников.
x
это список подшипников в отсортированном порядке. В идеале, x [[i]] ~ 2 [Pi] i / n.
offset[x_List] :=
Module[
{n = Length[x], y},
(* Compute the residuals. *)
y = Table[x[[i]] - 2 \[Pi] i / n, {i, 1, n}];
(* Return their midrange. *)
(Max[y] + Min[y])/2
]