在Rails中使用Google Maps

地图是导航世界的方式。 他们可以检查地球上的每个位置,从而为诸如路线和土壤成分之类的服务开辟道路。 在本文中,我将介绍Google Maps API(和一些替代方法)并将其与Rails集成。

教程源存储库

源代码可从Github存储库中获得 。 每个功能都是单独的提交,因此您可以使用git checkout跟随教程的进度 。

先决条件

  • 使用RailsBricks的Ruby on Rails的基础知识。
  • JavaScript的中级知识
  • 网络地图如何工作。 这篇文章值得一读,如果你是新的游戏。

我们的目标是简化与Rails的地图集成。 本教程详细介绍了集成Google Maps的确切步骤,其中涵盖了一些有用的Rails gem。 最后,将简要介绍一下Google Maps的替代方案,例如流行的开源库Leaflet.jsMapBox

初始化地图

过去,Google Maps需要一个API密钥,但是, 此API密钥在V3上不再是必需的 。 如果您使用的是其他版本,请按照下列步骤操作:

如果您使用Google Maps V3,请跳过此部分

  1. 转到Google API控制台

  2. 点击API和身份验证-> API。

  3. 点击“状态”按钮将其激活,以激活Google Maps JavaScript API v3。

  4. 单击凭据->创建新密钥->浏览器密钥

  5. 确保ACCEPT REQUESTS FROM THESE HTTP REFERERS (WEB SITES)为空或包含您的域,如下图所示。

注意:
下面的材料并不涵盖地图的每个方面。 有关更多详细信息,您可以访问Google Maps JavaScript文档 。

载入地图

首先,使用RailsBrick创建项目( 您可以自由使用任何工具来构建Rails应用程序 )。 它是Rails应用程序的创建者,具有开箱即用的基本功能。 您可以从此处的介绍视频中获得如何使用它。

要在我们的主页中初始化地图,请在主视图( /app/views/home.erb )中创建一个具有map-canvas ID的div。 将这个div包裹在另一个ID为map-container的ID中,该ID将用于向地图添加一些样式。

第一:这是主视图的代码:

<% title("Home Page") %>

Google Maps Tut

添加一些CSS。 打开名为framework overrides.css.scss的文件,该文件是RailsBricks样板文件的一部分,用于覆盖Bootstrap样式。

第二:添加以下样式:

#map-container {
   height: 400px;
   border-radius: 16px 16px;
   border-color: #fff;
   border-style: solid;
   box-shadow: 2px 2px 10px #B1B1B1;
   margin-top: 25px;
   border-width: 7px;
 }
 #map-canvas {
   height: 384px;
   width: 100%;
 }

如您在上面的CSS中所见,我们将map-container设置为400像素的固定高度,并添加了一些边框样式。 获取初始工作图的最后一步是在app / assets / javascript / map中创建一个名为“ map”的文件夹,并添加一个名为gmap.js的文件。 现在,地图应如下所示:

注意:
如果地图缩放控制器无法正确显示,则与图像和标签的Bootstrap样式冲突。 只需将以下替代添加到您的CSS( framework overrides.css.scss )文件中:

/* Bootstrap Css Map Fix*/
#map-container #map-canvas img {
  max-width: none;
}
/* Bootstrap Css Map Fix*/
#map-container #map-canvas label {
  width: auto; display:inline;
}

在地图上绘图

基本标记

Google Maps API包含一个标记对象,可让您轻松创建简单的标记。 Marker对象包含诸如标记位置 ,标记标题和标记所处地图之类的属性。

为避免重复代码,请创建一个名为createMarker的函数,其参数为coordsmaptitle

var marker;
function createMarker(coords, map, title){
  marker = new google.maps.Marker({
    position: coords,
    map: map,
    title: title
  });
}

自定义标记

标记对象具有“图标”属性,可以采用路径图像对象。 我们将创建两个功能:一个用于创建图像 ,一个用于创建自定义标记 。 要使标记可拖动,只需添加值为true的 可拖动属性即可。 另外,Google Maps API支持两种动画标记: DROPBOUNCE

创建一个名为createImage的函数,该函数将返回自定义标记使用的图像对象。 它的大小是32×32像素,其原点是(0,0)。

