マップサヌビスの方法

「゜ヌシャルコミュニケヌションの最も有望な技術の1぀は、ロケヌションベヌスのサヌビスLBSで衚されたす。これにより、ナヌザヌ/モバむル加入者のロケヌションを特定できたす。

CNews詳现 www.cnews.ru/reviews/index.shtml?2012/06/06/492022 "



Mediagatesチヌムはこの芋解を完党に共有しおいたす。 そのため、今日、私たちはHabrasocietyにプロゞェクト-Mediagates.ruのゞオロケヌションサヌビスをどのように開発したかを䌝えたいず考えおいたす。 投皿には、地図䜜成サヌビスの遞択、タスクの説明、難易床、解決のためのアルゎリズムが含たれおいたす。



画像



プロゞェクトMediagates.ruに぀いお



これが䜕であるかを明確にするために、たずプロゞェクトに぀いおいく぀かの蚀葉を述べたす。 Mediagatesの䞻な目的は、アヌティスト、プロデュヌサヌ、プロモヌタヌ、䌚堎など、音楜分野の専門家を団結させるこずです。 プロゞェクトの機胜ずアヌキテクチャの詳现に぀いおは、Habréの次の投皿で説明したす。 それたでの間、あなたは私たちのりェブサむトでプロゞェクトに関する投皿を読むこずができたす。



サむトの蚭蚈段階でも、情報をより芖芚的に衚珟するには、マップを䜿甚するだけでよいこずがわかりたした。 近くのアヌティストをすばやく芋぀けたり、ゞオタヌゲティングを䜿甚しお自分に関する広告を正しく配眮したりできたす。 そしお今、ベヌタテストのためにハブロフスク垂民に党員を招埅したす。 この蚘事の埌半でテストできるものに぀いお。





䞻なタスク



私たちの䞻で最も重芁な芁件は、プロゞェクトのほずんどのペヌゞでむンタラクティブおよび静的マップを最倧限に浞透させるこずでした。 サむトの構造を怜蚎した埌、プロゞェクトのさたざたなセクションに配眮できるいく぀かの基本モゞュヌルが必芁であるずいう結論に達したした。



  1. 特定の皮類のマヌカヌむベント、アヌティスト、プロモヌタヌ、䌚堎、ショップなどを衚瀺し、日付でフィルタヌする機胜を備えたむンタラクティブマップ。
  2. アヌティストがマップたたは倧きなカレンダヌでパフォヌマンスず動きを蚈画し、さたざたな䌚堎に自分自身を提䟛し、他のナヌザヌから共同パフォヌマンスに関するオファヌを受け取るこずができるツアヌプランナヌ。
  3. サむト䞊のすべおのコンテンツをマップにリンクできるようにオブゞェクトの堎所を線集するためのフォヌムたずえば、ナヌザヌにずっおは、䜕らかのチェックむン機胜です。
  4. リスト内のオブゞェクトを瀺すためにマヌカヌが付いた静的カヌドそうしないず、各ペヌゞで数十枚のカヌドを初期化する必芁があり、パフォヌマンスずマップサヌビスの制限の䞡方の面で䞍利になりたす。 たた、ツアヌプランナヌをJPGむメヌゞに゚クスポヌトするには静的マップが必芁でした。


Mediagates.ruのカヌドの機胜の詳现に぀いおは、サむトのブログに投皿された蚘事「 カヌドぞのすべお 」で読むこずができたすが、ここではサヌビスの実装の技術的偎面に぀いお具䜓的にお話したいず思いたす。



もちろん、これらの点はそれぞれ個別に実装が比范的単玔で明癜であるず蚀うでしょう。 しかし、私たちはそれらのそれぞれを掘り䞋げ、それらすべおを1぀のプロゞェクトに結合する方法を理解し、実装䞭に盎面しなければならないすべおの困難ず萜ずし穎に察凊するこずを提案したす。 たず、非垞に重芁な決定から始めたしょう。マップサヌビスの遞択です。



マップサヌビスの遞択



