STM32+Visual Studio

Не так давно я вновь занялся поиском IDE для STM. Keil, IAR — это, конечно, прекрасно, но да простят меня любители данных сред, они ужасны. CooCox — единственная вещь, которая напоминает о том, что мы живем в 21 веке. Но CooCoх даже рядом не стоял с моей любимой средой разработки — Visual Studio. И мне удалось найти проект, который позволит мне работать с STM в VS.

Зовут сие чудо VisualGDB. Плагин платный, 64 еврика, имеет триалку в 30 дней. В принципе, по сравнению с другими проектами, достаточно дешево. Плюс к тому — скидка для студентов в размере 50%.

Процесс установки предельно прост. Далее-далее, скачивание нескольких тулзов для работы ST-Link и импорт списка контроллеров (интотал около 40 Мб) — и вуаля. Быстрый старт (создание и запуск проекта) описан тут.

На текущий момент я не писал на нем ничего сверхсложного, джентельменский «Hello, World» из мира МК — поморгал светодиодиком. Но первые впечатления таковы:

Плюсы:

1. Простота установки. 10 минут, прозрачно и интуитивно понятно. Проверялось на Win7 64x + VS2010 Ultimate + VS2012 Ultimate
2. Простота запуска. F5 деплоит прошивку, одновременно запуская отладчик. На ST-Link бряки работают
3. Наличие библиотек. По аналогии с CooCox подтягиваются обертки для переферии для указанного контроллера.

Минусы:
1. IntelliSense. Ну да, оно вроде как работает, но при этом скорее Sense, чем Intelli. Автозавершение не работает. Выборки по типу нет. Общее впечатление — достаточно кутцо, особенно если кодил час назад под десктоп на шарпе.
2. Нет конфигуратора библиотек. Обертки для железа и прочего подтягиваются в проект автоматом при его создании, в отличие от CooCox, в котором выбор того, что тебе действительно надо, реализован посредством диалога.
3. Нет примеров. Опять же, CooCox — почти все библиотеки снабжены семплами по использованию, тут этого не хватает.

Как итог — хотелось бы услышать мнение сообщества по данной разработке (особенно ввиду того, что есть триалка и можно спокойно пощупать). Для себя же — она чуть удобнее по сравнению с кокосом, и единственный вопрос — стоит ли это удобство 64 евро?..

ссылка на оригинал статьи http://habrahabr.ru/post/204264/

Реальный опыт разработки на Meteor

Это рассказ о моем опыте разработки живого проекта на фреймворк Meteor. Фреймворк очень интересный, концептуально отличается подход от большинства существующих PHP/JS фреймворков. С Meteor приходится заново учиться веб-разработке.

Для начала пару слов о проекте. Это промо страница для одного небольшого местного сайта знакомств. Была задача создать отдельную страницу с конкурсом на лучшее фото среди участниц. Всего 8 участниц. Голосовать может кто угодно, никакой регистрации или авторизации требовать не нужно. На странице будет обратный отсчет до конца конкурса.

image

Meteor оказался хорошим выбором для этого проекта. Или же проект оказался хорошим в качестве моей первой работы на Meteor, равнозначно. Главная особенность Meteor – это т.н. реактивность (Reactivity). Идея в том, что программист декларативно описывает логику, не задумываясь о протоколе коммуникации между клиентом и сервером. Обновление данных на клиенте происходит автоматически, как только данные изменились на сервере. Это значит никаких больше AJAX запросов в коде проекта.

В качестве базы данных используется MongoDB. Клиентская часть имеет доступ к данным базы так же, как и серверная. Даже интерфейс доступа такой же, для имитации запросов к базе на клиентской стороне используется Minimongo. Клиент через Minimongo оперирует JavaScript массивами, в отличии от сервера который делает прямые запросы к MongoDB базе.

file: model.js

// Общий для клиента и сервера код Members = new Meteor.Collection('members'); 

В примере выше объявляется коллекция «участники». Так как этот файл доступен и клиентской и серверной части проекта, доступ к переменной Members есть и на клиенте и на сервере. Это можно проверить просто открыв консоль в браузере и выполнив typeof Members или Members.find().fetch(). Отличие только в реализации, ведь на сервере методы Members будут оперировать с MongoDB напрямую, а на клиенте с JavaScript массивами через Minimongo обертку.

