A dúvida que sempre persiste se assemelha à:
- Estou em Porto Alegre - RS e posso atender até 100 km desta cidade;
Ok, se tratando de mapas a solução mais utilizada é localizar a cidade de Porto Alegre e criar um raio/buffer de 100km, porém todos sabemos que esta distância é irreal, não condiz com a realidade das nossas estradas a fora.
A complexidade de termos uma base de dados e o custo que a mesma representa, muitas vezes foge da realidade das empresas, por este motivo podemos nos guiar através da API do Google para aproximar o resultado que nos traga um custo/benefício de acordo com a realidade da nossa empresa.
Através da API do Google, podemos utilizar até mesmo a API do Google Maps versão 2, basta você preencher a distância em KM que desejar, clique em Setar e após clique no local que deseja no mapa.
1) Ele irá criar um raio azul com a distância que você preencheu em linha reta, ou seja, como geralmente é feito nos dias de hoje, sem levar em consideração as rodovias.
2) Irá criar um poligono em verde, levando em consideração esta mesma distância preenchida através de rodovias.
Esta sim é a distância real do local onde você está, é uma solução de baixo custo, possui seus problemas (você irá perceber perto de fronteiras, ou rios, etc), mas é resolutiva e possui um ótimo custo/benefício para demandas em empresas que a geolocalização não é o produto principal que demande grandes investimentos.
Código Fonte:
1) MStatusControl.js
/*
MStatusControl
Copyright 2008 - Marcelo Montagna - http://maps.forum.nu
Free to use as long as copyright notices are left unchanged.
Please save the file to your own server. Do not link directly,
or unexpected things might happen to your control :-)
------------------------------------------------------------
Adaptado por Maurício Reckziegel para suas necessidades
2012 - Maurício Reckziegel - mauricioreckziegel.blogspot.com.br
------------------------------------------------------------
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
------------------------------------------------------------
Note: This script contains code to prevent hotlinking. (marked with 'REMOVE')
You need to remove it when saving the file to your server.
Usage:
map.addControl(new MStatusControl());
map.addControl(new MStatusControl(options?));
map.addControl(new MStatusControl({DMS:true}));
MStatusControl options:
DMS: Boolean - Default: false - Show Degrees, Minutes, Seconds
Position: GControlPosition()
vertical: Boolean - Default: false - Make the control taller and more narrow
background: HTML color - Default: '#eeeeee';
foreground: HTML color - Default: '#000000';
*/
/////////////////////////////////////////////////////////////////////////////
function MStatusControl(MOptions) {
MOptions = MOptions ? MOptions : {};
this.DMS = MOptions.DMS ? MOptions.DMS : false;
this.background = MOptions.background ? MOptions.background : '#eeeeee';
this.foreground = MOptions.foreground ? MOptions.foreground : '#000000';
this.vertical = MOptions.vertical ? MOptions.vertical : false;
this.position = MOptions.position ? MOptions.position : new GControlPosition(G_ANCHOR_BOTTOM_LEFT, new GSize(0, 45));
this.parent = MOptions.container ? MOptions.container : null;
}
MStatusControl.prototype = new GControl(true,true);
MStatusControl.prototype.initialize = function(map) {
var globalThis_3456 = this;
this.map = map;
this.projection = this.map.getCurrentMapType().getProjection();
this.container = document.createElement('div');
if (!this.vertical) {
var w = this.map.getContainer().clientWidth - 2;
this.container.style.width = w + 'px';
}
this.container.style.backgroundColor = this.background;
this.container.style.border = '1px solid gray';
var innerDiv = document.createElement('div');
this.container.appendChild(innerDiv);
var crDiv = document.createElement('div');
this.container.appendChild(crDiv);
crDiv.innerHTML = 'Maurício Reckziegel©2012 - Adaptado de <a style="color: #aaaaaa;" href="http://maps.forum.nu" target="_blank">http://maps.forum.nu</a>';
crDiv.style.padding = '2px';
crDiv.style.marginTop = '2px';
crDiv.style.textAlign = 'right';
crDiv.style.color = '#aaaaaa';
crDiv.style.backgroundColor = '#eeeeee';
crDiv.style.font = 'normal 10px verdana';
var oTable = document.createElement('table');
oTable.setAttribute('cellSpacing','0');
oTable.setAttribute('cellPadding','0');
if (!this.vertical) {
oTable.style.width = '100%';
}
innerDiv.appendChild(oTable);
var oTableBody = document.createElement('tbody');
oTable.appendChild(oTableBody);
if (this.vertical) {
//-------------------------
var oRow = document.createElement('tr');
oTableBody.appendChild(oRow);
this.centerDisplay = document.createElement('td');
this.setStyleValue(this.centerDisplay);
// this.centerDisplay.style.width = iWidth + 'px';
oRow.appendChild(this.centerDisplay);
//-------------------------
var oRow = document.createElement('tr');
oTableBody.appendChild(oRow);
this.zDisplay = document.createElement('td');
this.setStyleValue(this.zDisplay);
oRow.appendChild(this.zDisplay);
//-------------------------
var oRow = document.createElement('tr');
oTableBody.appendChild(oRow);
this.swDisplay = document.createElement('td');
this.setStyleValue(this.swDisplay);
oRow.appendChild(this.swDisplay);
//-------------------------
var oRow = document.createElement('tr');
oTableBody.appendChild(oRow);
this.neDisplay = document.createElement('td');
this.setStyleValue(this.neDisplay);
oRow.appendChild(this.neDisplay);
//-------------------------
var oRow = document.createElement('tr');
oTableBody.appendChild(oRow);
this.mouseDisplay = document.createElement('td');
this.setStyleValue(this.mouseDisplay);
// this.mouseDisplay.style.width = iWidth + 'px';
oRow.appendChild(this.mouseDisplay);
//-------------------------
var oRow = document.createElement('tr');
oTableBody.appendChild(oRow);
this.mousePxDisplay = document.createElement('td');
this.setStyleValue(this.mousePxDisplay);
oRow.appendChild(this.mousePxDisplay);
//-------------------------
var oRow = document.createElement('tr');
oTableBody.appendChild(oRow);
this.mouseTileDisplay = document.createElement('td');
this.setStyleValue(this.mouseTileDisplay);
oRow.appendChild(this.mouseTileDisplay);
//-------------------------
var oRow = document.createElement('tr');
oTableBody.appendChild(oRow);
this.clickDisplay = document.createElement('td');
this.setStyleValue(this.clickDisplay);
oRow.appendChild(this.clickDisplay);
//-------------------------
}
else {
//-------------------------
var oRow = document.createElement('tr');
oTableBody.appendChild(oRow);
this.centerDisplay = document.createElement('td');
this.setStyleValue(this.centerDisplay);
// this.centerDisplay.style.width = iWidth + 'px';
oRow.appendChild(this.centerDisplay);
this.mouseDisplay = document.createElement('td');
this.setStyleValue(this.mouseDisplay);
// this.mouseDisplay.style.width = iWidth + 'px';
oRow.appendChild(this.mouseDisplay);
//-------------------------
var oRow = document.createElement('tr');
oTableBody.appendChild(oRow);
this.swDisplay = document.createElement('td');
this.setStyleValue(this.swDisplay);
oRow.appendChild(this.swDisplay);
this.mousePxDisplay = document.createElement('td');
this.setStyleValue(this.mousePxDisplay);
oRow.appendChild(this.mousePxDisplay);
//-------------------------
var oRow = document.createElement('tr');
oTableBody.appendChild(oRow);
this.neDisplay = document.createElement('td');
this.setStyleValue(this.neDisplay);
oRow.appendChild(this.neDisplay);
this.mouseTileDisplay = document.createElement('td');
this.setStyleValue(this.mouseTileDisplay);
oRow.appendChild(this.mouseTileDisplay);
//-------------------------
var oRow = document.createElement('tr');
oTableBody.appendChild(oRow);
this.zDisplay = document.createElement('td');
this.setStyleValue(this.zDisplay);
oRow.appendChild(this.zDisplay);
this.clickDisplay = document.createElement('td');
this.setStyleValue(this.clickDisplay);
oRow.appendChild(this.clickDisplay);
//-------------------------
}
this.mouseDisplay.innerHTML = 'Mouse LatLon: '
this.mousePxDisplay.innerHTML = 'Mouse Px: '
this.mouseTileDisplay.innerHTML = 'Mouse Tile: '
this.clickDisplay.innerHTML = 'Mouse Click: '
if (this.parent) {
this.parent.appendChild(container);
}
else {
this.map.getContainer().appendChild(this.container);
}
GEvent.addListener(this.map, "click", function(ol,pt){globalThis_3456.MMapClick(pt)});
GEvent.addListener(this.map, "moveend", function(){globalThis_3456.MMoveEnd()});
GEvent.addListener(this.map, "zoomend", function(){globalThis_3456.MZoomEnd()});
GEvent.addListener(this.map, "mousemove", function(pt){globalThis_3456.MMouseMove(pt)});
this.MMapClick = function (pt) {
var point = pt ? pt : null;
if (point) {
if (this.DMS) {
this.clickDisplay.innerHTML = 'Mouse Click: ' + this.degToDms(point.lat()) + ', ' + this.degToDms(point.lng());
}
else {
this.clickDisplay.innerHTML = 'Mouse Click: ' + point.lat().toFixed(6) + ', ' + point.lng().toFixed(6);
}
}
}
this.MMoveEnd = function() {
this.MUpdateStatus();
}
this.MZoomEnd = function() {
this.MUpdateStatus();
}
this.MMouseMove = function(mousePt) {
var zoom = this.map.getZoom();
var mousePx = this.projection.fromLatLngToPixel(mousePt, zoom);
if (this.DMS) {
this.mouseDisplay.innerHTML = 'Mouse LatLon: ' + this.degToDms(mousePt.lat()) + ', ' + this.degToDms(mousePt.lng());
}
else {
this.mouseDisplay.innerHTML = 'Mouse LatLon: ' + mousePt.lat().toFixed(6) + ', ' + mousePt.lng().toFixed(6);
}
this.mousePxDisplay.innerHTML = 'Mouse Px: ' + mousePx.x + ', ' + mousePx.y;
this.mouseTileDisplay.innerHTML = 'Mouse Tile: ' + Math.floor(mousePx.x / 256) + ', ' + Math.floor(mousePx.y / 256);
}
this.MUpdateStatus = function() {
var center = this.map.getCenter();
var zoom = this.map.getZoom();
var bounds = this.map.getBounds();
var SW = bounds.getSouthWest();
var NE = bounds.getNorthEast();
if (this.DMS) {
this.centerDisplay.innerHTML = 'Map Center: ' + this.degToDms(center.lat()) + ', ' + this.degToDms(center.lng());
this.swDisplay.innerHTML = 'Map SW: ' + this.degToDms(SW.lat()) + ', ' + this.degToDms(SW.lng());
this.neDisplay.innerHTML = 'Map NE: ' + this.degToDms(NE.lat()) + ', ' + this.degToDms(NE.lng());
this.zDisplay.innerHTML = 'Map Zoom: ' + zoom;
}
else {
this.centerDisplay.innerHTML = 'Map Center: ' + center.lat().toFixed(6) + ', ' + center.lng().toFixed(6);
this.swDisplay.innerHTML = 'Map SW: ' + SW.lat().toFixed(6) + ', ' + SW.lng().toFixed(6);
this.neDisplay.innerHTML = 'Map NE: ' + NE.lat().toFixed(6) + ', ' + NE.lng().toFixed(6);
this.zDisplay.innerHTML = 'Map Zoom: ' + zoom;
}
}
this.MUpdateStatus();
return this.container;
}
MStatusControl.prototype.degToDms = function(dec) {
var deg = Math.floor(Math.abs(dec));
var min = Math.floor((Math.abs(dec)-deg)*60);
var sec = (Math.round((((Math.abs(dec) - deg) - (min/60)) * 60 * 60) * 100) / 100 ) ;
var len = String(deg).length
deg = Array(3 + 1 - len).join('0') + deg;
var len = String(min).length
min = Array(2 + 1 - len).join('0') + min;
var len = String(sec).length
sec = Array(5 + 1 - len).join('0') + sec;
deg = dec < 0 ? '-' + deg : deg;
var dms = deg + '° ' + min + '\' ' + sec + '"';
return dms;
}
MStatusControl.prototype.getDefaultPosition = function() {
return this.position;
}
////////////////////////////
MStatusControl.prototype.setStyleValue = function(obj) {
obj.style.padding = '0px';
obj.style.paddingRight = '2px';
obj.style.paddingLeft = '2px';
obj.style.textAlign = 'left';
obj.style.color = this.foreground;
obj.style.backgroundColor = this.background;
obj.style.font = 'normal 12px courier new';
obj.style.lineHeight = '14px'
obj.setAttribute('noWrap','true');
if (!this.vertical) {
obj.style.width = '50%'
}
}
MStatusControl.prototype.show = function () {
this.container.style.display = '';
}
MStatusControl.prototype.hide = function () {
this.container.style.display = 'none';
}
MStatusControl.prototype.toggle = function () {
this.container.style.display = this.container.style.display == '' ? 'none' : '';
}
//////////// END MStatusControl /////////////////
2) mapa.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>
<title>Google Maps</title>
<script>
var scriptTag = '<' + 'script src="http://maps.google.com/maps?file=api&v=2&key=">'+'<'+'/script>';
document.write(scriptTag);
</script>
<script src="MStatusControl.js"></script>
<script type="text/javascript">
//<![CDATA[
var map;
var container;
var zoom = 9;
var centerPoint = new GLatLng(-30.02932,-51.227);
var dirObj = new GDirections();
var centerMarker;
var circleMarkers = Array();
var circlePoints = Array();
var drivePolyPoints = Array();
var searchPolygon,drivePolygon;
var distToDrive = 30;
var pointInterval = 30;
GEvent.addListener(dirObj, "load", onDirectionsLoad);
GEvent.addListener(dirObj, "error", onDirectionsError);
var baseIcon2 = new GIcon();
baseIcon2.iconSize=new GSize(8,8);
baseIcon2.iconAnchor=new GPoint(4,4);
baseIcon2.infoWindowAnchor=new GPoint(4,0);
var redIcon8 = (new GIcon(baseIcon2, "http://maps.forum.nu/images/redSquare_8.png", null, ""));
function SetarDistancia()
{
distToDrive = document.getElementById("distancia").value;
document.getElementById("divDistancia").innerHTML = "Distância atual: " + distToDrive + " km";
}
function load() {
if (GBrowserIsCompatible()) {
container = document.getElementById("mapDiv");
map = new GMap2(container, {draggableCursor:"crosshair"});
map.addMapType(G_NORMAL_MAP)
map.setCenter(centerPoint, zoom, G_NORMAL_MAP);
map.addControl(new GScaleControl());
map.addControl(new GLargeMapControl());
map.addControl(new GMapTypeControl());
var pos = new GControlPosition(G_ANCHOR_BOTTOM_LEFT, new GSize(0, -80));
map.addControl(new MStatusControl({position:pos}));
map.enableContinuousZoom();
map.enableScrollWheelZoom();
GEvent.addListener(map, "click", mapClick);
document.getElementById("divDistancia").innerHTML = "Distância atual: " + distToDrive + " km";
}
}
function mapClick(ov,pt) {
map.clearOverlays();
circleMarkers = Array();
if (!centerMarker) {
centerMarker = new GMarker(pt);
}
else {
map.removeOverlay(centerMarker);
centerMarker.setPoint(pt);
}
map.addOverlay(centerMarker);
searchPoints = getCirclePoints(pt,distToDrive);
drivePolyPoints = Array();
getDirections();
}
function getCirclePoints(center,radius){
var circlePoints = Array();
var searchPoints = Array();
with (Math) {
var rLat = (radius/3963.189) * (180/PI); // miles
var rLng = rLat/cos(center.lat() * (PI/180));
for (var a = 0 ; a < 361 ; a++ ) {
var aRad = a*(PI/180);
var x = center.lng() + (rLng * cos(aRad));
var y = center.lat() + (rLat * sin(aRad));
var point = new GLatLng(parseFloat(y),parseFloat(x),true);
circlePoints.push(point);
if (a % pointInterval == 0) {
searchPoints.push(point);
}
}
}
searchPolygon = new GPolygon(circlePoints, '#0000ff', 1, 1, '#0000ff', 0.2);
map.addOverlay(searchPolygon);
map.setCenter(searchPolygon.getBounds().getCenter(),map.getBoundsZoomLevel(searchPolygon.getBounds()));
return searchPoints;
}
function getDirections() {
if (!searchPoints.length) {
return;
}
var from = centerMarker.getLatLng().lat() + ' ' + centerMarker.getLatLng().lng();
var to = searchPoints[0].lat() + ' ' + searchPoints[0].lng();
searchPoints.shift();
var loadStr = 'from: ' + from + ' to: ' + to;
dirObj.load(loadStr,{getPolyline:true,getSteps:true});
}
function onDirectionsLoad() {
var status = dirObj.getStatus();
var bounds = dirObj.getBounds();
var distance = parseInt(dirObj.getDistance().meters / 1609);
var duration = parseFloat(dirObj.getDuration().seconds / 3600).toFixed(2);
var polyline = dirObj.getPolyline();
shortenAndShow(polyline);
getDirections();
}
function shortenAndShow(polyline) {
//var distToDriveM = distToDrive * 1609; //milhas
var distToDriveM = distToDrive * 1000; //km
var dist = 0;
var cutoffIndex = 0;
var copyPoints = Array();
for (var n = 0 ; n < polyline.getVertexCount()-1 ; n++ ) {
dist += polyline.getVertex(n).distanceFrom(polyline.getVertex(n+1));
if (dist < distToDriveM) {
copyPoints.push(polyline.getVertex(n));
}
else {
break;
}
}
var lastPoint = copyPoints[copyPoints.length-1];
var newLine = new GPolyline(copyPoints, '#ff0000', 2, 1);
map.addOverlay(newLine);
drivePolyPoints.push(lastPoint);
addBorderMarker(lastPoint,dist)
if (drivePolyPoints.length > 3) {
if (drivePolygon) {
map.removeOverlay(drivePolygon);
}
drivePolygon = new GPolygon(drivePolyPoints, '#00ff00', 1, 1, '#00ff00', 0.4);
map.addOverlay(drivePolygon);
}
}
function addBorderMarker(pt,d) {
//var str = pt.lat().toFixed(6) + ',' + pt.lng().toFixed(6) + ' - Distância: ' + (d/1609).toFixed(2) + ' miles';
var str = pt.lat().toFixed(6) + ',' + pt.lng().toFixed(6) + ' - Distância: ' + (d/1000).toFixed(2) + ' km';
var marker = new GMarker(pt,{icon:redIcon8,title:str});
circleMarkers.push(marker);
map.addOverlay(marker);
}
function onDirectionsError() {
getDirections();
}
function unload() {
GUnload();
}
//]]>
</script>
</head>
<body>
<center>
<table cellspacing="5" cellpadding="10">
<tr>
<td valign="top">
<a href="#" onclick="map.clearOverlays();">Limpar Mapa</a> |
Distância (km): <input type="text" id="distancia" value="30" size="5" /> <button onclick="SetarDistancia()">Setar</button> |
<span id="divDistancia">Distância atual: </span>
</td>
</tr>
<tr>
<td valign="top">
<div id="mapDiv" style="width: 800px; height: 500px"></div>
</td>
</tr>
</table>
</center>
<script>
window.onload = load;
window.onunload = unload;
</script>
</body>
</html>
Fonte: http://maps.forum.nu