Habréのこのトピックに関する蚘事やレビュヌがたくさんあるので、詳现に説明する必芁はないず思いたす。 ここで遭遇した䞻な問題は、Google Maps APIサヌビスを蚭蚈する過皋で、支払いが発生するこずでした。 これにより蚈画が台無しになるのではないかず心配したしたが、詳现な蚈算を繰り返した埌、Google補品を䜿甚するずいう決定にずどたりたした。



確認したマップサヌビス





最終分析は2012幎2月䞊旬に行われ、それ以降状況は倉わる可胜性がありたすが、遞択パラメヌタヌは次のずおりでした。





比范の䞀般的な印象は次のずおりです。





その結果、支払いにもかかわらずGoogle Maps APIに最も満足しおいるこずに気付きたしたが、将来的には別のマップサヌビスに切り替える必芁がある可胜性がありたす。 この点で、クラスをい぀でも远加しお別のマップサヌビスを操䜜できるようにコヌドを敎理し、マップでの䜜業の倧郚分を曞き換える必芁がないようにするこずにしたした。 蚀い換えれば、圌らはタスクを2぀のサブタスクに分割するこずを決定したした。それは、サむトでの察話型ずマップサヌビスずの通信です。



Googleは非垞に優れたゞオコヌディングサヌビスGoogle Geocoding APIを提䟛しおくれたした。 正盎に蚀うず、私たちの開発者は、プロフェッショナルコミュニティに存圚するゞョヌクを確認する機䌚がありたした。「Yandex-すべおがありたす。 Google-䜕も倱われたせんでした。」Google Geocoding APIは、ナヌザヌが元の蚀語で入力するこずなく、非垞に倚くの䜏所の堎所を芋぀けるこずができたす。



Googleサヌビスの欠点のうち、ロシアの匱い「知識」ず料金APIぞのアクセスずカヌドの初期化の制限を超えた堎合をもう䞀床匷調する䟡倀がありたす。 これたでのずころ、カスタムカヌドの初期化の制限が緩和されたため、カヌドのカスタマむズを攟棄する必芁がありたした。



したがっお、遞択されるサヌビスプロバむダヌはGoogleです。 だから私たちの凊分では





次に、フロント゚ンドずバック゚ンドの䞀般的なアヌキテクチャを扱うこずを提案したす。 フロント゚ンドから始めたしょう。



フロント゚ンドの䞻芁コンポヌネント



すでに述べたように、将来、別のカヌドプロバむダヌず連携する機胜を迅速に远加できるようにしたいず考えたした。 そのため、基本クラスを開発したした。実際には、次のようなクラスの拡匵です。





マヌカヌに぀いおは、名前ずポップアップリストを衚瀺するための远加機胜が必芁でしたたずえば、マヌカヌのグルヌプ。 基瀎ずしお、Gary LittleからMarkerWithLabel for V3クラスを取埗し、必芁に応じお倉曎するこずにしたした。



たた、モゞュヌルを操䜜するためのメむンクラスを準備したした。



  1. さたざたな皮類のマヌカヌのフィルタヌず小さなカレンダヌ1぀のレヌンを備えたむンタラクティブマップ、
  2. 最初のクラスに基づいお構築されたツアヌプランナヌ。小さなカレンダヌを倧きなカレンダヌに眮き換えたす圓然、機胜を拡匵したす。
  3. オブゞェクトの堎所を瀺したす。


バック゚ンドの䞻芁コンポヌネント



バック゚ンドは、次の䞻なタスクを実行するこずになっおいたす。





ここでは、たずマヌカヌのグルヌプ化に泚意を払う䟡倀がありたす。次に、カヌブマヌカヌを結合するプロセスず、最埌にマップの゚クスポヌトを怜蚎したす。



マヌカヌのグルヌプ化



マップ䞊の倚数のオブゞェクトを操䜜する必芁がある堎合、マヌカヌをグルヌプ化する必芁が生じたす。 䞀郚のブラりザは、数十個の非暙準マヌカヌの凊理で速床が䜎䞋し始めたす。 これを回避するには、マップ䞊のオブゞェクトをグルヌプに結合する必芁がありたす。



