Работа с Microsoft MapPoint WebService

Автор: Гаенко Дмитрий, d.gaenko@gmail.com
 

- Где карта, Билли?
Слепой Пью

В одном из проектов мне пришлось работать с Microsoft MapPoint WebService. Задача была следующая: по заданному почтовому индексу отобразить карту местности с ближайшими к указанной точке объектами (список объектов был задан). Вариантов по реализации было несколько, но выбирал средство мой работодатель из своих соображений. Его выбор остановился на Microsoft MapPoint WebService. Сразу должен заметить для тех, кто заинтересуется этим продуктом и захочет использовать - как и многие продукты от Microsoft требует денег, начиная от контракта, заканчивая платой за каждый показ карты, так что пусть это не будет для вас сюрпризом.

Итак, после разборки на части демок вопрос использования Microsoft MapPoint WebService более или менее прояснился - веб-сервис позволял выполнить определение географических координат точки по ее реквизитам:

  • адресу, почтовому индексу или комбинации из них;
  • искать ближайшие к указанной точке объекты по залитым в базу MapPoint данным в нужном радиусе;
  • получить карту в виде картинки по выполненному выше;
  • вычислять длину пути (по автодорогам) между 2-мя точками, время на поездку;
  • ну и кое-что другое не заслуживающее в данной статье внимания.
После освоения примеров от Microsoft и практического их применения начали появляться нюансы. А именно: проект требовал немецкого языка и на карте должны были выводиться названия найденных объектов, сама карта с названиями населенных пунктов на немецком языке. Если вопрос я локализацией карты решился довольно просто - необходимо было прописать нужную культуру у каждого запроса, например, так: UserInfoRenderHeader myUserInfoRenderHeader = new UserInfoRenderHeader(); myUserInfoRenderHeader.Culture.Name = "de"; А вот локализацию отображения данных найденных объектов по началу решить ни как не удавалось - для поиска этих объектов необходимо было предварительно залить данные в базу MapPoint: в ней можно хранить как определенные документацией данные, так и собственные, но вот собственные данные (названия объектов с умляутами) корректно на карте отображаться не хотели. Ни какие ухищрения с кодировками и прочими трюками так и не помогли и вопрос отложился на потом. И вторая неприятность - перерыв форумы по MapPoint оказалось также, что объем записей, который можно в нем хранить, ограничен - точную цифру не помню, да и не важно. Еще почему то со временем мой проект стал тормозить: если по началу все почти вылетало, то тут вдруг стало замирать на пару-тройку секунд - это ужасно раздражало и меня и клиента - сначала тормоза свалили на тестовый режим, но и когда позже переключили на нормальный изменений не почувствовалось (до сих пор сушим мозги :). Так вот, в попытках побороть тормоза я стал перебирать варианты ускорения работы и мне пришла в голову идея - а почему собственно не находить ближние точки самому вместо MapPoint? Рассчитать расстояние по координатам между центром и объектами вроде не особо сложно: чуть ли не по теореме Пифагора. С теоремой Пифагора правда пришлось распрощаться.. даже не попробовав. После поиска в Интернете на эту тему попалась функция для Delphi которая выполняла нужные вычисления, автор кода был не указан, так что и благодарить не кого. После перевода с Delphi на C# получилась следующая функция, возвращающая расстояние между 2-мя точками в метрах:

double calc2PointDistance(LatLong start, LatLong end) { double StartLat = start.Latitude, StartLong = start.Longitude, EndLat = end.Latitude, EndLong = end.Longitude; double fPhimean, fdLambda, fdPhi, fAlpha, fRho, fNu, fR, fz, fTemp, Distance, D2R = Math.PI/180, a = 6378137.0, e2 = 0.006739496742337; fdLambda = (StartLong-EndLong)*D2R; fdPhi = (StartLat-EndLat)*D2R; fPhimean = (StartLat+EndLat)/2.0*D2R; fTemp = 1-e2*Math.Pow(Math.Sin(fPhimean),2); fRho = a*(1-e2)/Math.Pow(fTemp,1.5); fNu = a/Math.Sqrt(1-e2*Math.Sin(fPhimean)*Math.Sin(fPhimean)); fz = 2*Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(fdPhi/2.0),2)+ Math.Cos(EndLat*D2R)*Math.Cos(StartLat*D2R)*Math.Pow(Math.Sin(fdLambda/2.0),2))); fAlpha = Math.Asin(Math.Cos(EndLat*D2R)*Math.Sin(fdLambda)/Math.Sin(fz)); fR = fRho*fNu/(fRho*Math.Pow(Math.Sin(fAlpha),2)+fNu*Math.Pow(Math.Cos(fAlpha),2)); Distance = fz*fR; return Distance; } // End calc2PointDistance(LatLong start, LatLong end) На основе этой функции была переработана функция получения ближайших к указанной точке объектов из примеров MapPoint - был выброшен запрос к веб-сервису и заменен на собственный локальный код. Реализация функции приведена ниже: public Pushpin[] findNearbyPoints(LatLong coords, double d_distance) { XmlDocument database = new XmlDocument(); database.Load(Server.MapPath("data/objects.xml")); XmlNode root = database.DocumentElement; XmlNodeList nodeList = root.SelectNodes("//Item"); ArrayList list = new ArrayList(); foreach (XmlElement item in nodeList) { LatLong point = new LatLong(); point.Latitude = Convert.ToDouble(item.SelectSingleNode("Latitude").InnerText.Replace(".",",")); point.Longitude = Convert.ToDouble(item.SelectSingleNode("Longitude").InnerText.Replace(".",",")); double distance = calc2PointDistance(coords, point); if (distance/1000&lt;=d_distance || Double.IsNaN(distance)) { string EntityID = item.SelectSingleNode("EntityID").InnerText; string PointName = item.SelectSingleNode("Name").InnerText; Pushpin myPushpin = new Pushpin(); myPushpin.PinID = EntityID; myPushpin.Label = PointName; myPushpin.IconDataSource = "MapPoin.Icon"; myPushpin.IconName = "29 "; myPushpin.LatLong = point; myPushpin.ReturnsHotArea = true; list.Add(myPushpin); } } Pushpin[] myPushpins = new Pushpin[list.Count+1]; for (int i=0; i<list.Count; i++) myPushpins[i] = (Pushpin)list[i]; return myPushpins; } Данная функция позволила решить проблему локализации данных, снять ограничения на количество записей, да и просто повысить скорость работы системы. База данных для данной функции храниться теперь в XML-файле. Пример структуры XML-файла: &lt;?xml version="1.0" encoding="UTF-8"?> <dataroot> <Item> <EntityID>1</entityid> <Name>Vasya Pupkin & Co</name> <City>Moskow</city> <AddressLine>Pupkin str, 4</addressline> <PostalCode>11100</postalcode> <Phone>+7(495)3222234</phone> <Latitude>55.8627488513155</latitude> <Longitude>14.1792344306166</longitude> </item> </dataroot> Для приведенной выше функции обязательны следующие условия:

  • все объекты хранятся в полях Item
  • обязательно поле EntityID (идентификатор записи)
  • обязательно поле Name (название)
  • обязательно поле Latitude (широта - можно получить через тот же MapPoint)
  • обязательно поле Longitude (долгота)
По результатам тестирования из 5 десятков опытов результаты стандартного варианта и моей функции не расходились ни разу. Вот собственно и все.


Опубликовал admin
3 Июл, Понедельник 2006г.



Программирование для чайников.