понедельник, 28 февраля 2011 г.

Как подружить Yandex Maps и SQL Server посредством YMapsML

В прошлой статье мы рассматривали организацию отображения на карте Яндекс.Карты объектов из БД MS SQL Server при помощи http-обработчика, написанного на ASP.Net (C#). Однако, это решение не является единственным (кто бы мог подумать J ). В API Яндекс.Карты существует возможность загрузки объектов посредством специального файла в XML формате. Объекты в этом файле описываются специальным YMapsML языком. В общем виде такой файл имеет вид как на листинге 1.

Листинг 1 – Пример YMapsML – файла
<?xml version="1.0" encoding="utf-8"?>
<ymaps:ymaps xmlns:ymaps="http://maps.yandex.ru/ymaps/1.x"
             xmlns:gml="http://www.opengis.net/gml"
             xmlns:repr="http://maps.yandex.ru/representation/1.x"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maps.yandex.ru/schemas/ymaps/1.x/ymaps.xsd">
    <ymaps:GeoObjectCollection>
        <ymaps:style>#anyStyle</ymaps:style>
        <gml:featureMembers>
            <ymaps:GeoObject>
                <gml:description>Описание</gml:description>
                <gml:Point>
                    <gml:pos>37.63 55.75</gml:pos>
                </gml:Point>
            </ymaps:GeoObject>
            
            <ymaps:GeoObject>
                <gml:LineString>
                    <gml:pos>70 55</gml:pos>
                    <gml:pos>80 70</gml:pos>
                    <gml:pos>120 30</gml:pos>
                </gml:LineString>
            </ymaps:GeoObject>
        </gml:featureMembers>
    </ymaps:GeoObjectCollection>
</ymaps:ymaps>

Итак, получим из таблицы (листинг 2) YMapsML – файл, который загрузим на нашу страницу.

Листинг 2 – Таблица с объектами
CREATE TABLE [dbo].[t_Map] (
  [id] int IDENTITY(1, 1) NOT NULL,
  [ObjectName_Var] nvarchar(128) COLLATE Cyrillic_General_CI_AS NULL,
  [Address_Var] nvarchar(256) COLLATE Cyrillic_General_CI_AS NULL,
  [Longitude_Var] nvarchar(128) COLLATE Cyrillic_General_CI_AS NULL,
  [Latitude_Var] nvarchar(1) COLLATE Cyrillic_General_CI_AS NULL,
  PRIMARY KEY CLUSTERED ([id])
)

Как видно из листинга 1, файл YMapsML представляет собой довольно сложную иерархическую структуру. Поэтому для преобразования наших реляционных данных (листинг 2) воспользуемся режимом форматирования FOR XML EXPLICIT.
Запрос приведен в листинге 3, я не буду подробно его разбирать, там все довольно понятно, хотя и весьма громоздко.

Листинг 3 – Получение XML из реляционных данных
CREATE PROCEDURE dbo.p_Get_XML
AS
SET NOCOUNT ON
BEGIN
  SELECT
      1 Tag
      , NULL Parent
      , NULL [ymaps:ymaps!1]
      , 'http://maps.yandex.ru/ymaps/1.x' [ymaps:ymaps!1!xmlns:ymaps]
      , 'http://maps.yandex.ru/representation/1.x' [ymaps:ymaps!1!xmlns:repr]
      , 'http://www.opengis.net/gml' [ymaps:ymaps!1!xmlns:gml]
      , 'http://www.w3.org/2001/XMLSchema-instance' [ymaps:ymaps!1!xmlns:xsi]
      , 'http://maps.yandex.ru/schemas/ymaps/1.x/ymaps.xsd' [ymaps:ymaps!1!xsi:schemaLocation]
      , NULL [repr:Representation!2]
      , NULL [repr:View!3]
      , NULL [repr:mapType!4]
      , NULL [ymaps:GeoObjectCollection!5]
      , NULL [ymaps:style!6]
      , NULL [gml:featureMembers!7]
      , NULL [ymaps:GeoObject!8]
      , NULL [gml:name!9]
      , NULL [gml:description!10]
      , NULL [gml:Point!11]
      , NULL [gml:pos!12]
  UNION ALL
  SELECT 2 Tag, 1 Parent, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'MAP',
                   NULL, '#customStyle', NULL, NULL, NULL, NULL, NULL, NULL
  UNION ALL
  SELECT 3 Tag, 2 Parent, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'MAP',
                   NULL, '#customStyle', NULL, NULL, NULL, NULL, NULL, NULL
  UNION ALL
  SELECT 4 Tag, 3 Parent, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'MAP',
                   NULL, '#customStyle', NULL, NULL, NULL, NULL, NULL, NULL
  UNION ALL
  SELECT 5 Tag, 1 Parent, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'MAP',
                   NULL, '#customStyle', NULL, NULL, NULL, NULL, NULL, NULL
  UNION ALL
  SELECT 6 Tag, 5 Parent, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'MAP',
                   NULL, '#customStyle', NULL, NULL, NULL, NULL, NULL, NULL
  UNION ALL
  SELECT 7 Tag, 5 Parent, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'MAP',
                   NULL, '#customStyle', NULL, NULL, NULL, NULL, NULL, NULL
  UNION ALL
  SELECT 8 Tag, 7 Parent, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'MAP',
                   NULL, '#customStyle', NULL, NULL, m.ObjectName_Var, m.Address_Var, NULL, NULL FROM t_Map m
  UNION ALL
  SELECT 9 Tag, 8 Parent, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'MAP',
                   NULL, '#customStyle', NULL, NULL, m.ObjectName_Var, m.Address_Var, NULL, NULL FROM t_Map m
  UNION ALL
  SELECT 10 Tag, 8 Parent, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'MAP',
                   NULL, '#customStyle', NULL, NULL, m.ObjectName_Var, m.Address_Var, NULL, NULL FROM t_Map m
  UNION ALL
  SELECT 11 Tag, 8 Parent, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'MAP',
                    NULL, '#customStyle', NULL, NULL, m.ObjectName_Var, m.Address_Var, NULL, NULL FROM t_Map m
  UNION ALL
  SELECT 12 Tag, 11 Parent, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'MAP',
                   NULL, '#customStyle', NULL, NULL, m.ObjectName_Var, m.Address_Var, NULL, m.Longitude_Var + ' ' + m.Latitude_Var
  FROM dbo.t_Map m
  ORDER BY [ymaps:style!6], [gml:description!10], Tag
  FOR XML explicit
END

Как было сказано, запрос громоздкий, но один раз стоит потратить время. Я не стал подробно расписывать раздел repr:Representation, для нас сейчас не это главное.
Теперь надо сделать, чтоб результат данного запроса сохранялся в файл *.xml. Скрипт, который делает это, приведен в листинге 4.


Листинг 4 – Сохранение XML в файл
DECLARE @result int
DECLARE @OutputFileName varchar(150)
DECLARE @cmd varchar( 150)

SET @OutputFileName = 'c:\YMapsML.xml'

SET @cmd = 'BCP "EXEC Site..p_Get_XML" queryout "' + @OutputFileName + '" -w -C1251 -r -T'

EXEC @result = master..xp_cmdshell @cmd

Не забыть проверить, чтоб у файла, куда произойдет выгрузка был открыт доступ и назначены права на редактирование.
Далее файл нужно выложить в интернете так, чтоб он был доступен парсеру Яндекс.Карты. В этом есть ограничение использования данного метода. Например, мой сайт находится в локальной сети, и парсер сюда зайти не может.
Ну и напоследок страничка с подключенным YMapsML (листинг 5).

Листинг 5Подключение YMapsML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Примеры. Визуализация YMapsML.</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <script src="http://api-maps.yandex.ru/1.1/index.xml?key=ANpUFEkBAAAAf7jmJwMAHGZHrcKNDsbEqEVjEUtCmufxQMwAAAAAAAAAAAAvVrubVT4btztbduoIgTLAeFILaQ==" type="text/javascript"></script>
    <script type="text/javascript">
        // Создание обработчика для события window.onLoad
        YMaps.jQuery(function () {
            // Создание экземпляра карты и его привязка к созданному контейнеру
            var map = new YMaps.Map(YMaps.jQuery("#YMapsID")[0]);

            // Установка для карты ее центра и масштаба
            map.setCenter(new YMaps.GeoPoint(55.983161, 54.73794), 12);

            // Создание YMapsML-документа и добавление его на карту
            var ml = new YMaps.YMapsML("http://download1317.mediafire.com/6v198lba6hdg/a4a6lovzelap5b9/1.xml");
            map.addOverlay(ml);

            // Обработчик неудачного создание документа YMapsML
            YMaps.Events.observe(ml, ml.Events.Fault, function (ml, error) {
                alert("Ошибка: " + error);
            });
        });
    </script>
</head>

<body>
    <div id="YMapsID" style="width:600px;height:400px"></div>
</body>

</html>

Удачи!

7 комментариев:

Анонимный комментирует...

Спасибо огромное! Внятно и по-человечески написано!

dedMazDie комментирует...

Я рад, что статья полезна.

Андрей Р. комментирует...

Да, большое спасибище!

Konstantin S комментирует...

Все же лучше наверно использовать ASHX хэндлеры а не файлы писать.

dedMazDie комментирует...

Это просто вариант. Возможно, в какой то ситуации проще будет им воспользоваться.

MirDD комментирует...

я вижу что статья написана давно, например я для создания .kml использую Limq to XML очень удобно создавать элементы и атрибуты.
Ну и к делу. Я задался вопросом как на один адрес привезать несколько элементов, если создавать несколько Placemark То будет много иконок и находится они будут друг под другом (это не гуд) Если создавать несколько description то отображаться будет только последний (это тоже не гуд). Может Вы подскажете как поступить мне в данной ситуации , а может это подталкнет Вас продолжить вашу серию рассказов про взаимодействия с яндекс картами .

dedMazDie комментирует...

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