function createImage(url){
  var image = {
    url: url,
    // This marker is 32 pixels wide by 32 pixels tall.
    size: new google.maps.Size(32, 32),
    // The origin for this image is 0,0.
    origin: new google.maps.Point(0,0),
    // The anchor for this image is the base of the flagpole at 0,32.
    anchor: new google.maps.Point(0, 32)
  };
  return image;
}

接下来,创建一个名为createCustomMarker的函数来完成创建标记对象的实际工作。 它包含坐标,地图对象和标记的标题。 使用函数createImage可以为我们的图标返回正确的图像。

function createCustomMarker(coords,map,title){
  marker = new google.maps.Marker({
    position: coords,
    map: map,
    title: title,
    icon: createImage("/assets/icon.png")
  });
}

信息窗口

信息窗口是用于显示内容(文本或图像)的工具提示。 您可以将信息窗口添加到标记或指定的longitudelatitudelonlat )上。 InfoWindow对象采用一个InfoWindowOptions对象。

function createInfoWindow(text){
  var infowindow = new google.maps.InfoWindow({
    content: text
  });
  return infowindow;
}

将以下代码放入initialize()函数中

// add infowindow when clicking on the simple marker marker
var info = createInfoWindow("Congratulations!");
google.maps.event.addListener(marker, 'click', function() {
  info.open(map,marker);
});

此代码将创建一个名为info的Infowindow并放置文本“ Congratulations!”。 在里面。 事件侦听器处理标记上的click事件以打开Infowindow。

画线

绘图线需要一系列坐标才能连接。 Google Maps API提供了一个称为Polyline的对象,用于绘制具有stroke_colorweightopacity属性的线,并添加图标,符号或动画。

简单线

// drawing static polyline
var lineCoordinates = [
  new google.maps.LatLng(30.055487, 31.279766),
  new google.maps.LatLng(30.223356, 31.324345),
  new google.maps.LatLng(30.345656, 31.567677),
  new google.maps.LatLng(30.565678, 31.676887)
];
createPolyline(map, lineCoordinates, lineSymbol);

var linePath;
function createPolyline(map,lineCoordinates,lineSymbol){
  linePath = new google.maps.Polyline({
    path: lineCoordinates,
    geodesic: true,
    strokeColor: '#FF0000',
    strokeOpacity: 1.0,
    strokeWeight: 2
   });
 linePath.setMap(map);
}

我们用折线的坐标定义一个名为lineCoordinates的数组。 函数createPolyline实际上创建折线,并使用lineCoordinates数组设置其pathgeodesic属性为true ,它告诉Google Maps为我们处理复杂的数学运算。 将其笔划颜色设置为#FF0000 ,将不透明度设置为1,将笔划粗细设置为2以使其可见。 准备好折线对象后,使用setMap函数将其添加到地图中。

带有短划线的简单线条。

绘制虚线仅是创建样式并告诉该线使用它的问题。 下面,将lineSymbol变量添加到折线中。 注意, lineSymbol必须遵循的路径和4中的一个刻度createPolyline功能被修改,以使用lineSymbol为重复图标。

var lineSymbol = {
  path: 'M 0,-1 0,1',
  scale: 4,
  strokeOpacity: 1,
  strokeColor: '#393'
};

// modify the createPolyline function to contain the symbol
var linePath;
function createPolyline(map, lineCoordinates, lineSymbol){
  linePath = new google.maps.Polyline({
    path: lineCoordinates,
    geodesic: true,
    strokeColor: '#FF0000',
    strokeOpacity: 1.0,
    strokeWeight: 2,
     icons: [{ // this Array is for adding symbols to the line
      icon: lineSymbol,
      offset: '100%'
    }]
  });
  linePath.setMap(map);
}

动画短剑

我们甚至可以通过添加一个称为animateCircle的函数来对破折号进行动画处理,该函数可以通过更改偏移量沿线移动图标。 甚至不需要更改createPolyline函数。

function animateCircle() {
  var count = 0;
  window.setInterval(function() {
    count = (count + 1) % 200;

    var icons = linePath.get('icons');
    icons[0].offset = (count / 2) + '%';
    linePath.set('icons', icons);
  }, 20);
}