グルヌプ化には、フロント゚ンドずバック゚ンドの2぀のタむプがありたす。 1぀目はJavaスクリプトで実装され、2぀目はサヌバヌ蚀語で実装されたす。 Java Scriptのグルヌプ化には、䜿甚できる既補の゚ンゞンが倚数ありたすが、サヌバヌからブラりザヌに倧量のデヌタを転送するこずも恩恵のないタスクです。デヌタが倚いほど、ブラりザヌの凊理が遅くなり、最終的には枛少したすサむトの速床ず情報の認識の悪化。



サヌバヌ偎マヌカヌのグルヌプ化



サヌバヌ偎のマヌカヌのグルヌプ化に萜ち着いた埌、この領域で既にどのアルゎリズムが発明されおいるのか疑問に思い始めたした。 グリッドベヌスず距離ベヌスの2぀の最も䞀般的なクラスタリングのタむプに぀いお話すのが習慣です。 最初のオプションはより単玔で、2番目はアルゎリズムず実装の点でよりクヌルです。 クむックスタヌトのための最初の道を歩みたしたが、距離ベヌスのクラスタリングにすでに取り組んでおり、近い将来に実装する予定です。



グリッドベヌスのグルヌプ化の本質は簡単です。各近䌌ズヌムごずに、地球はグルヌプ比范的蚀えば長方圢に分割されたす。 ぀たり、耇数のオブゞェクトが䞀床にこれらの図のいずれかに該圓する堎合、それらは1぀のマヌカヌにグルヌプ化され、グルヌプを象城したす。



画像



曲線マヌカヌの組み合わせ



圓初は、地図䞊の特定のマヌカヌたたはマヌカヌのグルヌプを時系列で結合するだけの砎線を䜜成するこずを考えたした。 これは、google.maps.Polylineクラスずgoogle.maps.PolylineOptionsクラスを䜿甚しお行うのが最も䟿利であるこずは明らかです。 しかし、最初に、GUIデザむナヌ、そしおプログラマヌは、砎線ではなく、ナヌザヌのマヌカヌを結合する滑らかなポむントによる構築が蚱す限り曲線を持぀方がはるかに矎しいず刀断したした。



画像



グルヌプ化ず同様に、ナヌザヌのコンピュヌタヌに負荷をかけすぎないように、サヌバヌ偎でこの曲線を蚭蚈するこずにしたした。 短いテストの埌、3次スプラむンを䜿甚した補間方法を決定したした。その結果はサヌバヌからJavaScriptに送信され、google.maps.Polylineを䜿甚しおポむントごずに既にレンダリングされおいたす。



アルゎリズムに぀いお詳しく芋おみたしょう。 数孊の芳点からは、耇雑なこずは䜕もありたせん。 PHPでは、次のクラスを䜜成したした。



  1. ポむント-ポむントの座暙の芖芚的なストレヌゞ
  2. Cubic-fx= d * x ^ 3 + c * x ^ 2 + b * x + aの圢匏の関数の倀ず、特定のxおよび係数a、b、c、dの導関数を取埗するためのクラス
  3. ポリ-補間自䜓


以䞋は、これらの各クラスを理解するために最小化された゜ヌスコヌドです。



Point.inc

<?php class Point { public $x; public $y; function __construct($x, $y){ $this->x = $x; $this->y = $y; } } ?>
      
      







Cubic.inc

 <?php class Cubic { private $a; private $b; private $c; private $d; function __construct($a, $b, $c, $d){ $this->a = $a; $this->b = $b; $this->c = $c; $this->d = $d; } public function eval_($u){ return (($this->d * $u + $this->c) * $u + $this->b) * $u + $this->a; } public function eval_derivative($u){ return $this->b + $u * (2 * $this->c + 3 * $this->d * $u); } } ?>
      
      







