domingo, 3 de junho de 2012

Mapas - Aprimorando o cálculo das distâncias (via Rodovias)

Em um mundo corporativo muitas vezes nos deparamos com problemas ligados a distância entre um determinado ponto e sua abrangência. 


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&copy;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&nbsp;LatLon:&nbsp;'  
      this.mousePxDisplay.innerHTML = 'Mouse&nbsp;Px:&nbsp;'  
      this.mouseTileDisplay.innerHTML = 'Mouse&nbsp;Tile:&nbsp;'  
      this.clickDisplay.innerHTML = 'Mouse&nbsp;Click:&nbsp;'  
      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&nbsp;Click:&nbsp;&nbsp;' + this.degToDms(point.lat()) + ',&nbsp;' + this.degToDms(point.lng());  
           }  
           else {  
                this.clickDisplay.innerHTML = 'Mouse&nbsp;Click:&nbsp;&nbsp;' + point.lat().toFixed(6) + ',&nbsp;' + 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&nbsp;LatLon:&nbsp;' + this.degToDms(mousePt.lat()) + ',&nbsp;' + this.degToDms(mousePt.lng());  
      }  
      else {  
           this.mouseDisplay.innerHTML = 'Mouse&nbsp;LatLon:&nbsp;' + mousePt.lat().toFixed(6) + ',&nbsp;' + mousePt.lng().toFixed(6);  
      }  
      this.mousePxDisplay.innerHTML = 'Mouse&nbsp;Px:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' + mousePx.x + ',&nbsp;' + mousePx.y;  
      this.mouseTileDisplay.innerHTML = 'Mouse&nbsp;Tile:&nbsp;&nbsp;&nbsp;' + Math.floor(mousePx.x / 256) + ',&nbsp;' + 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&nbsp;Center:&nbsp;&nbsp;&nbsp;' + this.degToDms(center.lat()) + ',&nbsp;' + this.degToDms(center.lng());  
           this.swDisplay.innerHTML = 'Map&nbsp;SW:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' + this.degToDms(SW.lat()) + ',&nbsp;' + this.degToDms(SW.lng());  
           this.neDisplay.innerHTML = 'Map&nbsp;NE:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' + this.degToDms(NE.lat()) + ',&nbsp;' + this.degToDms(NE.lng());  
           this.zDisplay.innerHTML = 'Map&nbsp;Zoom:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' + zoom;  
      }  
      else {  
           this.centerDisplay.innerHTML = 'Map&nbsp;Center:&nbsp;' + center.lat().toFixed(6) + ',&nbsp;' + center.lng().toFixed(6);  
           this.swDisplay.innerHTML = 'Map&nbsp;SW:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' + SW.lat().toFixed(6) + ',&nbsp;' + SW.lng().toFixed(6);  
           this.neDisplay.innerHTML = 'Map&nbsp;NE:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' + NE.lat().toFixed(6) + ',&nbsp;' + NE.lng().toFixed(6);  
           this.zDisplay.innerHTML = 'Map&nbsp;Zoom:&nbsp;&nbsp;&nbsp;' + 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 + '&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>&nbsp;|&nbsp;  
                Distância (km): <input type="text" id="distancia" value="30" size="5" /> <button onclick="SetarDistancia()">Setar</button>&nbsp;|&nbsp;  
                <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

sábado, 2 de junho de 2012

Analista de Inovação em TI


Não vou falar que chegou o momento de reinventar, ou que precisamos mudar a forma de pensar e agir ou, ainda mais radical, que precisamos esquecer tudo que conhecemos de TI e recriá-la. São muitas as teorias e conceitos que fomentam um salto enorme do cenário que vivenciamos em nossas empresas para o que o mercado vem nos apresentando. Onde estivemos nesses últimos anos? Porque agora teríamos que esquecer tudo que sabemos e recriar a TI? A inovação não foi e nunca terá um abismo para realidade. São as empresas que estiveram muitos anos estagnadas na metodologia mais utilizada desde os primórdios. Que metodologia é esta? É a do “não se meche em time que se esta ganhando”. Muitas das empresas precisaram estar perdendo para mudar o seu foco de investimentos, dar um passo atrás, voltar alguns metros do caminho errado ou, melhor, identificar a saída correta do labirinto do sucesso.

Sua empresa possui um abismo para a inovação?
Dica: Faça um pé de meia!

Uma das principais mudanças que as empresas precisam investir é em novas equipes e novos cargos. A TI vem se ramificando e necessita de especialistas. A grandeza da sua empresa é tão quanto à ramificação que ela consegue se especializar, dentro da missão que ela se propõe.

Procura-se Analista de Inovação:
Pra que? Prospectar novas tecnologias e Identificar oportunidades.
Como deve ser? Pró-Ativo e possuir experiência.
Grande diferencial: Ter bom relacionamento inter-pessoal: (principalmente com a área de Segurança e Infra-Estrutura).

Release ou Stable?

Você já deve ter ouvido do seu gerente que a última versão, stable ok?, do sistema não serve para a empresa. Empresas como Google, não são suficientemente confiáveis para fazer parte do seu negócio. Talvez um analista de inovação possa te dar bons motivos sobre esta mudança. Deixamos claro que não defendo essa mudança, e sim ao menos um debate embasado se devemos mudar.

Em 2014 os sistemas móveis irão superar as plataformas PC.

Não vamos sair trocando todos os PCs por celular e tablets, calma lá. Mas vamos prover recursos para que o seu produto chegue a estas pessoas. Meu iPad não tem muita graça se for usado apenas com os itens de fábrica. Vou ler e-mails, navegar na internet (algumas coisas não vão abrir porque são em Flash e não HTML5 (sua empresa usa flash? Leia na internet “Adobe vai descontinuar o Flash para dispositivos móveis.”)).

Quantos porcentos sua empresa gasta para manter tudo como esta e quanto em inovação?

A inovação muitas vezes é semelhante a terras nunca habitadas. É necessária uma equipe pró-ativa, que vença barreiras e assuma riscos controlados. O investimento em inovação demora mais para se ter um produto fidedigno das idealizações e confiança. Convencer a infra-estrutura e a segurança da informação ás mudanças nunca foi uma tarefa tão difícil. Convenhamos, vamos nos colocar nesse lugar, inovação em se tratando de software é cartaz de NÃO boas vindas na porta. Mas por quê? Aqui entra frase um famoso meme, “você não esta fazendo isso certo”. 
Posso ser criticado por esse comentário, mas, minha opinião, o maior erro do orkut foi ter virado um produto do Google, ainda mais sem dono. Ele foi uma inovação criada por um dos seus funcionários no tempo livre disponível pela empresa (todos já conhecem esta história), porém, ao ser incorporado como um produto passou a ser operador pelo Google Brasil (amigos do Google Brasil, não culpo vocês pelo fracasso ok?), quando perdeu mercado para uma empresa chamada Facebook e sua grande equipe e política inovadora. Facebook mostrou que a rede social não é apenas um produto e sim uma empresa com mais de três mil funcionários. O motivo pelo qual o orkut perdeu mercado? Vocês mesmos podem pensar nessa resposta, vocês foram os responsáveis. Porém lembro que a maioria dos produtos inovadores que foram vistos com grandes olhos por entusiastas (Wave, Gears) tropeçaram nas suas próprias políticas inovadoras.

Será que chove? Que diabos é essa nuvem?

Bom, sou relativamente novo, mas lembro muito bem dos terminais burros que tínhamos em nossas lojas há muitos anos atrás. Confesso que mesmo novo admirava a possibilidade de termos acesso a uma base de dados centralizada através daquele monitor/teclado tela verde em vários pontos do estabelecimento. Imagina você sair de casa e o seu computador ir junto? Ou ainda, você nunca mais precisar formatar o computador do seu pai? Apenas você vai falar, pai, clica em restaurar configurações de fábrica que tuas coisas estão lá ainda. Falando em questões empresariais, aquele teu ájax de UFs, aquela base de CEPs, Logradouros, poxa, todo mundo tem isso. Porque todo mundo precisa atualizar? O mundo é um retrabalho que os WebServices não resolveram. Quem sabe esse maldito tempo um dia faça sol para nossos lados. Pensar em soluções globais parece um futuro muito distante, mas isso é uma realidade dentro de diversas empresas onde os próprios setores ou parceiros não se interagem.

Seu médico trabalha com inovação ou ele te trata ainda com os mesmos métodos de uma década atrás?
Fica apenas a pergunta.

E a TI no serviço público?
É amigos, como falar de tecnologia de ponta enquanto a realidade ainda são sistemas em ACCESS, arquiteturas mainframe, entre outros...???

Tecnologia - um grande aliado na luta contra a criminalidade


Tecnologia - um grande aliado na luta contra a criminalidade

"As organizações políticas, como os Estados e os Municípios, não atingirão seu pleno desenvolvimento se não contarem com informações atualizadas, precisas e rápidas sobre os melhores meios de distribuírem seus recursos gerados por seus povos.".

Começo este artigo citando uma frase de Cezar Henrique Rocha, 2000. A alusão a esta frase não apenas se enquadra neste artigo, mas também é uma crítica a todos o órgão públicos e seu pequeno investimento em tecnologia.
Notoriamente a tecnologia aos poucos passa a fazer parte de políticas públicas, auxiliando nos processos de organização e distribuição. Fato que há pouco tempo temos acesso a notícias como a integração da base de dados de setores públicos de diversos estados, o que, convenhamos, não é uma tecnologia muito atual, apesar de que muitas vezes vemos repartições ainda com arquivos em papel.
Quando tratamos de criminalidade no Brasil, nos deparamos com uma forte falta de transparência nas informações. As poucas estatísticas que existem são genéricas, e o que mais se vê é o pleno senso comum dos locais onde existe o considerado "maior risco". A falta de transparência nos faz pensar que a forma como as informações são tratadas soam de forma precária e nenhuma ação é realizada em cima das mesmas.
Penso que não só a redução da criminalidade, mas outras tomadas de decisão são possíveis se houver a implantação de políticas com o objetivo de prevenir e reduzir as ações criminais, fundamentalmente utilizando pesquisas que permitam avançar no entendimento das causas destes fenômenos, gerindo uma base de dados espacial e temporal dos atos ocorridos. 
O cruzamento destas informações com dados estatísticos (dados censitários, imagens de satélite, cadastros urbanos, etc.) tendem a explicar algumas ações e determinar as principais áreas onde existe a necessidade de maior atenção. Estes dados, aliados a tecnologias como sistemas de informação geográfica, possibilitam a criação de diversas análises como, por exemplo, a identificação de territórios criminais, a determinação de fronteiras, a recuperação de bens roubados e a análise de zonas quentes, entre outros.
A criminalidade é um fenômeno social e depende apenas de ações sociais para reduzi-la. Ou seja, apenas os dados sobre atos criminosos não são tomadores de decisão e não justificam as ações a serem executadas, mas sim o cruzamento com dados de fatores sócio-econômicos, ocupações desordenadas, falta de infra-estrutura, desemprego, tráfico e consumo de drogas, aumento desenfreado da população etc., com o objetivo de entender o que resulta neste aumento da criminalidade e possíveis atos sociais a serem executados para uma redução a longo prazo.
A importância da qualidade dos dados juntamente com a espacialização da informação relacionada ao tempo, aliada a dados públicos existentes, tende a ser não a resposta, mas a pergunta que falta nos meios de hoje: "o que fazer? Onde fazer?".

Funciona em outros países?

Alguns países têm já bem desenvolvidos sistemas de informações sobre a criminalidade.
  • Reino Unido: http://maps.police.uk/ - Sistema de informação geográfica mantido pelos órgãos públicos da Inglaterra.
Los Angeles - http://www.lapdcrimemaps.org/ - Sistema de informações geográfico mantido por órgãos públicos de Los Angeles, muito bom.
USA - Mapping Crime, US Departament of Justice, Keith Harries, Ph.D., 1999.

E no Brasil?

O que fazemos?

A falta de recursos faz com que a sociedade desenvolva serviços de utilidade pública mesmo antes do Estado. Parti deste princípio quando desenvolvi o MapaCrime, baseado em muitos outros sistemas existentes, com mais ênfase aos citados acima.
São geralmente sistemas voltados à sociedade, sem fins lucrativos e com baixa credibilidade, visto que qualquer pessoa pode entrar e cadastrar a informação que queira, mentirosa ou não.
"Um site que muitos esperam não utilizar", acredito que é o melhor slogan que podemos dar. Além de tudo, o retorno do usuário ao site é quase zero. Quantas vezes por dia você irá visitar este site? Mesmo assim, a sociedade se mexe e busca soluções capazes de trazer à tona os principais problemas.
O grande diferencial do site é tornar públicas também as ocorrências não notificadas aos órgãos municipais ou estaduais responsáveis pela segurança, já que muitas vezes nem boletins de ocorrência são abertos. Porém, é preciso salientar que hoje existem delegacias online, onde os BOs devem ser realizados para ao menos contabilizar e cobrarmos ações dos órgãos públicos responsáveis.
Delegacias Online

Concluindo

A falta de transparência aliada ao aumento da criminalidade e aos avanços tecnológicos da sociedade demonstram o enfraquecimento dos Estados e Municípios nas ações do dia-a-dia. Enquanto empresas privadas se utilizam de tecnologias de ponta para aumentar a sua carteira de clientes e buscam insaciavelmente a qualificação de seus processos, o setor público sofre com o seu atraso tecnológico e falta de investimento.

Google Directions e Elevation API - Aperfeiçoando o cálculo de autonomia de veículos


Google Directions e Elevation API - Aperfeiçoando o cálculo de autonomia de veículos

Olá, amigos! Estou de volta, com muitas idéias e pouco tempo para escrever.
Bom, há algum tempo o Google disponibilizou algumas APIs que eu já sentia falta em 2009, teriam facilitado minha vida um bocado no ano passado. São elas:
Obs.: Verifique os limites de utilização e termos de uso de cada API antes de utiliza-las, tanto academicamente quanto comercialmente. Até a publicação deste artigo, havia apenas documentação em English.

Directions API

A Directions é um serviço que calcula as direções entre dois locais (podendo haver pontos de passagem) através de uma solicitação HTTP, ou seja, para quem já trabalha com a API do Google Maps não é preciso mais de um objeto do tipo GMap para conseguir capturar informações sobre uma determinada rota ou utilizar a Google Maps Javascript API V3 Services (http://code.google.com/intl/en/apis/maps/documentation/javascript/services.html#Directions).
Esta API aceita tanto endereços do tipo texto como também latitudes e longitudes previamente formatadas de acordo com o padrão estabelecido.
Como eu a chamo? No browser, basta digitar a seguinte URL:http://maps.google.com/maps/api/directions/output?parameters
Tipos de Saída (output) Até o momento, XML ou JSON
Como está ficando?
XML: http://maps.google.com/maps/api/directions/xml?parameters
JSON: http://maps.google.com/maps/api/directions/json?parameters
Parameters:
  • origin: Endereço ou latitude e longitude em formato texto da origem da rota.
  • destination: Endereço ou latitude e longitude em formato texto do destino da rota.
  • mode (opcional, o default é de carro): [driving, walking, bicycling] só não te esqueces que o Google não se responsabiliza por onde você irá passar.
  • waypoints (opcional): pontos de passagem, caso queira fazer uma paradinha no meio da rota ou passar por algum local específico, o formato é o mesmo dos parametros de origem e destino.
  • alternative (opcional): Se definido para true, especifica que o serviço pode fornecer alternativas, mais de uma rota. Demora um pouco mais pra responder... Lembre de tratar a resposta depois para o usuário.
  • avoid (opcional): evitar certos locais pré-definidos. Hoje [tolls (estradas com pontes), highways(estradas).
  • language (opcional): já diz tudo, verifique no site as linguas suportadas.
  • sensor: Obrigatório, [true/false] se você esta utilizando um aparelho com sensor de localização (gps, celular, etc...).
Exemplos
Lembre-se sempre de utilizar o nome da cidade e estado para uma melhor precisão do local.
Resposta
<?xml version="1.0" encoding="UTF-8"?>
<DirectionsResponse>
<status>OK</status>
<route>
<summary>Av. Ipiranga</summary>
<leg>
<step>
<travel_mode>DRIVING</travel_mode>
<start_location>
<lat>-30.0465300</lat>
<lng>-51.1975700</lng>
</start_location>
<end_location>
<lat>-30.0467000</lat>
<lng>-51.2119800</lng>
</end_location>
<polyline>
<points>xmkvDxonwHqOvJm@p@m@fAe@~AUxBOdXX~Bd@bBdBzDxCpFzK|T</points>
<levels>B??@??@????B</levels>
</polyline>
<duration>
<value>195</value>
<text>3 minutos</text>
</duration>
<html_instructions>Siga na direção &lt;b&gt;noroeste&lt;/b&gt; na &lt;b&gt;Av. Ipiranga&lt;/b&gt; em 
direção à &lt;b&gt;R. Vicente da Fontoura&lt;/b&gt; &lt;span style=&quot;white-space: nowrap&quot;&gt;- 
siga &lt;b&gt;1,7&amp;nbsp;km&lt;/b&gt;&lt;/span&gt;</html_instructions>
<distance>
<value>1699</value>
<text>1,7 km</text>
</distance>
</step>
<step>
<travel_mode>DRIVING</travel_mode>
<start_location>
<lat>-30.0467000</lat>
<lng>-51.2119800</lng>
</start_location>
<end_location>
<lat>-30.0432600</lat>
<lng>-51.2146000</lng>
</end_location>
<polyline>
<points>znkvDziqwHoTjO</points>
<levels>BB</levels>
</polyline>
<duration>
<value>57</value>
<text>1 min</text>
</duration>
<html_instructions>Vire à &lt;b&gt;direita&lt;/b&gt; na &lt;b&gt;Av. João Pessoa&lt;/b&gt; &lt;span 
style=&quot;white-space: nowrap&quot;&gt;- siga &lt;b&gt;450&amp;nbsp;m&lt;/b&gt;&lt;/span&gt;</html_instructions>
<distance>
<value>458</value>
<text>0,5 km</text>
</distance>
</step>
<duration>
<value>252</value>
<text>4 minutos</text>
</duration>
<distance>
<value>2157</value>
<text>2,2 km</text>
</distance>
<start_location>
<lat>-30.0465300</lat>
<lng>-51.1975700</lng>
</start_location>
<end_location>
<lat>-30.0432600</lat>
<lng>-51.2146000</lng>
</end_location>
<start_address>Av. Ipiranga, 3200 - Petrópolis, Porto Alegre - RS, 90160-001, Brasil</start_address>
<end_address>Av. João Pessoa - Porto Alegre - RS, Brasil</end_address>
</leg>
<copyrights>Map data ©2010 MapLink</copyrights>
<overview_polyline>
<points>xmkvDxonwHqOvJm@p@m@fAe@~AUxBOdXX~Bd@bBdBzDxCpFzK|ToTjO</points>
<levels>B??@??@????@B</levels>
</overview_polyline>
</route>
</DirectionsResponse>

Elevation API

A API Elevação fornece dados de elevação para todas as localizações na superfície da terra, incluindo locais de profundidade no fundo do oceano (que retornam valores negativos). Nos casos em que o Google não possui medidas de elevação na posição exata que você precisa pedir, o serviço irá interpolar e retornar um valor médio mais próximo.
Como funciona na prática? Você passa uma coordenada ou um conjunto de coordenadas e ela lhe responde a altitude de cada ponto.
Para que serve? Existem alguns exemplos de rotas de bicicleta para evitar locais altos, morros. Mas hoje vou mostrar um pequeno exemplo da sua utilização para autonomia e logística.
Como eu a chamo?
Tipos de Saída (output): até o momento, XML ou JSON
Como está ficando?
XML: http://maps.google.com/maps/api/elevation/xml?parameters
JSON: http://maps.google.com/maps/api/elevation/json?parameters
Parametros:
  • De Posição: locations: {latitude,longitude} Você precisa passar uma latitude e longitude, neste formato: 40.714728,-73.998672 ou mais de um local concatedado por pipes: 40.714728,-73.998672|-34.397,150.644. Ainda existe outro formato, utilizando o Encoded Polyline Algorithm (verificar na documentação da API)
  • ou Posições de Amostras: path: O mesmo que o parametro locations, porém para este caso você precisa definir 2 pontos e utilizar o parametro samples que define a quantidade de pontos entre os locais. Como? É o mesmo que uma rota, você define uma origem e um destino e a quantidade de pontos que deseja no meio da rota. Ele irá lhe retornar os pontos de origem e destino a quantidade de pontos desejada entre os mesmos, informanda o elevação do terreno (pontos em linha reta).
  • sensor: Obrigatório, [true/false] se você está utilizando um aparelho com sensor de localização (gps, celular, etc...).
Exemplos
<ElevationResponse>
<status>OK</status>
<result>
<location>
<lat>39.7391536</lat>
<lng>-104.9847034</lng>
</location>
<elevation>1608.8402100</elevation>
</result>
</ElevationResponse>
{
"status": "OK",
"results": [ {
"location": {
"lat": 39.7391536,
"lng": -104.9847034
},
"elevation": 1608.8402100
}, {
"location": {
"lat": 36.4555560,
"lng": -116.8666670
},
"elevation": -50.7890358
} ]
}

Aperfeiçoando o cálculo de autonomia de veículos

Sem mais delongas, vamos a um exemplo. Porém já aviso de antemão que é apenas uma explanação da idéia, as fontes e maiores detalhamentos sobre a solução não serão disponibilizados, porque o problema não será resolvido na sua totalidade, apenas serão utilizados valores fictícios. Só lembrando, não sou da área de logística, não esperem muito conhecimento disso.
Softwares utilizados:
  • PostgreSQL 8.4 + PostGIS + PlPython
  • Python 2.6
  • Quantum GIS
Breve descrição do problema:
O problema que imaginei para utilização destas duas APIs é o simples cálculo de autonomia que qualquer pessoa nos dias de hoje utiliza, além é claro de empresas de logística. A pergunta básica é quantos km/litro seu carro faz? Na cidade? Na estrada? Que tal ampliarmos esta pergunta e calcularmos também em determinada rota?
O problema apresentado, bem como sua solução, não é novidade. As empresas especializadas em logística muitas vezes possuem bases de dados com informações quanto a elevação do terreno e já se utilizam de alguns cálculos semelhantes para tomada de decisão, porém com a criação destas APIs, nós, meros mortais, temos acesso a estas informações.
Objetivo
  • Capturar para uma determinada rota as elevações do terreno.
  • Identificar possíveis ladeiras/declives onde o consumo de combustível é maior/menor.
  • Realiza o cálculo de consumo de acordo com este trajeto e suas peculiaridades.
Objetivo Principal: abrir horizontes! Este não um artigo passo a passo de como fazer.
Solucionando
Caminho a ser percorrido: 
  • Origem: -30.0330800,-51.2161500, Rua Tomaz Flores, Bom Fim - Porto Alegre - RS
  • Destino: -51.2153200,10.1994400, Rua Santo Antonio, Bom Fim - Porto Alegre - RS
Esta trajeto foi selecionado por cruzar com a AvenidaêIndependencia, ponto mais alto do bairro Bomfim, em Porto Alegre.
Como o que me interessa é apenas a autonomia do meu veículo para este trajeto, sem precisar de um relatório visual, irei criar uma função diretamente no banco de dados para solucionar o problema.
Para isto, vou utilizar o PostrgreSQL com a extensão espacial PostGIS, adicionado da biblioteca PlPython para criação de procedures que acessam as APIs do Google diretamente do banco de dados.
Inicialmente crie a função directions no banco, que irá acessar a API Directions do Google, passando como parâmetros origem e destino, montando a URL que irá retornar o XML. Depois, apenas trato os parâmetros de retorno e chamo uma nova função elevation, buscando a elevação do terreno para cada trecho encontrado.
CREATE OR REPLACE FUNCTION directions(origin character varying, destination character varying)
RETURNS character varying AS
$BODY$

try:
        params = {"origin": origin, "destination" : destination, "sensor" : "false"}
        query = urllib.urlencode(params)
        url = "http://maps.google.com/maps/api/directions/xml?%s" % query
        (...) 
        sql = 'SELECT elevation('%s,%s|%s,%s',%s,%s)' % (start_lat, start_lng, stop_lat, stop_lng, vald, val);
        plpy.execute(sql, 5) 
        return;
        (...)

except:
return 'erro excp';

$BODY$
LANGUAGE 'plpythonu' VOLATILE STRICT
COST 100;
ALTER FUNCTION directions(character varying, character varying) OWNER TO postgres;
A função elevation funciona da mesma maneira, porém é acionada diversas vezes, de acordo com a quantidade de pontos/rotas/steps que o trajeto possui.
CREATE OR REPLACE FUNCTION elevation(string character varying, samples integer, distance integer)
  RETURNS character varying AS
$BODY$

try:
            params = {"path": string, "samples" : samples, "sensor" : "false"}
            query = urllib.urlencode(params)
            url = "http://maps.google.com/maps/api/elevation/xml?%s" % query
            (...)
            while x < total:
                  (...)
                  sql = 'INSERT INTO t_consumo (id,lat,lng,alt,distance) VALUES (%d,'%s','%s','%s','%s')' 
                                          % (id,lat,lng,elevation,distance)
                  plpy.execute(sql, 5)
                  (...)
            return 'ok';
except:
return 'erro excp';

$BODY$
LANGUAGE 'plpythonu' VOLATILE STRICT
COST 100;
ALTER FUNCTION elevation(character varying, integer, integer) OWNER TO postgres;
Como resultado tenho uma tabela chamada t_consumo populada com as coordenadas da minha rota e suas respectivas elevações.
t_consumo (lat, lng, elevação)
  • "-30.0330800";"-51.2161500";"18.5466881"
  • "-30.0329859";"-51.2161432";"18.9679661"
  • "-30.0328917";"-51.2161363";"19.3892441"
  • "-30.0327976";"-51.2161295";"19.8105221"
  • "-30.0327034";"-51.2161227";"20.2318001"
  • "-30.0326093";"-51.2161159";"20.6530762"
  • "-30.0325151";"-51.2161090";"21.0743542"
Obs.: Os pontos geográficos foram dispersos a 10m de um para outro, ou seja, o cálculo de angulação é realizado nesta distância.

Como vou calcular?

Bom, neste momento parto do princípio que meu veículo faz 6 km/litro na cidade, em linha reta, vamos apenas utilizar valores para exemplificar.
Para definir a inclinação do trajeto, neste momento vou pegar a elevação do próximo ponto menos a elevação do ponto que estou, o resultado pode ser positivo (subida) ou negativo (descida) de acordo com o andamento do trajeto.
SELECT  t1.gid AS tab1, 
        t2.gid AS tab2,
        transform(setsrid(makeline(geometry('POINT('||t1.lng||' '||t1.lat||')'),
        geometry('POINT('||t2.lng||' '||t2.lat||')')),4326),4291) AS the_geom, 
        (t2.alt::numeric - t1.alt::numeric) as alt_rest, 
               round(((t2.alt::numeric - t1.alt::numeric)*-2)+6,2) as autonomia from teste t1, teste t2 
WHERE (t1.gid+1) = (t2.gid) 
O resultado desta consulta nada mais é que os campos indicadores de cada ponto, a geometria da linha (ponto que estou e próximo ponto), altitude restante (elevação da linha) e a autonomia do meu veículo para este deslocamento (neste cálculo, parto do principio que meu veículo possui um ganho ou perda de 2 km/litro para angulações superiores e inferiores ao plano. Este é um dado que deve ser enquadrado de acordo com o tipo de veículo, por exemplo, meu carro faz 6km/litro em linha reta, 4km/litro em subidas de ~25° e 8km/litro em descidas com o mesmo declive de ~25°).
Pode-se apenas utilizar o -1 como fator determinante de angulação do declive.
Exemplo:
Subida
  • Altitude que estou: 17.8
  • Próximo altitude: 18.2
  • Total: 18.2 - 17.8 = 0,4 -tan (tangente) = ~21° (Como é positivo, subida), multiplique por -1 para diminuir a autonomia padrão do veículo
Autonomia: 6 + (0,4*-1) = 5,6 km/litro
Descida
  • Altitude que estou: 18.5
  • Próximo altitude: 18.2
  • Total: 18.2 - 18.5 = -0,3 -tan (tangente) = ~-16° (Como é negativo, descrida), multiplique por -1 para diminuir a autonomia padrão do veículo
Autonomia: 6 + (-0,3*-1) = 6,3 km/litro

Resultado do trajeto no Google Maps e Quantum GIS:

Fonte: Google Maps - Camada Earth
Na imagem abaixo, a coluna à esquerda, em azul, são os valores da elevação do terreno para cada ponto dentro da linha. As informações a direita são quanto ao resultado da autonomia do meu veículo neste mesmo ponto.
A legenda abaixo representa os intervalos de valores selecionados e as cores designadas.
  1. Elevação do Terreno (em metros)
  2. Consumo Km/Litro no trecho
Legenda quantumgis
O ponto branco representa o local de menor elevação, enquanto o ponto preto representa o local de maior elevação, no nosso trajeto a Av Independencia. A linha representa o consumo litros/km, verde é o menor consumo e vermelho o maior.
Podemos ver que a região com maior declive é a que antecede a Av Independencia, ainda na Rua Thomaz Flores, onde a autonomia do veículo se encontra no seu menor valor.
Concluimos com isso, obviamente, que não é a elevação do ponto que reduz a autonomia do veículo, e sim a inclinação da via naquele determinado trajeto.
Resultado
Consumo geral do meu veículo: 6 km/litro
Para este trajeto:
SELECT avg(consumo) AS media FROM t_consumo
Igual: 6.16 km/litro

Anexos
Imagem: Consumo na rota, utilize para comparar com as imagens abaixo.
foto1.png
Foto1: Após Interseção R. José Otão, inicio do declive.

Foto2: Declive superior da Rua Tomaz Flores, maior consumo do trajeto.
Foto3: Av Independencia, ponto de maior elevação porém plano, consumo moderado/médio.
Foto4: Declive Rua Santo Antonio, aumento de autonomia.
Feito, abraço.

Google Static Maps API


Google Static Maps API

Olá a todos.
Como prometi, hoje vou escrever sobre a Google Static Maps API (API estática do Google Maps).
Esta API do Google Maps permite incorporar uma imagem do Google Maps à sua página web, sem a necessidade de JavaScript ou qualquer outra página dinâmica.
Resumindo, apenas com base em parâmetros de URL através de uma solicitação HTTP para o Google e ele lhe retorna uma imagem para a sua página web, utilizando nada mais nada menos que uma tag <img>.
Confesso que este recurso é muito útil no meu dia a dia para algumas funções tais como:
  • aplicações em celulares que não suportam javascript;
  • páginas que não necessitam um mapa sofisticado, apenas uma imagem próxima ao local chave;
  • confirmação visual de um endereço digitado em um formulário;
  • entre muitas outras;
Limites de Uso
Segundo a página do Google Static Maps API, existe um limite de uso diário por leitores (!!e não por chaves API maps!!) de 1.000 solicitações de imagens diferentes. O que acredito ser muito difícil ocorrer, porém, essa limitação pode ser alterada pelo Google sem aviso prévio.
Parâmetros
Para utilizar a API estática do Google Maps basta acessar a seguinte URL:
http://maps.google.com/staticmap?parametros
Segue a lista de parâmetros disponíveis e em destaque sua obrigatoriedade:
  • center (obrigatório caso não haja marcadores (markers: identificador de um local/endereço)) : este parâmetro define o ponto central do seu mapa, levando em consideração o zoom em que o mesmo se encontra. Para se obter o centro do mapa você deve passar os parâmetros latitude e longitude (os quais já vimos em outros artigos) separados por vírgula (ex.: -30.245422,-51.542684).
  • zoom (obrigatório caso não haja marcadores) : determina o zoom de aproximação do mapa (varia de 1 a 18, no Brasil na maioria das capitais temos no máximo 17 níveis).
  • size (obrigatório) : define o tamanho da imagem (largura e altura) no seguinte formato: LARGURA x ALTURA. (ex.: 300x250).
  • maptype : (opcional) : representa o tipo de mapa a ser criado. Hoje existem sete tipos disponíveis: roadmap : (padrão) tipo de mapa padrão utilizado caso o parâmetro maptype não for definido, representa o mesmo padrão utilizado no Google Maps; mobile : apropriado para sistemas para celulares, onde o nível de detalhes é menor e os textos são maiores para uma melhor visualização das informações em resoluções menores. satellite: imagens de satélite; terrain: imagens de terreno; hybrid: junção dos dados vetoriais e imagem de satélite;mapmaker-roadmap: especifica imagens do Google Map Maker (não disponível no Brasil); mapmaker-hybrid: mostra uma camada transparente das principais ruas unida com imagens de satélite utilizando a base do Map Maker (o map Maker esta disponível em apenas alguns países).
Exemplos
maptype: padrão
maptype: mobile
maptype: satellite
maptype: hybrid
maptype: terrain
  • markers : (opcional) : define um ou mais marcadores no mapa. A representação dos marcadores é feita também com os parâmetros de latitude e longitude, separados pelo caracter barra ( | ) caso existam mais de um. Se você utilizar um ou mais marcadores em seu mapa não é necessária a utilização dos parâmetros zoom e center, pois as mesmas serão automaticamente calculadas de acordo com seus marcadores.
  • key : (obrigatório) : como já vimos em outros exemplos de APIs do Google, identifica a chave de API do Maps em relação ao domínio em que ocorre a solicitação de URL.

Formatos de Imagens

  • gif (default)
jpg
  • jpg-baseline
png8
png32

Markers

O parâmetro markers define um ou mais parâmetros para a representação no mapa.
Exemplo:
markers=marker1|marker2|marker3|... etc.
Utilize o caracter barra/pipe (|) para concatenar os markers, ou %7C no caso de URL ASCI.
Uma declaração de marcador de exemplo é do formato {latitude},{longitude},{color}{alpha-character}. Na seqüência de caracteres os valores da cor e do caractere alfa especialmente não são separados por uma vírgula.
Exemplo:
http://maps.google.com/staticmap?size=250x250&markers=-30.0277041,-51.2003346,bluea|-30.0217041,-51.2033346,greenv&key=MAPS_API_KEY
markers=-30.0277041,-51.2003346,bluea|-30.0217041,-51.2033346,greenv
O primeiro marker será azul, com o caracter A e o segundo marker verde com o caracter V.

Caminhos (Paths)

O parâmetro path especifica um ou mais caminhos sobrepostos no mapa.
Atualmente o tamanho máximo são 100 pontos.
Este parâmetro utiliza as seguintes atribuições:
path= pathColorType : pathColorValue ,weight: pathWeight | pathPoint1 | pathPoint2 | pathPoint3 |... etc 
Parâmetros:
pathColorType:
  • rgb: especifica um padrão RGB utilizando um esquema de cores de 24 bits hexadecimal. O padrão de opacidade para rgb é de 50%. (exemplo de pathColorValue: 0xffffff)
  • rgba: especifica um padrão RGB de 32 bits hexadecimal. Usando rgba lhe permite variar a transparência dos caminhos que você criar.
weight: especifica a espessura de um determinado caminho.
Alguns exemplos:
linha fina azul, 50% de opacidade: path=rgb:0x0000ff,weight:1
linha vermelha sólida: path=rgba:0xff0000ff,weight:5
linha espessa preta sólida: path=rgba:0xffffffff,weight:10
Para traçar o caminho, vamos passar um ou mais pontos para representarmos as linhas.
Exemplo
http://maps.google.com/staticmap?size=250x250&key=MAPS_API_KEY&
path=rgba:0xff0000ff,weight:5|-30.0286526,-51.2053849|-30.0285127,-51.2006248|-30.0284554,-51.1992281
Não se esqueça dos meu artigos anteriores de como obter a latitude e longitude dos pontos: Utilize o link: http://maps.google.com/maps/geo?q=LOGRADOURO_NUM_CIDADE_UF&output=xml&key=SUA_CHAVE
Agora uma dica melhor ainda
Utilize o Google Static Map Wizard para a geração de seus mapas estáticos, é realmente simples.
Até a próxima.