//modify the `createPolyline` function to be like the following
var linePath;
function createPolyline(map, lineCoordinates, lineSymbol){
  linePath = new google.maps.Polyline({
    path: lineCoordinates,
    geodesic: true,
    strokeColor: '#FF0000',
    strokeOpacity: 1.0,
    strokeWeight: 2,
     icons: [{ // this Array is for adding symbols to the line
      icon: lineSymbol,
      offset: '0',
      repeat: '20px'
    }]
  });
   linePath.setMap(map);
}

然后,在使用createPolyline函数创建折线之后,调用animateCircle()函数。

用户创建的动态折线

在下面的代码中,我们将折线选项/属性添加到变量中,并使用它来创建折线。 这与上面的代码示例差别不大。 在地图上添加click事件侦听器,以向我们的线路添加一个点,从而允许用户随意绘制。

// drawing dynamic polyline
var polyOptions = {
  strokeColor: '#000000',
  strokeOpacity: 1.0,
  strokeWeight: 3
};
poly = new google.maps.Polyline(polyOptions);
poly.setMap(map);
google.maps.event.addListener(map, 'click', addLatLng);

function addLatLng(event){
  var path = poly.getPath();
  // Because path is an MVCArray, we can simply append a new coordinate
  // and it will automatically appear.
  path.push(event.latLng);
}

绘制多边形

多边形与折线相似,因为它们是通过一系列坐标绘制的。 多边形具有strokefill ,可以自定义。 我们将在名为polygonCoords的数组中手动​​添加多边形的坐标 ,并将其传递给名为drawingPolygon的新函数。 此函数创建多边形并将其路径设置为polygonCoords数组中添加的坐标。 多边形也是draggableeditable

简单多边形

// drawing polygon
var polygonCoords = [
  new google.maps.LatLng(30.055487, 31.279766),
  new google.maps.LatLng(30.466465, 31.118292),
  new google.maps.LatLng(30.321384, 31.75737),
  new google.maps.LatLng(30.055487, 31.279766)
];

// Construct the polygon.
drawingPolygon(polygonCoords);

function drawingPolygon(polygonCoords) {
  var polygon = new google.maps.Polygon({
    paths: polygonCoords,
    strokeColor: '#FF00FF',
    strokeOpacity: 0.8,
    strokeWeight: 2,
    fillColor: '#FF0000',
    fillOpacity: 0.35,
    draggable:true,
    editable: true
  });
  polygon.setMap(map);
}

使用图形库在地图上绘图

Google Maps API支持提供图形界面的图形库。 该GUI允许用户在地图上绘制折线,多边形,圆形,标记和三角形。

要将地图库加载到地图上,只需使地图API URL包含&libraries=drawing并开始使用DrawingManager对象。

API源链接应如下所示:

https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=drawing

初始化DrawingManager对象:

// trying the drawing liberary
var drawingManager = new google.maps.drawing.DrawingManager({
  drawingMode: null,
  drawingControl: true,
  drawingControlOptions: {
    position: google.maps.ControlPosition.TOP_CENTER,
    drawingModes: [
      google.maps.drawing.OverlayType.MARKER,
      google.maps.drawing.OverlayType.CIRCLE,
      google.maps.drawing.OverlayType.POLYGON,
      google.maps.drawing.OverlayType.POLYLINE,
      google.maps.drawing.OverlayType.RECTANGLE
    ]
  },
  markerOptions: {
    icon: "/assets/icon.png"
  }
});
drawingManager.setMap(map);

DrawingManager构造函数允许您通过指定要渲染的叠加层,其在地图上的位置及其初始状态来管理地图绘制工具(控件)。 将drawingMode设置为null意味着它不会默认为特定的叠加层控件。 通过将null更改为google.maps.drawing.OverlayType.POLYLINE可以将其与折线叠加层默认设置。

第二个参数是drawingControl这需要true用于呈现控制或false隐藏它。 drawingControlOptions指定控件在地图上的位置。 Google Maps提供了放置控件的各种位置,例如TOP_CENTERTOP_RIGHTBOTTOM_LEFT等。

