{"id":8246,"date":"2019-05-08T09:52:23","date_gmt":"2019-05-08T12:52:23","guid":{"rendered":"http:\/\/www.fernandoquadro.com.br\/html\/?p=8246"},"modified":"2019-05-08T09:52:23","modified_gmt":"2019-05-08T12:52:23","slug":"representando-linhas-de-onibus-no-leaflet","status":"publish","type":"post","link":"https:\/\/www.fernandoquadro.com.br\/html\/2019\/05\/08\/representando-linhas-de-onibus-no-leaflet\/","title":{"rendered":"Representando linhas de \u00f4nibus no Leaflet"},"content":{"rendered":"<p>Hoje vamos falar sobre o plugin <a href=\"https:\/\/github.com\/bbecquet\/Leaflet.PolylineOffset\" rel=\"noopener\" target=\"_blank\">Polylines Offset<\/a> que adiciona a habilidade de voc\u00ea desenhar uma linha com um offset (deslocamento) de pixel relativo, sem modificar seus LatLongs reais. O valor do desse offset (deslocamento) pode ser negativo ou positivo, para o deslocamento do lado esquerdo ou do lado direito, e permanece constante nos n\u00edveis de zoom.<\/p>\n<p>A ideia do plugin \u00e9 desenhar uma linha paralela a uma existente, a uma dist\u00e2ncia fixa. N\u00e3o \u00e9 uma simples tradu\u00e7\u00e3o (x, y) de toda a forma, pois n\u00e3o deve se sobrepor. Ele pode ser usado para enfatizar visualmente diferentes propriedades do mesmo recurso linear ou obter um estilo composto complexo.<\/p>\n<p><strong>1. Instalando o plugin<\/strong><\/p>\n<p>Caso voc\u00ea esteja utilizando o Node.js voc\u00ea pode instalar o plugin da seguinte forma:<\/p>\n<pre>\r\nnpm install leaflet-polylineoffset\r\n<\/pre>\n<p>Caso n\u00e3o esteja utilizando o Node.js, pode referenciar diretamente a URL no seu HTML apontando para o GitHub:<\/p>\n<pre>\r\n&lt;script src=\"http:\/\/bbecquet.github.io\/Leaflet.PolylineOffset\/leaflet.polylineoffset.js\"&gt;&lt;\/script&gt;\r\n<\/pre>\n<p><strong>2. Dados<\/strong><\/p>\n<p>Para representar as linhas de \u00f4nibus, iremos utilizar um arquivo no formato GeoJSON, conforme demonstrado abaixo:<\/p>\n<pre>\r\n          \"features\": [\r\n            {\r\n              \"type\": \"Feature\",\r\n              \"properties\": {\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\"features\":&nbsp;[\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\"type\":&nbsp;\"Feature\",\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\"properties\":&nbsp;{\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>\"lines\":&nbsp;[0,&nbsp;1]<\/strong>\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\"geometry\":&nbsp;{\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\"type\":&nbsp;\"LineString\",\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\"coordinates\":&nbsp;[\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2.357919216156006,\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48.87621773324153\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;],\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2.357339859008789,\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48.874834693731664\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;],\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2.362983226776123,\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48.86855408432749\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;],\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2.362382411956787,\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48.86796126699168\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;],\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2.3633265495300293,\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48.86735432768131\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}\r\n<\/pre>\n<p>\u00c9 importante ressaltar a propriedade <strong>lines<\/strong>, indica a quais linhas de \u00f4nibus o trecho (Feature) representa, podendo representar uma ou mais linhas de \u00f4nibus. Ou seja, nesse GeoJSON temos as informa\u00e7\u00f5es geogr\u00e1ficas de todas as nossas linhas de \u00f4nibus, que no nosso caso s\u00e3o 4.  <\/p>\n<p><strong>3. Adicionando os pontos de parada<\/strong><\/p>\n<p>Como em toda rede de \u00f4nibus, precisamos ter alguns pontos de parada, para o nosso mapa iremos defini-los dinamicamente, como demonstra o c\u00f3digo abaixo:<\/p>\n<pre>\r\n        \/\/ Adicionando os pontos de parada\r\n        var ends = [];\r\n        function addStop(ll) {\r\n          for(var i=0, found=false; i&lt;ends.length && !found; i++) {\r\n            found = (ends[i].lat == ll.lat && ends[i].lng == ll.lng);\t\t\t\r\n          }\r\n          if(!found) {\r\n            ends.push(ll);\r\n          }\r\n        }\r\n<\/pre>\n<p><strong>4. Gerando os segmentos das linhas de \u00f4nibus<\/strong><\/p>\n<p>Agora que nossos pontos de parada j\u00e1 est\u00e3o criados, n\u00f3s vamos pegar os dados vindos do GeoJSON e organizar as linhas por segmentos, definindo suas propriedades e tamb\u00e9m criar o offset que visualmente n\u00e3o deixa de ser um buffer ao redor das linhas. Para isso, faremos da seguinte maneira:<\/p>\n<pre>\r\n        \/\/ Gera os segmentos de linha\r\n\t\tvar lineSegment, linesOnSegment, segmentCoords, segmentWidth;\r\n        geoJson.features.forEach(function(lineSegment) {\r\n          segmentCoords = L.GeoJSON.coordsToLatLngs(lineSegment.geometry.coordinates, 0);\r\n\r\n          linesOnSegment = lineSegment.properties.lines;\r\n          segmentWidth = linesOnSegment.length * (lineWeight + 1);\r\n\t\t  \r\n          \r\n\t\t  \/\/ Gera o linha ao redor do buffer\r\n\t\t  L.polyline(segmentCoords, {\r\n            color: '#000',\r\n            weight: segmentWidth + 5,\r\n            opacity: 1\r\n          }).addTo(outlines);\r\n\r\n         \r\n\t\t \/\/ Gera Buffer ao redor das linhas\r\n\t\t L.polyline(segmentCoords, {\r\n            color: '#fff',\r\n            weight: segmentWidth + 3,\r\n            opacity: 1\r\n          }).addTo(lineBg);\r\n\r\n          \/\/ Organiza as linhas por segmento, definindo cor, largura, opacidade e offset\r\n\t\t  for(var j=0;j&lt;linesOnSegment.length;j++) {\r\n            alert(lineColors[linesOnSegment[j]]);\r\n\t\t\tL.polyline(segmentCoords, {\r\n              color: lineColors[linesOnSegment[j]],\r\n              weight: lineWeight,\r\n              opacity: 1,\r\n              offset: j * (lineWeight + 1) - (segmentWidth \/ 2) + ((lineWeight + 1) \/ 2)\r\n            }).addTo(busLines);\r\n          }\r\n\r\n          addStop(segmentCoords[0]);\r\n          addStop(segmentCoords[segmentCoords.length - 1]);\r\n        });\r\n<\/pre>\n<p>Por \u00faltimo, vamos adicionar os pontos de \u00f4nibus e as camadas ao mapa:<\/p>\n<pre>\r\n\/\/ Adicionando os pontos de \u00f4nibus\r\n        ends.forEach(function(endCoords) {\r\n          L.circleMarker(endCoords, {\r\n            color: '#000',\r\n            fillColor: '#ccc',\r\n            fillOpacity: 1,\r\n            radius: 10,\r\n            weight: 4,\r\n            opacity: 1\r\n          }).addTo(busStops);\r\n        });\r\n\r\n\t\/\/ Adiciona as linhas ao mapa\r\n        outlines.addTo(map);\r\n        lineBg.addTo(map);\r\n        busLines.addTo(map);\r\n        busStops.addTo(map);\r\n<\/pre>\n<p><strong>5. Resultado<\/strong><\/p>\n<p>Ap\u00f3s executar os passos acima, o resultado obtido deve ser o seguinte:<\/p>\n<p><center><br \/>\n<img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.fernandoquadro.com.br\/html\/wp-content\/uploads\/2019\/05\/bus_line.png\" alt=\"\" width=\"1039\" height=\"757\" class=\"aligncenter size-full wp-image-8255\" srcset=\"https:\/\/www.fernandoquadro.com.br\/html\/wp-content\/uploads\/2019\/05\/bus_line.png 1039w, https:\/\/www.fernandoquadro.com.br\/html\/wp-content\/uploads\/2019\/05\/bus_line-300x219.png 300w, https:\/\/www.fernandoquadro.com.br\/html\/wp-content\/uploads\/2019\/05\/bus_line-768x560.png 768w, https:\/\/www.fernandoquadro.com.br\/html\/wp-content\/uploads\/2019\/05\/bus_line-1024x746.png 1024w, https:\/\/www.fernandoquadro.com.br\/html\/wp-content\/uploads\/2019\/05\/bus_line-945x689.png 945w, https:\/\/www.fernandoquadro.com.br\/html\/wp-content\/uploads\/2019\/05\/bus_line-600x437.png 600w\" sizes=\"auto, (max-width: 1039px) 100vw, 1039px\" \/><br \/>\n<\/center><\/p>\n<p><strong>6. O C\u00f3digo<\/strong><\/p>\n<p>Para baixar o c\u00f3digo completo, <a href=\"http:\/\/www.fernandoquadro.com.br\/html\/wp-content\/uploads\/2019\/05\/leaflet_busline.zip\" rel=\"noopener\" target=\"_blank\">clique aqui<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hoje vamos falar sobre o plugin Polylines Offset que adiciona a habilidade de voc\u00ea desenhar uma linha com um offset (deslocamento) de pixel relativo, sem modificar seus LatLongs reais. O valor do desse offset (deslocamento) pode ser negativo ou positivo,&#8230; <a class=\"more-link\" href=\"https:\/\/www.fernandoquadro.com.br\/html\/2019\/05\/08\/representando-linhas-de-onibus-no-leaflet\/\">Continue Reading &rarr;<\/a><\/p>\n","protected":false},"author":275,"featured_media":8247,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[24],"tags":[140,293],"class_list":["post-8246","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-gis","tag-javascript","tag-leaflet"],"_links":{"self":[{"href":"https:\/\/www.fernandoquadro.com.br\/html\/wp-json\/wp\/v2\/posts\/8246","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.fernandoquadro.com.br\/html\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.fernandoquadro.com.br\/html\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.fernandoquadro.com.br\/html\/wp-json\/wp\/v2\/users\/275"}],"replies":[{"embeddable":true,"href":"https:\/\/www.fernandoquadro.com.br\/html\/wp-json\/wp\/v2\/comments?post=8246"}],"version-history":[{"count":12,"href":"https:\/\/www.fernandoquadro.com.br\/html\/wp-json\/wp\/v2\/posts\/8246\/revisions"}],"predecessor-version":[{"id":8261,"href":"https:\/\/www.fernandoquadro.com.br\/html\/wp-json\/wp\/v2\/posts\/8246\/revisions\/8261"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.fernandoquadro.com.br\/html\/wp-json\/wp\/v2\/media\/8247"}],"wp:attachment":[{"href":"https:\/\/www.fernandoquadro.com.br\/html\/wp-json\/wp\/v2\/media?parent=8246"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.fernandoquadro.com.br\/html\/wp-json\/wp\/v2\/categories?post=8246"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.fernandoquadro.com.br\/html\/wp-json\/wp\/v2\/tags?post=8246"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}