Вигнуті "карти маршруту" від точки до точки


39

Нещодавно я переглядав веб-сторінки авіакомпаній, на яких відображаються маршрути, що від'їжджають до певного міста до всіх інших міст, які вони обслуговують. Мені б хотілося створити схожі зігнуті маршрути між точками. Хтось створив сценарії чи функції, які будуть генерувати вигнуті дуги, як показані в цьому прикладі ?

Шляхи польоту

Чи є у PostGIS реалізація ST_MakeLine, яка б дозволила вам вказати об'єм кривої, який слід використовувати під час підключення 2 точок?

У той час як я зараз використовую PostGIS і QGIS, я б радий почути про інші варіанти програмного забезпечення, які могли б створити такий самий вигляд.


Хтось знає про якісь приємні реалізації цього? Приклади чи що завгодно?
Марк Боулдер

Відповіді:


26

Створення чудових кіл може дати вам бажаний ефект.

Можливо, щось подібне обговорювалося на http://lists.osgeo.org/pipermail/postgis-users/2008-February/018620.html

Оновлення:

Я продовжував цю ідею у "Візуалізації глобальних зв'язків" . Це суто PostGIS-рішення, що використовує перепроекцію для створення дуг.

SELECT ST_Transform(
  ST_Segmentize(
    ST_MakeLine(
      ST_Transform(a.the_geom, 953027),
      ST_Transform(b.the_geom, 953027)
    ), 
  100000), 
4326)