参数还指定了可用的drawingModes ,这是可用google.maps.drawing.OverlayType常量的数组,例如CIRCLEPOLYLINEPOLYGONSRECTANGLEMARKER 。 您还可以为每个叠加层赋予特定的属性,就像我们在前面的代码片段中所做的一样。

最后一步是在drawingManager上设置地图。

添加地图服务

地理编码和反向地理编码

Google Maps API提供了一个称为Geocoder的类,用于动态获取已知地址的坐标位置(地理编码),反之亦然(反向地理编码)。

尽管该服务不再需要API密钥,但确实将地理编码限制为每天2,500,并且要求生成的应用程序使用Google Map显示数据。 返回的数据是JSON或XML。

var geocoding  = new google.maps.Geocoder();
$("#submit_button_geocoding").click(function(){
  codeAddress(geocoding);
});
$("#submit_button_reverse").click(function(){
  codeLatLng(geocoding);
});

通过地理编码获取坐标

在这里,我们通过在输入字段中输入地址来获取坐标。

function codeAddress(geocoding){
  var address = $("#search_box_geocoding").val();
  if(address.length > 0){
    geocoding.geocode({'address': address},function(results, status){
      if(status == google.maps.GeocoderStatus.OK){
        map.setCenter(results[0].geometry.location);
        var marker = new google.maps.Marker({
          map: map,
          position: results[0].geometry.location
        });
      } else {
        alert("Geocode was not successful for the following reason: " + status);
      }
    });
  } else {
    alert("Search field can't be blank");
  }
}

通过反向地址解析获取地址

在这种情况下,我们将latlng变量传递给geocode对象,以生成地图上的位置(地址)。

function codeLatLng(geocoding) {
  var input = $('#search_box_reverse').val();
  console.log(input);

  var latlngbounds = new google.maps.LatLngBounds();
  var listener;
  var regex = /([1-9])+\.([1-9])+\,([1-9])+\.([1-9])+/g;
  if(regex.test(input)) {
    var latLngStr = input.split(",",2);
    var lat = parseFloat(latLngStr[0]);
    var lng = parseFloat(latLngStr[1]);
    var latLng = new google.maps.LatLng(lat, lng);
    geocoding.geocode({'latLng': latLng}, function(results, status) {
      if (status == google.maps.GeocoderStatus.OK) {
        if(results.length > 0) {
          //map.setZoom(11);
          var marker;
          map.setCenter(results[1].geometry.location);
          var i;
          info = createInfoWindow("");
          for(i in results){
            latlngbounds.extend(results[i].geometry.location);
              marker = new google.maps.Marker({
              map: map,
              position: results[i].geometry.location
            });

            google.maps.event.addListener(marker, 'click', (function(marker,i) {
              return function() {
                info.setContent(results[i].formatted_address);
                info.open(map,marker);
              }
            })(marker,i));
          }

          map.fitBounds(latlngbounds);
          listener = google.maps.event.addListener(map, "idle", function() {
            if (map.getZoom() > 16) map.setZoom(16);
            google.maps.event.removeListener(listener);
          });
        }
      } else {
        alert("Geocoder failed due to: " + status);
      }
    });
  } else {
    alert("Wrong lat,lng format!");
  }
}

产生方向

Google Map API为计算两个或多个地址之间的路线提供了很好的指导服务。 可以通过初始化google.maps.DirectionsService来启用此服务,该服务不带任何参数,但是有一个名为route()方法。 此方法有两个参数: google.maps.DirectionsRequest的对象和回调函数。

DirectionRequest的基本属性是origindestination和定义运输方式的travelModeDirectionsStatus包含路线请求的回复状态。

要公开生成的路线,可以使用DirectionsRenderer ,它不带任何参数,并具有一个用于定义地图的名为setMap的方法和一个用于设置返回的响应的名为setDirections的方法。

有关路线服务的更多详细信息,请阅读本教程

var directionsService = new google.maps.DirectionsService();
var directionsDisplay = new google.maps.DirectionsRenderer();

map = new google.maps.Map(document.getElementById("map-canvas"),mapOptions);

directionsDisplay.setMap(map);