Эти коллекции управляются самим Meteor – он сам решает когда данные необходимо обновить на клиенте. Программист может ограничить объем данных, который будет представлен переменной Members на клиенте. Это будет подмножество данных с сервера. Делается это при помощи Meteor.publish() и Meteor.subscribe(). В данном случае все участники со всеми их данными должны быть доступны клиенту, поэтому никаких искусственных ограничений не накладывается.

file: server/server.js

Meteor.startup(function () { 	if (Members.find().count() === 0) { 		Members.insert({ name: 'Александра Богинич', title: 'Александру', url: 'http://mariels.ru/member/profile_alexandra_igorevna.html', photo: 'images/member/Александра Богинич.jpg', thumb: 'http://mariels.ru/$userfiles/thumb_450_1136_94.jpg', vote: 0 }); 		Members.insert({ name: 'Алена Мансурова', title: 'Алену', url: 'http://mariels.ru/member/profile_Alionushka.html', photo: 'images/member/Алена Мансурова.jpg', thumb: 'http://mariels.ru/$userfiles/thumb_444_1120_90.jpg', vote: 0 }); 		// и так далее... 	} });	 

В коде выше стандартный способ инициализации коллекции в Meteor. Так как код находится в файле server/server.js, то выполняется он только на сервере.

Данные есть, теперь их нужно вывести в браузере. В Meteor по умолчанию используется JavaScript шаблонизатор Handlebars. На самом деле, довольно кривой шаблонизатор и для выполнения простой задачи вроде «получить доступ к индексу массива в цикле foreach» приходится писать новый обработчик тега. Но, привыкнув, работать с ним можно.

file: client/view/members.html

<template name="members">  <div id="members"> {{#render_members members}} <span class="member span6"> 	<span class="info-cont"> 		<span class="shadow"></span> 		<a href="{{member.url}}" class="account"> 			<img src="{{member.thumb}}" width="" height="" class="avatar"/> 			<span>{{member.name}}</span> 		</a> 	</span> 	<img src="{{member.photo}}" class="image" /> 	<span class="rate-cont"> 		<span class="shadow"></span> 		<button class="btn {{#if voted}}btn-info{{else}}btn-warning{{/if}} pull-center btn-large" data-id="{{member._id}}" {{#if voted}}disabled{{/if}}> 		{{#if voted}} 			Проголосуйте снова завтра 		{{else}} 			Голосовать за <span>{{member.title}}</span> 		{{/if}} 		</button> 	</span> </span> {{/render_members}} </div>  </template> 

Тег render_members был создан только для того, чтобы делить вывод на строки (выводить через каждые две записи), а вообще это обычный foreach цикл. Переменная доступная шаблону только одна — массив members. В теле render_members доступны все поля каждого объекта из массива members. Если быть уж совсем точным, то members не массив, а курсор, но это не суть.

file: client/client.js

Template.members.members = function() { 	return Members.find({}, { sort: { vote: -1 }}); } 

Members.find() возвращает курсор, тогда как Members.find().fetch() простой JavaScript массив. Используя курсор в качестве переменной шаблона members и обернув его в function() { } мы активируем реактивность Meteor на этой переменной шаблона. Это значит, что как только данные коллекции Members на сервере изменятся и обновления будут переданы на клиент, шаблон будет автоматически перерисован используя новые данные. И для этого не нужно никакого дополнительного кода на клиенте!

file: server/server.js

// Код только для сервера Votes = new Meteor.Collection('votes'); 

В коллекции Votes будут храниться все голоса и она может разрастись до нескольких тысяч записей. Мы не можем позволить такому огромному объему данных курсировать между сервером и клиентом по понятным причинам. К тому же, на клиенте нам совершенно ни к чему знать данные каждого голоса, такие как IP и дата. По этим причинам коллекция объявляется только в коде, выполняемом на сервере.

file: server/server.js

// Проверка валидности IP и даты последнего голосования var CanVote =  Match.Where(function(ip) { 	check(ip, String); 	if (ip.length > 0) { 		var yesterday = new Date(); 		yesterday.setDate(yesterday.getDate() - 1); 		// голосовать можно только раз в сутки 		return Votes.find({ ip: ip, date: { $gt: yesterday } }).count() == 0; 	} 	return false; }); // Методы сервера, доступные клиенту Meteor.methods({ 	// возвращает true если клиент может голосовать и false в обратном случае 	canVote: function() { 		return Match.test(headers.get('x-forwarded-for'), CanVote); 	}, 	// проголосовать за участницу 	vote: function(memberId) { 		check(memberId, String); 		check(headers.get('x-forwarded-for'), CanVote); 		var voteId = Votes.insert({ memberId: memberId, ip: headers.get('x-forwarded-for'), date: new Date() }); 		Members.update(memberId, { $set: { vote: Votes.find({ memberId: memberId }).count() } }); 		return voteId; 	}, 	// возвращает количество голосов за участницу 	getMemberVotes: function(memberId) { 		check(memberId, String); 		return Votes.find({memberId:memberId}).count(); 	}, 	// возвращает общее суммарное количество голосов 	getTotalVotes: function() { 		return Votes.find().count(); 	} }); 

При помощи Meteor.methods() объявляется интерфейс связи между клиентом и сервером в рамках проекта. Так как коллекция Votes не доступна на клиенте, здесь объявлены методы для получения нужных данных об этой коллекции, как то количество голосов за участницу и общее количество голосов.
В функции голосования добавляется новая запись в коллекцию Votes, а также обновляется количество голосов (votes) у соответствующей записи из коллекции Members. Это нужно чтобы использовать реактивность в выводе списка участников (сортируется по votes) и графика рейтингов.

// file: client/views/ratings.html

<template name="ratings"> 	 <div id="ratings" class="well">   <h1 class="heading uppercase">Рейтинги</h1>   <div class="chart"> 	  {{#each_with_index members}} 	  <div class="rating num{{index}}"> 	  	<img src="{{data.thumb}}" class="avatar"/> 	  </div> 	  {{/each_with_index}}   </div>   <div class="pull-center pull-center-1"> 	  <div id="votes">{{votes}}</div>     	  <div><strong>голосов</strong></div>     </p>   </div> </div>  </template> 

// file: client/client.js

Session.setDefault('totalVotes', 0); Meteor.startup(function() { 	// обновление значения totalVotes сессии 	Deps.autorun(function() { 		var total = 0; 		Members.find().forEach(function(m) { total += m.vote; }); 		Session.set('totalVotes', total); 	}); 	// обновление графика рейтингов топ-5 	Deps.autorun(function() { 		var top = Members.findOne({}, { sort: { vote: -1 }}); // текущий лидер голосования 		// update ratings chart 		Members.find({}, { sort: { vote: -1 }, limit: 5 }).forEach(function(m, i) { 			var height = top ? Math.floor((m.vote / top.vote) * 190) + 100 : 100; 			$('.rating.num'+(i+1)).css('height', height); 		}); 	}); }); Template.ratings.members = function() { 	return Members.find({}, {limit: 5, sort: { vote: -1 }}); }; Template.ratings.votes = function() { 	return Session.get('totalVotes'); }; 

Session существует только на клиенте и она не персистентна, то есть сбрасывается при обновлении страницы. Объект Session так же как и курсор коллекций активирует реактивность, поэтому при изменении значения totalVotes в сессии будет перерисован шаблон ratings.

Deps.autorun() выполняется каждый раз, как реактивные данные в фукнции меняются. В данном случае это курсор Members.find(). Идея в том, что как только сервер обновит votes у какой-нибудь участницы, обновится и значение totalVotes сессии у всех клиентов, а это приведет к перериcовке блока рейтингов. Deps.autorun() используется для добавления коллбэка на изменения данных на клиенте. Есть способы подписаться на конкретные события коллекций типа added, changed, removed подробнее здесь.

Также здесь можно заметить использование jQuery. Его можно перемешивать с клиентским кодом Meteor почти без ограничений. Кстати, Meteor.startup(function {}) и jQuery(function() { }) идентичны.

file: client/views/index.html

<head>     <!-- Много meta тегов в т.ч. для SEO и социалок -->     <link href="stylesheets/project.css" media="screen" rel="stylesheet" type="text/css" />     <!-- ... другие стили CSS -->     <script type="text/javascript" src="js/flipclock/flipclock.min.js"></script>     <!-- другие скрипты JavaScript -->     <title>Мисс Осень 2013</title> </head>    <body>   <div class="page-header-bg"></div>   {{>header}}   <div class="container-fluid"> 	  <div class="container"> 		<div class="page-header"> 			<h1 class="header-gradient">Мисс Осень 2013</h1> 		</div> 	  	{{>page}} 	 </div>  </div> </body>  <template name="page">   {{#if contestInProgress}} 	{{>partners}} 	{{>countdown}} 	{{>members}} 	{{>social}} 	{{>ratings}} 	{{>terms}} 	{{>partners}} 	{{>footer}} {{else}} 	{{>winner}} 	{{>partners}} 	{{>social}} 	{{>footer}} {{/if}} </template> 

Meteor обрабатывает все JavaScript, HTML, CSS файлы найденные в проекте и объединяет по определенным правилам. Однако, файлы в папке public считаются статичными, доступны как есть и не обрабатываются Meteor. Стили можно было бы перенести под управление Meteor, но было решено использовать стандартный подход – включить ссылки на статичные файлы в заголовок HTML.

Некоторые сторонние библиотеки JavaScript тоже включаются как статичные файлы, хотя их можно было перенести в папку client и так же использовать из своего клиентского JavaScript кода. Дело в том, что не все библиотеки написаны так, что могут быть использованы в Meteor, в таких случаях всегда можно вернуться к стандартному включению в заголовок HTML. При различии способа включения сторонней библиотеки, использование в клиентском коде Meteor одинаково естественно.

file: client/client.js

contestEndDate = new Date('01/30/2014 12:00');  Session.set('inProgress', new Date() < contestEndDate);  Template.header.contestInProgress = Template.page.contestInProgress =  Template.footer.contestInProgress = function() { 	return Session.get('inProgress'); }  Meteor.startup(function() { 	// обратный отсчет  	var targetDate = contestEndDate; 	var currentDate = new Date(); 	var offsetSeconds = (targetDate.getTime() - currentDate.getTime()) / 1000; 	offsetSeconds = Math.max(0, offsetSeconds); 	var clock = $('#countdown').FlipClock(offsetSeconds, { 		clockFace: 'DailyCounter', 		defaultClockFace: 'DailyCounter', 		countdown: true, 		callbacks: { 			stop: function() { 				Session.set('inProgress', false); 			} 		} 	}); }); 

В index.html можно увидеть еще одно применение реактивности. Переменная contestInProgress обозначает статус конкурса – в процессе или уже окончен. Вид страницы полностью меняется в зависимости от этого статуса. Статус устанавливается при инициализации страницы, а также меняется клиентом при возникновеннии события stop счетчика FlipClock.

Переменная contestInProgress есть в трех шаблонах и значение у нее одно и то же. Шаблоны независимы друг от друга и перерисовываются по отдельности.

Из кода видно, что из события инициируемого сторонней библиотекой FlipClock меняется значение сессии клиента Meteor. И это при том, что библиотека FlipClock загружается браузером клиента при загрузке страницы.
Вот тут открывается неочевидное преимущество Meteor. Раз так просто перерисовать страницу по завершении отсчета, так почему бы этого не сделать? Это всего лишь одна строчка кода, зато будет эффектно смотреться если кто-то в этот момент будет просматривать страницу.
Если бы проект разрабатывался на PHP+AJAX, эта была бы отдельная задача. Несложная, но учитывая что это событие случится только один раз за все существование проекта, возможно у программиста просто не дойдут руки сделать обновление статуса страницы. Да и зачем тратить на это время, если это увидят пару человек? Остальные, просто получат уже страницу с победителем. В этом и есть прелесть Meteor – программисту не нужно думать над протоколом общения и он может сконцентрироваться на тех мелочах, которые раньше бы откладывались в долгий ящик.

Традиционно, ощущения от использования Meteor в виде pros/cons:

ссылка на оригинал статьи http://habrahabr.ru/post/204262/

Magento. Процесс загрузки конфигурационных файлов

Добрый день, хабрасообщество.
Это моя первая статья, поэтому не судите строго. Несколько лет я занимаюсь разработкой интернет магазинов на базе Magento. За это время у меня накопилось много информации относительно работы данной платформы, которую невозможно вечно держать в голове, поэтому я решил структурировать ее в виде серии статей.
Кому интересно, добро пожаловать под кат.

Любое обращение к страницам в интернет магазине на базе Magento начинается с обработки index.php. В данном файле производится первичная проверка, в том числе версии PHP, объявляются константы и подключается файл Mage.php с базовым классом Mage. Данный класс реализует множество интересных методов, которые будут рассмотрены в дальнейших статья, а на данный момент нас интересует метод
run, отвечающий за запуск магазина. Чтобы понять, как происходит загрузка конфигурационных файлов, рассмотрим данный метод:

self::$_app = new Mage_Core_Model_App(); if (isset($options['request'])) {      self::$_app->setRequest($options['request']); } if (isset($options['response'])) {      self::$_app->setResponse($options['response']); } self::$_events = new Varien_Event_Collection(); self::_setIsInstalled($options); self::_setConfigModel($options); self::$_app->run(array(      'scope_code' => $code,      'scope_type' => $type,      'options'    => $options, )); 

Видно, что в методе создается новый класс Mage_Core_Model_App и для него вызывается метод run, где и происходит вызов методов первоначальной загрузки конфигурационных файлов.

Рассмотрим данный метод:

public function run($params)     {         $options = isset($params['options']) ? $params['options'] : array();         $this->baseInit($options);         Mage::register('application_params', $params);           if ($this->_cache->processRequest()) {             $this->getResponse()->sendResponse();         } else {             $this->_initModules();             $this->loadAreaPart(Mage_Core_Model_App_Area::AREA_GLOBAL, Mage_Core_Model_App_Area::PART_EVENTS);               if ($this->_config->isLocalConfigLoaded()) {                 $scopeCode = isset($params['scope_code']) ? $params['scope_code'] : '';                 $scopeType = isset($params['scope_type']) ? $params['scope_type'] : 'store';                 $this->_initCurrentStore($scopeCode, $scopeType);                 $this->_initRequest();                 Mage_Core_Model_Resource_Setup::applyAllDataUpdates();             }               $this->getFrontController()->dispatch();         }         return $this;     } 

Метод baseInit отвечает за инициализацию свойства _config и загрузку базовых конфигураций. Базовая конфигурация загружается в методе _initBaseConfig, который в дальнейшем вызывает loadBase метод из класса Mage_Core_Model_Config.
Метод loadBase парсит и сохраняет в свойство _xml объекта Mage_Core_Model_Config данные из файлов app/etc/local.xml и app/etc/config.xml. В данных файлах содержатся доступы к БД и базовые настройки интернет магазина.
После чего происходит загрузка конфигурационных файлов модулей. За это отвечает метод _initModules, который, в свою очередь, вызывает метод loadModules из класса Mage_Core_Model_Config.
Для загрузки конфигурационных файлов модулей сначала загружается список всех файлов из папки app/etc/modules и производится разбор этих файлов. За это отвечает метод _loadDeclaredModules. В методе loadModules производится обработка только трех типов файлов из папки etc в директории модуля:

  • 1. confix.xml
  • 2. config.{resource_name}.xml (в нашем случае config.mysql4.xml)
  • 3. local.xml

Для загрузки первых двух типов вызывается метод loadModulesConfiguration, которому в качестве $fileName передается массив с именами файлов (config.xml и config.mysql4.xml)
image
Далее производится парсинг и загрузка данных в свойство _xml для всех активных модулей:

$modules = $this->getNode('modules')->children(); foreach ($modules as $modName=>$module) {     if ($module->is('active')) {         if ($disableLocalModules && ('local' === (string)$module->codePool)) {             continue;         }         if (!is_array($fileName)) {             $fileName = array($fileName);         }         foreach ($fileName as $configFile) {             $configFile = $this->getModuleDir('etc', $modName).DS.$configFile;             if ($mergeModel->loadFile($configFile)) {                 $mergeToObject->extend($mergeModel, true);             }         }     } } 

С помощью файла local.xml можно вносить свои изменения в базовую конфигурацию интернет магазина.
На этом загрузка конфигурации завершается, и запускаются соответствующие процессы, в зависимости от роутера страницы на которую вы перешли.

Спасибо за внимание, с удовольствием жду вашей критики.

ссылка на оригинал статьи http://habrahabr.ru/post/204166/

10 месяцев бесплатных облаков на DigitalOcean

imageОдин из лучших обачных сервисов DigitalOcean дарит новым пользователям $50 на Чёрную пятницу.

Данной суммы должно хватить на 10 месяцев использования самого дешёвого тарифа (512Мб памяти, 1 ядро, 20Гб SSD, 1Тб трафика).

Сервис можно отнести к модели обслуживания «Инфраструктура как услуга». Как это использовать? Можно почитать в статьях. Сервис неоднократно упоминался на Хабрахабре, к примеру:

Для получения кредита нужно:

  1. Создать аккаунт
  2. Зайти в Billing
  3. Внизу страницы ввести Code: BLACK50
  4. Внести через Paypal $5 (может быть опционально)
  5. Создать Droplet с тарифом $5 в месяц
  6. 10 месяцев VPS за $5 (11 если внесли платёж через Paypal)
  7. ???????
  8. PROFIT!

Найдено на Hacker News.

Изображение лицензировано Creative Commons Attribution-Share Alike 2.0 Generic license.

ссылка на оригинал статьи http://habrahabr.ru/post/204252/

Простая публикация геоданных на собственной карте на базе 2ГИС

Недавно передо мной встала интересная задача — отображать на сайте карту, с различными объектами, причем этих объектов могут быть десятки и сотни, а управлять ими должен уметь любой менеджер ниже среднего звена.

Более формально задача сводилась к тому, чтобы дать возможность пользователям задавать некий список гео-объектов, дать им возможность легко и быстро этими объектами управлять и автоматически отображать эти объекты на некоей карте.

Для реализации задумки была выбрана связка Google Docs и API картографического сервиса 2ГИС. Решение получилось действительно простым, в духе знаменитых «30 строк» 🙂

Шаг 1. Заводим таблицу для данных.

Открываем Google Docs и создаем таблицу. В моей таблице было достаточно много ненужных мне, но очень нужных для прочих служб полей, поэтому я пойду на небольшую хитрость и для тестирования заведу отдельную.

Вот ссылка на нее: docs.google.com/spreadsheet/ccc?key=0AvdXWIJtCGnndG0tODY5MzRsVFhnbkJseFk0aWJkaUE&usp=sharing

В качестве тестовых данных я взял адреса домов, относящихся к двум небольшим избирательным участкам в городе Костроме. Номер участка, улица, номер дома и число квартир.

Использование в качестве источника данных Google Drive снимает множество проблем с совместной работой специалистов разной степени подготовки и с оперативным обновлением данных.

Теперь таблицу нужно опубликовать. Для этого идем в меню «Файл — Опубликовать в интернете». В появившемся окне выбираем «Начать публикацию», в нижней части — выбираем «CSV (значения, разделенные запятыми)» и получаем ссылку на наш лист таблицы в виде CSV.

Получается вот такая ссылка: docs.google.com/spreadsheet/pub?key=0AvdXWIJtCGnndG0tODY5MzRsVFhnbkJseFk0aWJkaUE&single=true&gid=0&output=csv

Шаг 2. Готовим данные для карты.

Напишем несложный скрипт на PHP и назовем его getMapData.php:

define('CSV_DATA_URL', 'https://docs.google.com/spreadsheet/pub?key=0AvdXWIJtCGnndG0tODY5MzRsVFhnbkJseFk0aWJkaUE&single=true&gid=0&output=csv'); define('DEFAULT_CITY', 'Кострома');  $points = array(); $csvDataHandle = fopen(CSV_DATA_URL, 'r'); $i = 0; while ( false !== ($line = fgetcsv($csvDataHandle, null, ',', '"')) ) {     // Пропускаем первую строку в файле     if ( !$i++ ) continue;     // Считываем номер участка     if ( !empty($line[0]) )         $zone = $line[0];     $points[] = array(         'zone' => $zone,         'address' => DEFAULT_CITY . ', ' . $line[1] . ', ' . $line[2],         'data' => $line[3],     ); } 

Тут нужно заметить, что скорее всего при первом запуске вы получите ошибку «Warning: fopen(): Unable to find the wrapper „https“». Это связано с тем, что в дефолтной установке PHP не включен модуль OpenSSL. Найдите в файле php.ini строчку
;extension=php_openssl.so и раскомментируйте ее, убрав точку с запятой. Обычного этого достаточно.

Разумеется, массив $points нужно куда-то закешировать, чтобы не «дергать» Google Docs при каждой отрисовке карты.

Теперь перед нами стоит следующая задача. Нужно адреса геообъектов каким-то образом перевести в координаты на карте. Для этого у 2ГИС есть соответствующее API: api.2gis.ru/doc/geo/search/

Вот простейший код для работы с API геокодера:

define('DGIS_API_KEY', 'XXXXXX'); // Ваш ключ define('DGIS_API_URL', 'http://catalog.api.2gis.ru/geo/search?version=1.3&key=' . DGIS_API_KEY . '&q=%s');  function getGeoObjectInfo($address) {     $url = sprintf(DGIS_API_URL, $address);     $response = json_decode(file_get_contents($url));     if ( $response->response_code == 200 ) {         // Координаты центра геообъекта         $coords = $response->result[0]->centroid;         // Немного магии, чтобы привести их в нужный вид         $coords = explode(',', preg_replace('/^POINT\(([\S]+)([\s]+)([\S]+)\)$/', '$1,$3', $coords));     } else         $coords = array();     return $coords; } 

Ну и наконец пробежимся по массиву $points нашим «геокодером» и выведем результат в формате JSON:

foreach ( $points as &$address ) {     $address['coords'] = getGeoObjectInfo($address['address']);     sleep(1); }  echo json_encode($points); 

Обратите внимание на странную строчку sleep(1); в коде. Дело в том, что я пользуюсь тестовым доступом к API 2GIS, а при нем частота запросов ограничена одним в секунду. После получения полного доступа эту строчку нужно, разумеется, убрать.

Ну и не забываем опять же кэшировать полученные данные, чтобы не создавать 2ГИС лишнюю нагрузку, а себе — лишнее время работы скрипта.

Шаг 3. Показываем карту.

Тут я приведу полный код страницы. Ничего сложного в нем нет, тем более что у 2ГИС есть неплохая документация, ее можно прочитать по адресу api.2gis.ru/doc/maps/info/

<!DOCTYPE html> <html> <head>     <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>     <script type="text/javascript" src="http://maps.api.2gis.ru/1.0?loadByRequire=1"></script>     <title>Тестовая карта с объектами</title> </head> <body>     <div id="DGMap" style="width:100%; height:600px"></div>      <script type="text/javascript">          $(DG.load(function() {              // Создаем карту             var map = new DG.Map('DGMap');             map.setCenter(new DG.GeoPoint(40.95,57.76),12);             map.controls.add(new DG.Controls.Zoom());              // Получаем данные             $.get('getMapData.php', function (data) {                 var objects = JSON.parse(data);                 for ( i in objects ) {                     var marker = new DG.Markers.MarkerWithBalloon({                         geoPoint: new DG.GeoPoint(objects[i].coords[0], objects[i].coords[1]),                         balloonOptions: {                             headerContentHtml: '<b>Участок №'+objects[i].zone+'</b>',                             contentHtml: 'Адрес:'+objects[i].address+'<br />Число квартир:'+objects[i].data                         }                     });                     map.markers.add(marker);                 }                 // Получаем границы добавленных на карту маркеров:                 var markersBounds = map.markers.getBounds();                 // Устанавливаем карте новые границы по маркерам:                 map.setBounds(markersBounds);             });          }));      </script>  </body> </html> 

На этом, собственно, всё. Мы получили простейшую, но вполне функциональную систему, которая показывает на карте объекты согласно списку, ведущемуся в Google Docs. Вот такой mash-up 🙂

P.S.

Ссылку на получившуюся карту не даю, потому что опасаюсь за сервер, который легко может лечь под хабраэффектом. Но картинку не могу не вставить:

P.P.S.

Собственно, ничто не мешает сделать такой же «трюк» с картами, например, от Яндекса. Получится даже красивее, за счет множества встроенных стилей и технологии кластеризации маркеров на карте. Изначально я пошел именно этим путем, но тесты показали, что геокодер 2ГИС все-таки гораздо точнее. Яндекс справляется с получением координат примерно от 95% адресов при условии их тщательной подготовки, 2ГИС — почти все 100%.

ссылка на оригинал статьи http://habrahabr.ru/post/204228/