(Визначення CRS для 953027 можна знайти тут: http://spatialreference.org/ref/esri/53027/ )

введіть тут опис зображення


4
Мені подобається ідея, хоча з великими колами проблема, з якою ви стикаєтесь, полягає в тому, що на менших відстанях ви все ще опиняєтеся як правило прямою лінією. Я хотів би мати можливість контролювати кількість дуги, яку я вкладаю в лінію (тобто довжина дуги = відстань * 2).
РайанДалтон

1
Ось хороший приклад проблеми з просто використанням великих кружків: gc.kls2.com/cgi-bin/…
RyanDalton

1
Після деяких додаткових досліджень я знайшов цю публікацію, яка може бути корисною для надання допомоги цьому методу. mail-archive.com/postgis-users@postgis.refractions.net/…
RyanDalton

Для використання майбутнім читачем я подумав, що я просто продовжую посилатися на нещодавню публікацію в блозі @ underdark, яка висвітлює цю тему. underdark.wordpress.com/2011/08/20/…
RyanDalton

Це чудово!! Використовується в моєму проекті, щоб намалювати лінії між реєстраціями користувачів та місцями розташування, підібраними з Forsquare
Лоренцо Барбаглі

24

Проблема полягає в тому, щоб розібратися, на скільки згинати дуги, щоб підвищити їх візуальне дозвіл.

Ось одне рішення (серед безлічі можливих). Розглянемо всі дуги, що випливають із загального походження. Дуги тут найпотужніші. Щоб їх найкраще відокремити, давайте розставимо так, щоб вони розтікалися в однаково розташованих кутах . Проблема, якщо ми прорисуємо відрізки прямих до напрямків прямих ліній, оскільки, як правило, будуть кластери напрямків у різних напрямках. Скористаємося своєю свободою для згинання дуг, щоб максимально рівномірно розташувати кути, що відходять.

Для простоти давайте на карті використовувати кругові дуги. Природною мірою "вигину" в дузі від точки 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 на півдорозі між max та min; це хороший кандидат для стартового підшипника, оскільки він мінімізує максимальну кількість вигину, яка відбудеться . Отже, визначте

b_i = i * 360 / n - a0 - a_i:

це згинання у використанні .

Справа в елементарній геометрії, щоб намалювати кругову дугу від y до x, яка подає кут у 2 b_i, тому я пропускаю деталі та переходжу прямо до прикладу. Ось ілюстрації рішень для 64, 16 та 4 випадкових точок, розміщених у межах прямокутної карти

alt текст

alt текст

alt текст

Як бачите, рішення, здається, стають приємнішими, оскільки кількість пунктів призначення збільшується. Рішення для n = 4 чітко показує, як підшипники однаково розташовані, оскільки в цьому випадку інтервал дорівнює 360/4 = 90 градусів, і очевидно, що точно досягнуто відстань.

Це рішення не є ідеальним: ви, ймовірно, можете виділити кілька дуг, які можна було вручну підлаштувати для покращення графіки. Але це не зробить жахливої ​​роботи і, здається, це справді вдалий початок.

Алгоритм також має перевагу в тому, що він простий: найскладніша частина складається з сортування пунктів призначення за їхніми підшипниками.


Кодування

Я не знаю PostGIS, але, можливо, код, який я використовував для малювання прикладів, може послужити керівництвом для впровадження цього алгоритму в PostGIS (або будь-який інший ГІС).

Розглянемо наступне як псевдокод (але Mathematica виконає це :-). (Якщо цей сайт підтримував TeX, як це робить математика, статистика та TCS, я можу зробити це набагато читабельніше.) Позначення включає в себе:

  • Імена змінних та функцій залежать від регістру.
  • [Альфа] - грецький символ із малих букв. ([Pi] має значення, яке, на вашу думку, повинно мати.)
  • x [[i]] - це елемент i масиву x (індексується починаючи з 1).
  • f [a, b] застосовує функцію f до аргументів a і b. Функції у відповідному випадку, такі як "Min" та "Table", визначені системою; функції з початковою малою літерою, такі як "кути" та "зміщення", визначені користувачем. У коментарях пояснюються будь-які незрозумілі функції системи (наприклад, 'Arg').
  • Таблиця [f [i], {i, 1, n}] створює масив {f [1], f [2], ..., f [n]}.
  • Коло [o, r, {a, b}] створює дугу кола, центрованого в o радіусом r від кута a до кута b (обидва в радіанах проти годинникової стрілки з-за сходу).
  • Порядок [x] повертає масив індексів відсортованих елементів x. x [[Замовлення [x]]] - це відсортована версія x. Коли y має таку ж довжину, що і x, y [[Порядок [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
]

Слід зазначити, що це рішення передбачає, що напрямки більш-менш оточують походження. Якщо це не так, вся ідея (про однаково розташованих підшипниках) не є хорошою. Але це можна легко виправити, ввівши деякі підроблені напрямки в кутових прогалинах і пізніше видалити ці пункти призначення (та їх дуги). Цей процес можна автоматизувати, обчисливши середню відстань між підшипниками та використовуючи його для ідентифікації великих зазорів тощо .
whuber

Приємна графіка. Цікаво, чи авіакомпанії використовують автоматизований інструмент, коли складають карти маршрутів, показані на звороті журналу польотів.
Кірк Куйкендалл

1
@Kirk Вони, ймовірно, платять комусь, щоб зробити картографію вручну :-). Мене надихнуло це питання, щоб зрозуміти, чи може простий підхід створити досить гарну графіку. Відповідь виглядає багатообіцяючо. До речі, ці графіки були створені Mathematica 8, використовуючи примітиви Circle and Point та трохи векторної арифметики для пошуку центрів кола.
whuber

Мені подобається результат, який ти показав, і мені це шлях. Я, чесно кажу, вважаю себе технічним, але я трохи загубився у формулі, яку ви дали, і тому перетворити це на код PostGIS стає майже неможливим. Хтось із них має ідеї щодо того, як перетворити концепцію Уаубера в працездатний код? Я спробую переглянути і дати йому відгук, але допомога буде дуже вдячна.
RyanDalton

@ whuber- Дякую за оновлений псевдокод. Нам доведеться дізнатися, чи реально це можна реалізувати в PostGIS.
RyanDalton

5

Спробуйте ST_CurveToLine

Щось на зразок:

SELECT ST_CurveToLine('CIRCULARSTRING(1 1,5 3,10 1)'::geometry) as the_geom;

Ви можете візуалізувати це, скопіювавши запит у текстове поле та натиснувши Map1 за адресою http://www.postgisonline.org/map.php


Я в кінцевому підсумку намагався це викривити набір рядків з "двох точок".
Брент Едвардс


3

Я в кінцевому підсумку спробував це викривити набір рядків з "двох точок", використовуючи функцію ST_CurveToLine, як запропонував @Nicklas Avén.

Я передав наступні 3 набори координат функції ST_OffsetCurve:

  1. Початок початкового рядка
  2. Середина лінії зміщена паралельно початковій лінії
  3. Кінець початкового рядка

Я використовував функцію ST_OffsetCurve для обчислення зміщення - 1/10 довжини вихідного рядка в моєму прикладі.

Ось SQL, який я використовував для генерації вигнутих ліній з оригінальних прямих:

    ST_CurveToLine('CIRCULARSTRING(' || st_x(st_startpoint(the_geom)) || ' ' || st_y(st_startpoint(the_geom)) || ', ' || st_x(st_centroid(ST_OffsetCurve(the_geom, st_length(the_geom)/10, 'quad_segs=4 join=bevel'))) || ' ' || st_y(st_centroid(ST_OffsetCurve(the_geom, st_length(the_geom)/10, 'quad_segs=4 join=bevel'))) || ', ' || st_x(st_endpoint(the_geom)) || ' ' ||  st_y(st_endpoint(the_geom)) || ')') AS the_curved_geom

Дійсно корисний, але чомусь результат не поважає мою сітку. Будь-яка ідея чому?
DMS02

Чи можете ви надати ще детальну інформацію - сітка вхідної геометрії, вихідна сітка відсутня, відрізняється, створюються помилки (які додатки - QGIS, PostgreSQL).
Брент Едвардс

Таблиця, куди я хочу вставити отримані вигнуті лінії, має обмеження "Execute_srid_geom". Коли я виконую запит, я отримую помилку, кажучи, що цей запит порушує це обмеження. З таблицею без обмежень вона працює, але потім, додаючи її до QGIS, вона перераховується з srid 0. Мій запит: ВСТАВКА INTO test (the_curved_geom) SELECT [тут ваш SQL] З рядків
DMS02

Спробуйте запустити функції postgis.net/docs/ST_GeometryType.html та postgis.net/docs/ST_SRID.html на стовпці геометрії (the_curved_geom) і перевірте, чи є конфлікти з вашою тестовою таблицею і Execute_srid_geom. Якщо це так, ви можете перетворити геометрію / srid за потребою або змінити тестову таблицю / обмеження.
Brent Edwards
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.