Yandex.Mapsのシェルとしてのリーフレット-マップ上に10万個のマーカーを表示

私はリーフレットが大好きです。 これを使用すると、インタラクティブマップを非常に迅速に作成できます。 ただし、タイル(カードのレイヤー)のほぼすべての利用可能なサプライヤーは、非常に印象的なお金でサービスを提供しています。 OSMなどのオープンソースプロジェクトは存在しますが、タイルが常に外観を満たしているとは限りません。



目的



目標は、彼の完全に自由なケンタウロスを盲目にすることでした。 私は常にYandexカードが好きでしたが、APIは好きではありませんでした。 したがって、Yandex-mapsをLeafletのレイヤーとして導入する問題に興味を持ちました。



完成したアプリケーションの例。 リポジトリには48 MBのダンプベースがあります。



作業例Habraeffectで生き残れない可能性があります。



大まかな研究



正当なYandexカードのリクエストを調べて、通信があるタイルサーバーを計算しました。



'http://vec{s}.maps.yandex.net/tiles?l=map&v=4.55.2&z={z}&x={x}&y={y}&scale=2&lang=ru_RU' {s} -  (subdomain),   ,              .    ,   01, 02, 03, 04 {z} -   (zoom) {x -  (latitude) {y} -  (longitude)
      
      





これは、リーフレット内でYandexマップタイルを使用するために必要なすべてのデータです。



実装



バックエンドについては、 Ruby On Railsを使用して、レールが遅いという神話を少し払拭します。 結局、地図上に10万個のマーカーを表示します!



まず、マーカーモデルを作成します。

 rails g model marker
      
      





移行コンテンツ
 class CreateMarkers < ActiveRecord::Migration def change create_table :markers do |t| t.float :lat t.float :lng t.string :name t.string :avatar t.string :website t.string :email t.string :city t.string :address t.string :phone t.text :about t.timestamps null: false end end end
      
      









 rake db:create rake db:migrate
      
      







私は、Fakerで埋められたフィールドを持つ100,000のマーカーを生成する小さな工場を書きました。 PostgreSQLを使用しています 。 ベースダンプはdb / db.dumpにあります。



工場
 # test/factories/markers.rb FactoryGirl.define do factory :marker do lat {Faker::Address.latitude} lng {Faker::Address.longitude} avatar {Faker::Avatar.image} name {Faker::Name.name} website {Faker::Internet.url} email {Faker::Internet.email} city {Faker::Address.city} address {Faker::Address.street_address} about {Faker::Hipster.paragraph} phone {Faker::PhoneNumber.cell_phone} end end # db/seeds.rb 100000.times do |num| FactoryGirl.create(:marker) ap "#{num}" end
      
      









マーカーモデルを制御するには、マーカーコントローラーを生成します。



 rails g controller markers
      
      







コントローラーコード
 class MarkersController < ApplicationController before_action :set_marker, only: [:show] def index respond_to do |format| format.html format.json { pluck_fields = Marker.pluck(:id, :lat, :lng) render json: Oj.dump(pluck_fields) } end end def show render "show", layout: false end private def set_marker @marker = Marker.find(params[:id]) end end
      
      









ARオブジェクトの作成に時間を無駄にしないために、 pluckメソッドを呼び出します。pluckメソッドは、必要なフィールドに対してのみSELECTリクエストを実行します。 これにより、生産性が大幅に向上します。 結果は配列の配列です:



 [ [1,68.324,-168.542], [2,55.522,59.454], [3,-19.245,-79.233] ]
      
      







また、 Oj gemを使用してjsonをすばやく生成します。 ビューでの損失は、100,000個のオブジェクトに対して2msを超えません。



routes.rbで新しいリソースを指定することを忘れないでください:



 Rails.application.routes.draw do root to: "markers#index" resources :markers, only: [:index, :show] end
      
      







マップ自体にアクセスします。



このような多数のマーカーには、クラスタリングが必要です。 Leafletには、必要な機能を追加するさまざまなプラグインが豊富に揃っています。 PruneClusterに決めました



必要なすべてのライブラリを接続します。



application.css
 /* *= normalize *= require leaflet *= require prune_cluster *= require_tree . *= require_self */
      
      







application.js
 //= require jquery //= require leaflet //= require prune_cluster //= require_self //= require_tree .
      
      









マップを描画するには、基本的なマークアップを作成する必要があります。



マーカー/ index.html.slim
  #map
      
      







application.css
 #map { position: fixed; left: 0; right: 0; top: 0; bottom: 0; }
      
      









これで、リーフレットマップを描画できます。

 var map = L.map('map').setView([54.762,37.375], 8), //    #map leafletView = new PruneClusterForLeaflet(); // ,      
      
      







マップには単一のレイヤーがないため、灰色の背景のみが表示されます。 マップへのレイヤーの追加は非常に簡単です。

 L.tileLayer( 'http://vec{s}.maps.yandex.net/tiles?l=map&v=4.55.2&z={z}&x={x}&y={y}&scale=2&lang=ru_RU', { subdomains: ['01', '02', '03', '04'], attribution: '<a http="yandex.ru" target="_blank"></a>', reuseTiles: true, updateWhenIdle: false } ).addTo(map);
      
      







これで、 #mapコンテナー内に、通常のYandexマップが表示されます。 ただし、地図投影を球形メルカトールから楕円形に再定義する必要があります。再定義しないと、座標に顕著なシフトが生じます。 同時に、リーフレットがマーカーのデフォルトアイコンを取得する場所を示します。



 map.options.crs = L.CRS.EPSG3395; L.Icon.Default.imagePath = "/leaflet";
      
      







すべてのマーカーを要求し、それらをマップに描画することは残ります。



 jQuery.getJSON("/markers.json", {}, function(res){ res.forEach(function (item) { leafletView.RegisterMarker(new PruneCluster.Marker(item[1], item[2], {id: item[0]})); }); map.addLayer(leafletView); })
      
      







マーカーに関する情報を取得できないため、マップは意味をなしません。 ポップアップを追加します。これは、マーカーをクリックしてサーバーからコンテンツを取得するときに呼び出されます。



 leafletView.PrepareLeafletMarker = function (marker, data) { marker.on('click', function () { jQuery.ajax({ url: "/markers/"+data.id }).done(function (res) { if (marker.getPopup()) { marker.setPopupContent(res) } else { marker.bindPopup(res); marker.openPopup(); } }) }) }
      
      







Popupに適切なマークアップを作成します。

マーカー/ show.html.slim
 h1 | #{@marker.name} .popup__address | #{@marker.city}, #{@marker.address} .nowrap .popup__avatar img src="#{@marker.avatar}" width="120" height="120" .popup__contacts .popup__contact b : div | #{@marker.phone} .popup__contact b . : div a href="mailto:#{@marker.email}" | #{@marker.email} .popup__contact b : div a href=" #{@marker.website}" target="_blank" | #{@marker.website} p | #{@marker.about}
      
      









まとめ



LeafletをYandex-cardsと統合しました。つまり、 leaflet-cardsのすべてのプラグインが利用可能になりました。 作成されたアプリケーションは、100,000個のマーカーの負荷に耐えるだけでなく、非常に便利な機能も備えています。



完成したアプリケーションの例。 リポジトリには48 MBのダンプベースがあります。



All Articles