I was able to add google map in order checkout page "onepagecheckout" by some steps, through which we will change and add code in some views in the theme directory, and adding "Address custom attributes" through the control panel, but these steps do not serve as a general solution for all types of stores. It is suitable only for stores in need of direct delivery process. And also I had one problem that it is not working in "Shipping Address" step, it works only in "Billing" step.
What I have done is the following:
1- Adding "Latitude" & "Longitude" attributes in "/Admin/Setting/CustomerUser" in "Address form fields" tab
2- Setting these attributes type as text boxes and making them not required.
3- Adding map content to the view located in theme folder : "Themes/.../Views/Shared/_AddressAttributes.cshtml", if it is not found in the folder simply copy it from the root project views folder:
@model IList<Nop.Web.Models.Common.AddressAttributemodel>
@using Nop.Core.Domain.Catalog;
@*changes1*@
@{
string GoogleMapsApiKey = "YourAPIKeyhere";
string latStr = "";
string lngStr = "";
}
<style>.pickup-points-map {min-width: 500px;height: 500px;margin: 20px;padding: 10px;border: 1px solid #d0d0d0;}</style>
@*end changes1*@
@foreach (var attribute in Model)
{
string controlId = string.Format("address_attribute_{0}", attribute.Id);
string textPrompt = attribute.Name;
@*changes2*@
if (textPrompt.ToLower() == "latitude")
{
latStr = attribute.DefaultValue;
}
if (textPrompt.ToLower() == "longitude")
{
lngStr = attribute.DefaultValue;
}
@*end changes2*@
<div class="inputs custom-attributes">
<label>@textPrompt:</label>
@switch (attribute.AttributeControlType)
{
case AttributeControlType.DropdownList:
{
<select name="@(controlId)" id="@(controlId)">
@if (!attribute.IsRequired)
{
<option value="0">---</option>
}
@foreach (var attributeValue in attribute.Values)
{
<option selected="@attributeValue.IsPreSelected" value="@attributeValue.Id">@attributeValue.Name</option>
}
</select>
}
break;
case AttributeControlType.RadioList:
{
<ul class="option-list">
@foreach (var attributeValue in attribute.Values)
{
<li>
<input id="@(controlId)_@(attributeValue.Id)" type="radio" name="@(controlId)" value="@attributeValue.Id" checked="@attributeValue.IsPreSelected" />
<label for="@(controlId)_@(attributeValue.Id)">@attributeValue.Name</label>
</li>
}
</ul>
}
break;
case AttributeControlType.Checkboxes:
case AttributeControlType.ReadonlyCheckboxes:
{
<ul class="option-list">
@foreach (var attributeValue in attribute.Values)
{
<li>
<input id="@(controlId)_@(attributeValue.Id)" type="checkbox" name="@(controlId)" value="@attributeValue.Id" checked="@attributeValue.IsPreSelected" @(attribute.AttributeControlType == AttributeControlType.ReadonlyCheckboxes ? Html.Raw(" disabled=\"disabled\"") : null) />
<label for="@(controlId)_@(attributeValue.Id)">@attributeValue.Name</label>
</li>
}
</ul>
}
break;
case AttributeControlType.TextBox:
{
<input name="@(controlId)" type="text" class="textbox" id="@(controlId)" value="@attribute.DefaultValue" />
}
break;
case AttributeControlType.MultilineTextbox:
{
<textarea id="@(controlId)" name="@(controlId)">@attribute.DefaultValue</textarea>
}
break;
case AttributeControlType.Datepicker:
case AttributeControlType.FileUpload:
case AttributeControlType.ColorSquares:
case AttributeControlType.ImageSquares:
{
//not support attribute type
}
break;
}
@if (attribute.IsRequired)
{
@Html.RequiredHint()
}
</div>
}
@*changes3*@
@{
double InitLat = 48.864716;
double InitLng = 2.349014;
double lat = 0;
double lng = 0;
double.TryParse(latStr, out lat);
double.TryParse(lngStr, out lng);
if (lat == 0 || lng == 0)
{
lat = InitLat; lng = InitLng;
}
var src = string.Format("https://maps.googleapis.com/maps/api/js{0}", string.IsNullOrEmpty(GoogleMapsApiKey) ? string.Empty : string.Format("?key={0}", GoogleMapsApiKey));
<div class="pickup-points-map" id="map">
<script type="text/javascript">
$(document).ready(function () {
if (typeof google === 'object' && typeof google.maps === 'object') {
gmapInit();
} else {
$.getScript("@src", function (data, textStatus, jqxhr){
gmapInit();
});
}
});
function gmapInit(){
var markersA = new Map();
var googleMap = null;
google.maps.visualRefresh = true;
googleMap = new google.maps.Map(document.getElementById("map"), {
zoom: 15,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var geocoder = new google.maps.Geocoder();
//var infowindow = new google.maps.InfoWindow();
@if(lat>0 && lng>0)
{
<text>
(function() {
var marker = new google.maps.Marker({
map: googleMap,
title: "Address",
draggable: true,
position: new google.maps.LatLng(@lat, @lng),
icon: "http://maps.google.com/mapfiles/ms/icons/blue-dot.png"
});
markersA.set("asdasdas", marker);
google.maps.event.addListener(marker, 'dragend', function () {
$("#address_attribute_1").val(this.getPosition().lat());
$("#address_attribute_2").val(this.getPosition().lng());
});
google.maps.event.addListener(googleMap, 'click', function (event) {
placeMarker(event.latLng);
});
function placeMarker(location) {
marker.setPosition(location);
// if we could rename the two text boxes attributes we added in admin area
// as address_attribute_lat & address_attribute it would be better
$("#address_attribute_1").val(marker.getPosition().lat());
$("#address_attribute_2").val(marker.getPosition().lng());
}
googleMap.setCenter(marker.getPosition())
}());
</text>
}
}
</script>
</div>
}
@*end changes3*@
4- By this we can read the latitude and longitude that the customer selected through the map and can see it in the order details in admin area. You can use the data and create map link like in the shipping info in the order details page in admin area
This is my solution to add the map to the client's address, but I'm sure there are better solutions that can be provided by more experienced developers. I would be grateful if there was a solution to the problem I mentioned above or giving us a better solution than this solution.
P.S.: I used the map code from shipping view provided by nopcommerce team.