Інтернет = комп'ютер
 
Останні записи
  •  

    Використання WPF для візуалізації картографічних об'єктів


    Введення. У зв'язку з широким розповсюдженням комп'ютерних технологій, що дозволяють здійснювати позиціонування об'єктів на земній поверхні, прикладами яких є GPS і ГЛОНАС, з'явилася унікальна можливість створювати такі комп'ютерні системи, які можна використовувати в повсякденній діяльності підприємства без значних витрат. Всім відомі такі системи відстежування автотранспортних засобів автобусних підприємств, медичних установ, а також персональні автомобільні навігатори. Всі ці системи об'єднує те, що вони всі в своїй основі мають картографічний модуль, що відображає інтерактивну карту і дозволяючий каким- або способом відстежувати заданий об'єкт. Наша фірма теж розробила систему для контролю автотранспортних засобів підприємств. Система складається з декількох частин. Відображення карти і автотранспортних засобів здійснює клієнтський модуль, написаний на C#. Net 3.5 WPF. Зовнішній вигляд клієнтського модуля представлений на малюнку. У даній статті ми розглянемо тільки питання, що відносяться до відображення картографічних об'єктів. Приклад до статті є максимально простою геоінформаційною системою з двома рівнями масштабування без завантаження областей, що не потрапляють в зону видимості, без пошуку, але з можливістю переміщення карти і отримання інформації про об'єкт. Питання відстежування переміщення транспортних засобів, з їх відображення на карті і подібні речі розглянуті не будуть, оскільки цілком логічно можуть бути виведені з викладеного матеріалу самостоятельнохраніліще даннихв теперішній час одним з самих широко поширених форматів для зберігання картографічної інформації є файли формату shp (ESRI Shapefile) [1]. Проте вони не дуже зручні у використанні, оскільки в стандарті не визначені механізми пошуку і вибірки об'єктів по певних критеріях. Хоча і самі розробники і сторонні фірми роблять спроби побудови спеціальних індексних файлів для таких цілей. Одним з таких безкоштовно поширюваних компонентів є Sharpmap [2]. Він дозволяє здійснювати всі необхідні дії при роботі з геоінформаційними даними, проте як показали тести об'єм споживаної пам'яті і продуктивність залишають бажати кращого, крім того компонент розповсюджується з ліцензією LGPL, що не дуже бажано для комерційної розробки, оскільки при внесенні змін потрібно здійснювати постачання з початковим кодом. Геоінформаційна система припускає можливості зміни масштабу карти, а також переміщення і пошуку картографічних об'єктів, що викликає ряд технічних проблем, наприклад, завантаження ще не завантажених областей, вивантаження неактуальної інформації і тому подібне Попередні тести показали неприйнятну продуктивність при використанні бібліотеки Sharpmap, тому було вирішено зробити імпорт картографічної інформації в СУБД. Як СУБД на первинному етапі було вирішено використовувати Microsoft SQL Server Compact Edition 3.5 [3]. Ця СУБД є встраєваємой в додаток, входить в постачання Visualstudio 2008, не має обмежень на розповсюдження, забезпечує хорошу продуктивність, легко може бути замінена практично на будь-яку іншу СУБД, що забезпечує велику продуктивність, наприклад на Microsoft SQL Server 2008 [4]. Це рішення є дуже гнучким, оскільки дозволяє здійснювати сегментацію програмного продукту, тобто версія програми не призначена для роботи в мережі може використовувати локальні бази даних з картами, а в мережевій версії карти зберігаються на центральному сервері, що має ряд своїх переваг, наприклад, легке оновлення. Після того, як була написана процедура імпорту з файлів формату shp в СУБД і отримані перші результати по відображенню картографічної інформації, виникло непереборне бажання порівняти різні вбудовувані СУБД на продуктивність. Це було зробити легко, оскільки тепер запити на вибірку об'єктів здійснюються на стандартному SQL. Ми протестували Firebird, Sqlite, DBF, MSSQL, результати приведені в таблиці 1. Таблиця 1. Порівняння різних СУДБ по середній продуктивності. Местосубд1mssql 2008 на виділеному сервере2mssql 2008 на одній машині з клієнтом3dbf4mssql CE 3.55firebird Embedded6sqliteподробності процедури тестування і обговорення результатів виходять за рамки даної статті. Уважні читачі можуть відмітити, що ми не проводили випробування СУБД Oracle і можуть провести цю процедуру самі. В рамках статті ми розглядатимемо реалізацію на MSSQL CE 3.5. Для реалізації нашої системи цілком достатньо однієї таблиці із структурою, представленою в таблиці 2. Таблиця 2. Структура таблиці бази даних. Імятіп даннихопісанієidintпервічний ключnamenvarchar(255) Ім'я об'ектаdescriptionnvarchar(255) Опис об'ектаdataimageданниєobjecttypetinyintтіп об'ектаelementtypetinyintтіп елементів об'ектаzoomtinyintмасштабobjectidintідентіфікатор об'ектаelementidintідентіфікатор елементу об'ектаcomplexbitпрізнак складеного елементавиборка об'єктів для відображення для потрібного масштабу легко здійснюється запитом вигляду: select Objectid, Objecttype, Name as Objectname, Elementtype, Data, Description, Complex, Elementid from Mapobject where @Zoom = Zoom order by Objecttypeподробності реалізації цієї функціональності можна подивитися в прикладі, що додається. Додаткові функції легко реалізуються зміною цієї таблички, і можливо додаванням декілька інших. У реальному застосуванні у нас є 5 таблиць, з яких 3 призначені для пошуку об'єктів. Вибір способу отрісовки. WPF використовує векторну графіку як формат отрісовки, це означає що спеціальним чином зберігається і передається підсистеми виводу набір інструкцій, які описують як саме необхідно виконати отрісовку, використовуючи графічні примітиви, такі як набір ліній, кривих і інших команд. Це дозволяє легко здійснювати масштабування без втрати якості. WPF надає на вибір два способи отрісовки. Це об'єкти Drawing і Shape. Об'єкти Shape представлені набором примітивів Rectangle, Ellipse і так далі І легко можуть бути використані безпосередньо в розмітці xaml, підтримують вирівнювання і обробку подій, проте продуктивність системи при отрісовке достатньо великої кількості таких об'єктів залишає бажати кращого. Об'єкти Drawingvisual забезпечують максимальну продуктивність при отрісовке фігур, картинок або тексту. Це досягається за рахунок того, що вони не підтримують компоновки і подій. До того ж їх не можна описати в розмітці. Для їх використання необхідно створити контейнер, успадкований від класу Frameworkelement. Для зберігання об'єктів Drawingvisual потрібно використовувати клас Visualcollection. Для забезпечення компоновки контейнера необхідно перекрити дві прості функції. Public class Baserenderer: Frameworkelement { //member for optimize protected Visualcollection Objectchildrenlist; protected override int Visualchildrencount { get { return Objectchildrenlist. Count; } } protected override Visual Getvisualchild(int index) { return Objectchildrenlist[index]; }}Созданные об'єкти Drawingvisual мають бути поміщені в колекцію Objectchildrenlist для того, щоб графічна підсистема WPF змогла здійснити їх отрісовку. Причому, для максимальної швидкодії необхідно використовувати функцію Add і додавати об'єкти послідовно. У іншому випадку, наприклад при використанні функції Insert здійснюється перерахунок візуального батька у всіх елементів з індексом, великим чим індекс вставки. Аналоїчним образом поводиться функція Remove. Тому краще очищати колекцію повністю. У цьому легко переконатися використовую будь-який профілювальник, наприклад jetbrance[5] або подивившись початкові коди, наприклад, використовуючи Reflector [6]. Отже, спочатку створюється об'єкт Mapdrawingvisual, який є спадкоємцем об'єкту Drawingvisual і має посилання на бізнес об'єкт карти, для виконання процедури Hittesting (перевірка попадання). Потім виходить стиль для отрісовки об'єкту, створюється геометрія і проводиться сама отрісовка. Private void Createdrawing(Mapelement element, bool closed){ Mapdrawingvisual drawingvisual = new Mapdrawingvisual(); drawingvisual. Mapobject = element. Mapobject; Drawingcontext drawingcontext = drawingvisual. Renderopen(); Geometrystyle style = Stylemanager. Getstyle(element); Geometry geometry = Createbasegeometry(element, closed); drawingcontext. Drawgeometry(style. Brush, style. Pen, geometry); drawingcontext. Close(); Adddrawingvisual(drawingvisual); }private void Adddrawingvisual(Drawingvisual drawingvisual){ Objectchildrenlist. Add(drawingvisual); }Малювання простих графічних примітивів здійснюється за допомогою класу Streamgeometry, який забезпечує максимальну продуктивність. Крім того, продуктивність можна ще збільшити, якщо її «заморозити» (Freeze). В більшості випадків це має сенс, оскільки у нас об'єкти карти не призначені для модифікації. Private static Geometry Creategeometry(Point[] points, bool closed, bool freeze){ Geometry geometry = new Streamgeometry(); using (Streamgeometrycontext ctx = ((Streamgeometry)geometry). Open()) { ctx. Beginfigure(points[0], true, closed); ctx. Polylineto(points, true, false); } // Freeze the geometry (make it unmodifiable) // for additional performance benefits. If (freeze) { geometry. Freeze(); } return geometry; }Кисти, пір'я, текст, переміщення об'єктів і інші питання оптімізациів силу специфіки додатку доводиться виконувати отрісовку об'єктів різних типів: удома, річки, дорогі і інше. Причому їх кількість вимірюється сотнями. На жаль, просте рішення, що приходить на думку в цьому випадку, а саме використання одного об'єкту кисті для одного типу об'єкту, викликає значне уповільнення роботи системи. І нам знову довелося займатися пошуками оптимального способу створення кистей і пір'я для поліпшення продуктивності. Створення нової кисті безпосередньо перед використанням також не привело до значного поліпшення продуктивності. Максимального ефекту вдалося добитися використовую деяку кисть або перо як шаблонні і отримуючи з них за допомогою методу Getcurrentvalueasfrozen() копії для використання. Приведений нижче код спрощений. Public Geometrystyle Clone(){ return new Geometrystyle { Brush = (Brush)Brush. Getcurrentvalueasfrozen(), Pen = (Pen)Pen. Getcurrentvalueasfrozen() }; }В прикладі до статті не приведена реалізація підписів об'єктів, проте це питання заслуговує на те, щоб сказати про нього декілька слів. У нашому застосуванні ми підписували об'єкти на тлі напівпрозорого прямокутника. Його розміри залежать від величини тексту. Проте логічне рішення використовувати ширину і висоту об'єкту Formattedtext для отрісовки прямокутника, а потім отрісовки тексту вище за нього не оптимально. Перерахунок відбувається неодноразово. Щоб цього уникнути, потрібно спочатку виконати отрісовку тексту, отрісовку прямокутника, а потім додати отримані об'єкти Drawingvisual в колекцію візуальних об'єктів в потрібному порядку. Перемещиніє карти за допомогою миші є досить простий завданням і фактично полягає в правильному обчисленні параметрів для класу Translatetransform що виконує зрушення об'єктів. Використовуючи транформациі можна створити ряд досить хитромудрих ефектів, наприклад ізмененіу кута огляду, обертання карти і інше, проте продуктивність залишає бажати кращого. Крім того було виявлено дуже сильне падіння прозводітельності при отрісовке ліній. Це є серйозною проблемою, оскільки абсолютно необхідно виконувати отрісовку таких картографічних об'єктів як дорогі, річки, межі регіонів і інше. Пошуки вирішення даної поблеми не увінчалися наскільки яким-небудь успіхом, єдиним знайденим способом хоч якось прискорити отрісовку з'явилася рекомендація використовувати тільки цілі числа для завдання товщини пера. Ще одним незрозумілим фактом є те, що при відключенні антіалісинга поїзводітельность значно знижується. Renderoptions. Setedgemode(this, Edgemode. Aliased); Я сподіваюся, що знайдуться читачі, які зможуть пояснити таку поведінку системи, а також способи підвищення прозводітельності. При створенні додатків WPF корисно викачати інструменти для профілізації продуктивності, наприклад WPF Perforator [7], а також ознайомитися з рекомендаціями Micrsoft, які можна знайти в MSDN. Перевірка попадання курсорапроверка попадання курсора (Hit testing) в описуваному випадк

    безпека | прибуток | бізнес | інтернет | блоги рунета