var request = {
  origin: "Mansoura, Daqahlia, Egypt",
  destination: "Cairo, Egypt",
  travelMode: google.maps.DirectionsTravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
  //Check if request is successful.
  if (status == google.maps.DirectionsStatus.OK) {
    console.log(status);
    directionsDisplay.setDirections(response); //Display the directions result
  }
});

地图控件

Google Maps在地图上提供控件,用于处理和操作地图UI。 这些控件可以被禁用,移动,甚至可以通过新功能进行自定义。

可用的控件有:

  • 变焦控制
  • 平移控制
  • 比例控制
  • MapType控件
  • 街景控制
  • 旋转控制
  • 概述地图控件

可以通过向地图选项添加disableDefaultUI: true来禁用默认UI控件。

要删除默认控件之一,请将其添加为MapOptions上的属性,例如panControl: truezoomControl: false

var mapOptions = {
  center: new google.maps.LatLng(30.055487, 31.279766),
  zoom: 8,
  mapTypeId: google.maps.MapTypeId.NORMAL,
  panControl: true,
  zoomControlOptions: {
    style: google.maps.ZoomControlStyle.SMALL,
    position: google.maps.ControlPosition.LEFT_CENTER
  },
  mapTypeControlOptions: {
    style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,
    mapTypeIds: [google.maps.MapTypeId.ROADMAP, "map_style"]
  },
  scaleControl: false,
  streetViewControl: true,
  overviewMapControl: true
};

自定义地图样式

此功能使您能够操纵标准基础地图显示UI。

地图表示由两个特征组成:地图特征包括道路,公园,山脉等,以及各种地图元素的样式。

陷阱:样式数组允许使用的字符数有限,如果超出限制,则无法将样式应用于地图。

要创建自定义地图样式,请执行以下操作:

第一:创建风格阵列有两个基本属性MapFeatures样式

var mapstyle = [
  {
    "featureType": "administrative.locality",
    "elementType": "labels.icon",
    "stylers": [
      { "invert_lightness": true },
      { "color": "#e40952" },
      { "visibility": "on" }
    ]
  },{
    "featureType": "water",
    "elementType": "geometry.fill",
    "stylers": [
      { "visibility": "on" },
      { "hue": "#5eff00" },
      { "color": "#282744" },
      { "weight": 0.1 },
      { "saturation": -56 },
      { "lightness": 22 },
      { "gamma": 3.91 }
    ]
  }
]

第二:在地图选项中设置mapTypeId

var mapOptions = {
  center: new google.maps.LatLng(30.055487, 31.279766),
  zoom: 8,
  mapTypeId: google.maps.MapTypeId.NORMAL,
  mapTypeControlOptions: {
    style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,
    mapTypeIds: [google.maps.MapTypeId.ROADMAP, "map_style"]
  }
};

第三:实例化StyledMapType ,提供预定义的mapstyle数组和地图名称。 该名称将作为样式选项出现在控件中。

var styledMap = new google.maps.StyledMapType(mapstyle, {name: "styled map"});
map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);

第四:将我们的自定义地图样式添加到地图的mapTypes

map.mapTypes.set("map_style", styledMap);

第五:mapType ID设置为我们的样式地图:

map.setMapTypeId("map_style");

有用的Rails宝石

地理编码器

Geocoder gem为Ruby提供地理编码,反向地理编码,查找附近位置,确定距离和地图服务。

注意:
利用地理编码支持rails3rails4 ,还有另一个分支rails2

可以使用gem install geocoder Gemfile或将gem "geocoder" Gemfile gem "geocoder"添加到Gemfile并运行bundle install来像任何Ruby gem一样gem install geocoder

在通过街道地址或邮政编码获取它们后,必须将两个浮点字段(纬度,经度)添加到用于存储纬度和经度值的任何模型中,如下所示:

rails generate migration AddLatitudeAndLongitudeToModel lat:float lng:float
rake db:migrate

另外,将以下内容添加到模型中以指定将使用的服务(地理编码器或反向地理编码器):

geocoded_by :address
# auto-fetch coordinates and the condition is for preventing fetching the same address more than once
after_validation :geocode, if: :address_changed?

full_street_address需要在模型上实现以创建可读地址。