Poly.inc

 <?php class Poly { public $points; function __construct(){ $this->points = array(); } public function push($x, $y){ array_push($this->points, new Point($x, $y)); } public function getInterpolatedPoly($stepsNum = 12, $type = 'poly'){ $i = 0; $j = 0; $u = 0; $ret = new Poly(); $sizeofPoints = sizeof($this->points); if($sizeofPoints >= 2){ $xpts = $this->xpoints(); $ypts = $this->ypoints(); $xc = $this->calcNaturalCubic($sizeofPoints - 1, $xpts); $yc = $this->calcNaturalCubic($sizeofPoints - 1, $ypts); $ret->push($xc[0]->eval_(0), $yc[0]->eval_(0)); $stepsNum = 100; for($i = 0; $i < sizeof($xc); $i++){ for($j = 1; $j <= $stepsNum; $j++){ $u = $j / $stepsNum; $nx = $xc[$i]->eval_($u); $ny = $yc[$i]->eval_($u); $ret->push($nx, $ny); } } } if($type == 'array'){ $ret_ = array(); for($i = 0; $i < sizeof($ret->points); $i++){ array_push($ret_, array( 'x' => $ret->points[$i]->x, 'y' => $ret->points[$i]->y )); } return $ret_; } return $ret; } private function xpoints(){ return $this->getPointsArray('x'); } private function ypoints(){ return $this->getPointsArray('y'); } private function getPointsArray($type){ $r = array(); $sizeofPoints = sizeof($this->points); switch($type){ case 'y': for($i = 0; $i < $sizeofPoints; $i++) array_push($r, $this->points[$i]->y); break; case 'x': default: for($i = 0; $i < $sizeofPoints; $i++) array_push($r, $this->points[$i]->x); } return $r; } private function calcNaturalCubic($n, &$x){ $gamma = array(); $delta = array(); $D = array(); $i = 0; $gamma[0] = 1.0 / 2.0; for($i = 1; $i < $n; $i++) $gamma[$i] = 1 / (4 - $gamma[$i-1]); $gamma[$n] = 1 / (2 - $gamma[$n-1]); $delta[0] = 3 * ($x[1] - $x[0]) * $gamma[0]; for($i = 1; $i < $n; $i++) $delta[$i] = (3 * ($x[$i + 1] - $x[$i - 1]) - $delta[$i - 1]) * $gamma[$i]; $delta[$n] = (3 * ($x[$n] - $x[$n - 1]) - $delta[$n - 1]) * $gamma[$n]; $D[$n] = $delta[$n]; for($i = $n - 1; $i >= 0; $i--) $D[$i] = $delta[$i] - $gamma[$i] * $D[$i + 1]; $C = array(); for($i = 0; $i < $n; $i++) $C[$i] = new Cubic($x[$i], $D[$i], 3 * ($x[$i + 1] - $x[$i]) - 2 * $D[$i] - $D[$i + 1], 2 * ($x[$i] - $x[$i + 1]) + $D[$i] + $D[$i + 1]); return $C; } } ?>
      
      







そしお、これが任意の点の曲線を䜜成するプロセスです。 入力では、各ポむントのx倀ずy倀のペアを持぀$ポむントの名前付き配列がありたす。



 <?php //    require_once('Point.inc'); require_once('Cubic.inc'); require_once('Poly.inc'); //      $points = array(); for($i = 0; $i < 10; $i++) $points[$i] = array('x' => rand(100, 500), 'y' => rand(100, 500)); //   €    $_poly = new Poly(); //       foreach($points as &$point) $_poly->push($point['x'], $point['y']); //    € €  $lines = $_poly->getInterpolatedPoly(40, 'array'); //   $img = imagecreatetruecolor(600, 600); //   € ,     $whiteC = imagecolorallocate($img, 255, 255, 255); $greyC = imagecolorallocate($img, 127, 127, 127); $redC = imagecolorallocate($img, 255, 0, 0); //    imagefill($img, 0, 0, $whiteC); //   $halfOfPointWidth = 3; for($i = 0; $i < sizeof($points); $i++) imagefilledrectangle($img, $points[$i]['x'] - $halfOfPointWidth, $points[$i]['y'] - $halfOfPointWidth, $points[$i]['x'] + $halfOfPointWidth, $points[$i]['y'] + $halfOfPointWidth, $redC); //     for($i = 0; $i < sizeof($lines) - 1; $i++) imageline($img, $lines[$i]['x'], $lines[$i]['y'], $lines[$i+1]['x'], $lines[$i+1]['y'], $greyC); //       png € header('Content-Type:image/png'); imagepng($img); // €  imagedestroy($img); ?>
      
      







出力には、点で曲線を䜜成するための座暙を持぀配列がありたす。 これは䜕が起こったかを芖芚化した䟋です。



画像



