There are very few articles that can be used as a reference, and it was the most difficult part of creating a portfolio, so I wrote it because I thought it would be good to leave it as an output for my own learning and to help someone! What is the meaning of this description as a beginner? I intend to explain the part that I think. of course! Please note that there are many parts that I think.
In addition to displaying Google Map with the Google Maps API, you can arbitrarily add it to the route search list from the marker balloon, and the goal is to search for routes at multiple points.
・ Ruby: 2.5.1 Rails: 5.2.1 ・ OS: macOS
・ Introduction of Slim -Enable the following APIs on the official Google Maps Platform ・ Maps JavaScript API → Display of Google Map ・ Geocoding API → Calculation of latitude and longitude from address ・ Directions API → Route search
Gemfile
gem 'dotenv-rails' #Make API key an environment variable
gem 'gon' #Make the instance variables defined in the controller available in JavaScript.
gem 'geocoder' #Calculate latitude and longitude from the address.
Terminal
$ bundle install
Create a ".env" file directly under the application
Terminal
$ touch .env
Write your API key in''
.env
GOOGLE_MAP_API = 'API key that you copied'
.gitignore
/.env
Gemfile
gem 'turbolinks' #Delete this line
app/assets/javascripts/application.js
//= require turbolinks //Delete this line
data-turbolinks-track': Remove'reload' attribute
ruby:app/views/layouts/application.html.slim
= stylesheet_link_tag 'application', media: 'all'
= javascript_include_tag 'application'
Create and edit geocorder configuration file
Terminal
$ touch config/initializers/geocoder.rb
config/initializers/geocoder.rb
#Postscript
Geocoder.configure(
lookup: :google,
api_key: ENV['GOOGLE_MAP_API']
)
This completes the settings. From here, we will enter the implementation that displays Google Map.
For your app, add an address column to your Place model. The latitude and longitude columns are the longitude and latitude values calculated from the values in the address column by the Geocoding API. Since it is a decimal value, the type uses float.
Terminal
$ rails g migration AddColumnsToPlaces address:string latitude:float longitude:float
Terminal
$ rails db:migrate
models/place.rb
#Postscript
geocoded_by :address #Calculate latitude and longitude based on the address column.
after_validation :geocode #Latitude and longitude are also changed when the address is changed.
controllers/places_controller.rb
def index
@place = Place.all
gon.place = @place #Postscript
end
private
def place_params
#Added "address" to strong parameters
params.require(:place).permit(:name, :description, :image, :address)
end
① Edit application.html.slim Write to load gon before CSS and JavaScript.
ruby:views/layouts/application.html.slim
doctype html
html
head
title
| app_name
= csrf_meta_tags
= csp_meta_tag
= include_gon #Postscript
= stylesheet_link_tag 'application', media: 'all'
= javascript_include_tag 'application'
② Add an address input form to the new registration screen
ruby:views/places/new.html.slim
= f.label :address, 'Street address'
= f.text_field :address, class: 'form-control'
③ Describe in the file that displays Google Map
ruby:views/places/index.html.slim
div id = 'map_index' #Give id,Google Map described in js file is embedded in this part
- google_api = "https://maps.googleapis.com/maps/api/js?key=#{ ENV['GOOGLE_MAP_API'] }&callback=initMap".html_safe
script{ async src = google_api }
.map-route
<Route search list>
ul id = "route-list" class = "list-group" #Add callout button in js file adds that location to the li element
div id = 'directions-panel' #Distance / time is embedded
<Distance / time between points>
ul id = "display-list" class = "display-group"
.map-search
= button_tag "Route search", id: "btn-search", class: "btn btn-primary", onclick: "search()" #Search by click processing()Call a function
[About the part of google_api = ~ ~ ~ ~] → Call the initMap function when reading in the callback process. → .html_safe is escape processing → The async attribute loads JavaScript asynchronously to speed up rendering.
④ Describe the size you want to display on Google Map in scss
stylesheets/application.scss
#map_index{
height: 400px;
width: 400px;
}
This is the liver. Create a new file directly under assets / javascripts and describe it. It may be hard to see for a long time, but after defining the variables, we just define each function. the function is, ・ InitMap ・ MarkerEvent (i) ・ AddPlace (name, lat, lng, number) ・ Search () There are four in the order of. Please refer to the commented out explanations for parts and points that are difficult to understand.
assets/javascripts/googlemap.js
var map
var geocoder
var marker = [];
var infoWindow = [];
var markerData = gon.places; //Assign the instance variable defined by the controller to the variable
var place_name = [];
var place_lat = [];
var place_lng = [];
//Function to display GoogleMap(Called in callback process)
function initMap(){
geocoder = new google.maps.Geocoder()
//View id='map_index'Embed Google Map in the part of
map = new google.maps.Map(document.getElementById('map_index'), {
center: { lat: 35.6585, lng: 139.7486 }, //Centering on Tokyo Tower
zoom: 9,
});
//Display multiple markers and balloons by iterative processing
for (var i = 0; i < markerData.length; i++) {
//Calculate the latitude and longitude of each point
markerLatLng = new google.maps.LatLng({
lat: markerData[i]['latitude'],
lng: markerData[i]['longitude']
});
//Display of markers
marker[i] = new google.maps.Marker({
position: markerLatLng,
map: map
});
//Display of balloons
let id = markerData[i]['id']
place_name[i]= markerData[i]['name'];
place_lat[i]= markerData[i]['latitude'];
place_lng[i]= markerData[i]['longitude'];
infoWindow[i] = new google.maps.InfoWindow({
//Contents of the balloon,Pass the array and array number of each attribute as an argument
content: `<a href='/places/${ id }'>${ markerData[i]['name'] }</a><input type="button" value="add to" onclick="addPlace(place_name, place_lat, place_lng, ${i})">`
});
markerEvent(i);
}
}
}
//Click the marker to display a balloon
function markerEvent(i) {
marker[i].addListener('click', function () {
infoWindow[i].open(map, marker[i]);
});
}
//Add to list
function addPlace(name, lat, lng, number){
var li = $('<li>', {
text: name[number],
"class": "list-group-item"
});
li.attr("data-lat", lat[number]); // data-lat to the attribute lat[number]Put in
li.attr("data-lng", lng[number]); // data-lng to the attribute lng[number]Put in
$('#route-list').append(li); //id is route-Add li to the end of the list element
}
//Search for a route
function search() {
var points = $('#route-list li');
//When there are two or more points
if (points.length >= 2){
var origin; //Starting point
var destination; //End point
var waypoints = []; //Waypoint
// origin, destination,Set waypoints
for (var i = 0; i < points.length; i++) {
points[i] = new google.maps.LatLng($(points[i]).attr("data-lat"), $(points[i]).attr("data-lng"));
if (i == 0){
origin = points[i];
} else if (i == points.length-1){
destination = points[i];
} else {
waypoints.push({ location: points[i], stopover: true });
}
}
//Make a request
var request = {
origin: origin,
destination: destination,
waypoints: waypoints,
travelMode: google.maps.TravelMode.DRIVING
};
//Root service request
new google.maps.DirectionsService().route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
new google.maps.DirectionsRenderer({
map: map,
suppressMarkers : true,
polylineOptions: { //Settings for drawn lines
strokeColor: '#00ffdd',
strokeOpacity: 1,
strokeWeight: 5
}
}).setDirections(response);//Line drawing part
//Display distance and time
var data = response.routes[0].legs;
for (var i = 0; i < data.length; i++) {
//distance
var li = $('<li>', {
text: data[i].distance.text,
"class": "display-group-item"
});
$('#display-list').append(li);
//time
var li = $('<li>', {
text: data[i].duration.text,
"class": "display-group-item"
});
$('#display-list').append(li);
}
const route = response.routes[0];
//View id='directions-panel'Embed in the part of
const summaryPanel = document.getElementById("directions-panel");
summaryPanel.innerHTML = "";
//Display the distance and time between each point
for (let i = 0; i < route.legs.length; i++) {
const routeSegment = i + 1;
summaryPanel.innerHTML +=
"<b>Route Segment: " + routeSegment + "</b><br>";
summaryPanel.innerHTML += route.legs[i].start_address + "<br>" + " ↓ " + "<br>";
summaryPanel.innerHTML += route.legs[i].end_address + "<br>";
summaryPanel.innerHTML += "<" + route.legs[i].distance.text + ",";
summaryPanel.innerHTML += route.legs[i].duration.text + ">" + "<br>";
}
}
});
}
}
__ Supplement to the content part of the content of the balloon: (because I struggled with the method of passing data) __
content: `<a href='/places/${ id }'>${ markerData[i]['name'] }</a><input type="button" value="add to" onclick="addPlace(place_name, place_lat, place_lng, ${i})">`
addPlace(place_name, place_lat, place_lng, ${i}) In the call to this function, the previous three arguments are passed as an array. The fourth argument is the expression expansion of the number (called an index) that represents what information in the array. Expression expansion in JavaScript seems to be this form. By providing such an argument, the function addPlace (name, lat, lng, number) can process information about what data it is normally.
Thank you for reading to the end. I myself am in a state where my portfolio is nearing completion and I have begun job hunting in earnest! We sincerely support those who have goals such as portfolio creation and job change activities, so let's do our best together! !!
-How to display multiple markers on Google Map and display a balloon when clicked
-[Search for routes of multiple points added to the list with Google Maps API] (https://qiita.com/yoshi_01/items/d3848e4e7c854fe585bd)
Recommended Posts