注意:
Geocoder支持某些流行的数据库,例如(MySQL,PostgreSQL,MongoDB)。

Geocoder还可以为您提供一种在各种地理编码提供程序之间进行交换的简便方法。

GMaps4rails

GMaps4rails是一款出色的宝石,可提供地理编码和地图位置。 渲染标记时,它使用JS生成过滤器。 它还具有地理编码功能,可计算简单的latlng值。

您可以将此宝石与Geocoder宝石特征结合使用,使用GMaps4rails在地图上呈现结果。

安装

首先:将以下行添加到您的Gemfile文件中:

gem 'gmaps4rails

然后运行bundle install

第二:添加一个div来保存地图:

第三:将Google脚本添加到application.html.erb布局中:


第四:因为Gmaps4rails使用它,所以也需要underscore.js库。 在您的Rails application.js

//= require underscore
//= require gmaps/google

现在,如下创建地图:

handler = Gmaps.build('Google');
handler.buildMap(
  {
    provider: {
      disableDefaultUI: true
      // here you can pass other Google Maps API options here
    },
    internal: {
      id: 'map'
    }
  },
  function() {
    markers = handler.addMarkers([
      {
        "lat": 0,
        "lng": 0,
        "picture": {
          "url": "https://addons.cdn.mozilla.net/img/uploads/addon_icons/13/13028-64.png",
          "width":  36,
          "height": 36
        },
        "infowindow": "hello!"
      }
    ]);
    handler.bounds.extendWith(markers);
    handler.fitMapToBounds();
  }
);

有关此gem的更多详细信息,请单击此链接 。

Google Maps的替代品

Leaflet.js

Leaflet是一个用于嵌入地图的现代JavaScript库,它由于实现标记,覆盖和操作各种地图组件的简单易行而广受欢迎。 可以使用大量可用插件扩展传单。 它使用许可的BSD开源许可证,因此可以将其添加到任何站点而没有法律问题。 此外,它还支持多个地图提供程序,包括OpenStreetMap,MapQuestOpen,Stamen,Esri和OpenWeatherMap。

安装

从其官方网站leaflet.com下载它。 它可以作为.zip文件或github上的fork使用 。

一段用来说明Leaflet简单性的代码:

// create a map in the "map" div, set the view to a given place and zoom
var map = L.map('map').setView([51.505, -0.09], 13);

// add an OpenStreetMap tile layer
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
    attribution: '© OpenStreetMap contributors'
}).addTo(map);

// add a marker in the given location, attach some popup content to it and open the popup
L.marker([51.5, -0.09]).addTo(map)
    .bindPopup('A pretty CSS3 popup. 
Easily customizable.') .openPopup();

查看传单快速入门 ,了解更多信息。

地图框

MapBox是一个漂亮的工具,可灵活地创建地图。 它具有使用TileAill (地图设计工作室)(可下载的应用程序)设计具有精美图层和许多自定义功能的自定义地图的功能。 或者,您可以使用其JavaScript API创建带有自定义标记和叠加层的MapBox Web应用程序。

安装

您只需要在MapBox上开设一个帐户即可 。 在MapBox上创建一些地图后,这些地图ID将用于将其与Web应用程序集成。

本示例将使您了解如何将MapBox与JavaScript结合使用。

在MapBox上创建地图后,在您的项目中包含Mapbox.js库。

首先:初始化地图:

var map = mapbox.map('map');

第二:设置地图的缩放范围和中心缩放点:

map.setZoomRange(5, 15);

map.centerzoom({
    lat: 37.871385,
    lon: -99.228516
}, 5);

第三:添加您在MapBox上创建的自定义图层。

map.addLayer(mapbox.layer().id('YOUR-MAP-ID-HERE'));

之后,您可以在地图中嵌入更多功能,例如标记,UI功能(全屏,缩放)等等。

注意:
这不是有关如何使用MapBox的深入教程,但是将其公开为Google Maps的替代选项。

摘要

本教程对如何创建地图和常规地图概念有基本的了解。 希望您现在可以使用所选的任何地图API创建地图以满足您的需求。

From: https://www.sitepoint.com/use-google-maps-rails/

你可能感兴趣的:(javascript,ui,ruby,ViewUI)