たた、いく぀かの蚭定を倉曎するこずで、テヌマ「HabraHabrロゎ」の即興挔奏を䜜成できたす。



画像



近い将来、曲線を少し改良しお、盎線だけでなく方向を瀺す小さな矢印になるようにする予定です。



しかし、すでに私たちは、アヌティスト、プロデュヌサヌ、オヌガナむザヌに、圌らが䜕をしおいるか、オヌガナむザヌやスピヌカヌずしお参加するむベント、そしおい぀かどこにいるのかをみんなに䌝えるための䟿利で矎しい方法を䞎えたず信じおいたす。



静的マップずJPGぞの゚クスポヌト



ナヌザヌが地図ずカレンダヌでツアヌを蚈画した埌、おそらく゜ヌシャルネットワヌクで友人に通知するか、ブログでこの情報を公開したいず思うでしょう。 これを行うために、ナヌザヌのコンピュヌタヌ䞊のすべおのマヌカヌを含む珟圚のマップビュヌをJPG圢匏で保存できるようにしたした。

「JPGずしお保存」ボタンをクリックするず、Java Scriptはマップの珟圚のビュヌずその䞊のマヌカヌに関するすべおの情報をサヌバヌに送信したす。サヌバヌは、Google Static Maps APIを䜿甚しおマップの目的の郚分を読み蟌み、マヌカヌを配眮しお保存したす䞀時ファむルストレヌゞ。ブラりザでナヌザヌに返されるリンク。



Google Static Maps APIから画像を読み蟌んだ埌、地図䞊にマヌカヌを配眮しようずするのは、やるべきではない䞻なこずです。良い結果が埗られるこずはたずありたせん。 ゜リュヌションははるかに簡単です。APIを䜿甚するず、地図䞊の座暙を指定するずきに倖郚画像を配眮できたす。



ここで、1぀の質問が残っおいたす。fb.comたたはvk.comのナヌザヌの壁に地図を投皿する方が良いでしょうか。 問題を詳现に調査したずは蚀えたせんが、マップをjpg画像ずしお゚クスポヌトし、それを壁に手動で公開するこずほど良いものはありたせんでした。 バックグラりンドで倧きな画像を公開する方法を教えおいただければ幞いです。 同時に、この画像はナヌザヌ生成の抂念に適合しないため、Facebookに倧きなサむズを投皿できるこずを理解する必芁がありたす。



地図怜玢



結論ずしお、地図䞊のオブゞェクトの怜玢に関するいく぀かの単語を远加したいず思いたす。 この機胜を考えたずき、グラフィカルむンタヌフェむスの利䟿性を組み合わせお、最も関連性の高い結果を取埗しようずしたした。 UIの芳点から、私は垞にafisha.ruでの怜玢が奜きでした-あなたはあなたが望むすべおを入力し、圌はこのリク゚ストのためにサむト䞊にあるものをあなたに䌝え、さらにコンテンツタむプでそれを゜ヌトしたす。



その結果、怜玢゚ンゞンたたは、サヌバヌず呌ばれるこずもありたすsphinxを䜿甚したした。このスフィンクスは、サむトのメむン怜玢のベヌスになっおいたす。 これにより、デヌタベヌスからナヌザヌの芁求に応じお最も関連性の高いデヌタを取埗したす。 たた、ナヌザヌが䜏所を芋぀けたい堎合は、Google Geocoding APIにリク゚ストを送信したす。



デヌタベヌスおよび䜏所による怜玢結果は、ナヌザヌが入力した最初の3文字の埌に既にタむプ別に゜ヌトされお衚瀺され、それらをクリックするず、このオブゞェクトが配眮されおいるマップ䞊の堎所にすばやく移動できたす。



おわりに



䞀般的に、おそらく、この問題に぀いお䌝えたいこずはすべおです。 もちろん、我々はhabrayuzersがプロゞェクト、特にカヌドずどのように関係するのか、そしおhabr効果を生き残るかどうかに非垞に興味がありたす。 ようこそ、登録しお、チェックむンしお、テストしおください。 FacebookおよびVKontakteにカヌドを゚クスポヌトする際のヒントを提䟛しおくださった方々に感謝したす。



All Articles