Hoje vamos falar sobre o plugin Polylines Offset que adiciona a habilidade de você 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íveis de zoom.

A ideia do plugin é desenhar uma linha paralela a uma existente, a uma distância fixa. Não é uma simples tradução (x, y) de toda a forma, pois não deve se sobrepor. Ele pode ser usado para enfatizar visualmente diferentes propriedades do mesmo recurso linear ou obter um estilo composto complexo.

1. Instalando o plugin

Caso você esteja utilizando o Node.js você pode instalar o plugin da seguinte forma:

npm install leaflet-polylineoffset

Caso não esteja utilizando o Node.js, pode referenciar diretamente a URL no seu HTML apontando para o GitHub:

<script src="http://bbecquet.github.io/Leaflet.PolylineOffset/leaflet.polylineoffset.js"></script>

2. Dados

Para representar as linhas de ônibus, iremos utilizar um arquivo no formato GeoJSON, conforme demonstrado abaixo:

          "features": [
            {
              "type": "Feature",
              "properties": {
          "features": [
            {
              "type": "Feature",
              "properties": {
                "lines": [0, 1]
              },
              "geometry": {
                "type": "LineString",
                "coordinates": [
                  [
                    2.357919216156006,
                    48.87621773324153
                  ],
                  [
                    2.357339859008789,
                    48.874834693731664
                  ],
                  [
                    2.362983226776123,
                    48.86855408432749
                  ],
                  [
                    2.362382411956787,
                    48.86796126699168
                  ],
                  [
                    2.3633265495300293,
                    48.86735432768131
                  ]
                ]
              }
            }

É importante ressaltar a propriedade lines, indica a quais linhas de ônibus o trecho (Feature) representa, podendo representar uma ou mais linhas de ônibus. Ou seja, nesse GeoJSON temos as informações geográficas de todas as nossas linhas de ônibus, que no nosso caso são 4.

3. Adicionando os pontos de parada

Como em toda rede de ônibus, precisamos ter alguns pontos de parada, para o nosso mapa iremos defini-los dinamicamente, como demonstra o código abaixo:

        // Adicionando os pontos de parada
        var ends = [];
        function addStop(ll) {
          for(var i=0, found=false; i<ends.length && !found; i++) {
            found = (ends[i].lat == ll.lat && ends[i].lng == ll.lng);			
          }
          if(!found) {
            ends.push(ll);
          }
        }

4. Gerando os segmentos das linhas de ônibus

Agora que nossos pontos de parada já estão criados, nós vamos pegar os dados vindos do GeoJSON e organizar as linhas por segmentos, definindo suas propriedades e também criar o offset que visualmente não deixa de ser um buffer ao redor das linhas. Para isso, faremos da seguinte maneira:

        // Gera os segmentos de linha
		var lineSegment, linesOnSegment, segmentCoords, segmentWidth;
        geoJson.features.forEach(function(lineSegment) {
          segmentCoords = L.GeoJSON.coordsToLatLngs(lineSegment.geometry.coordinates, 0);

          linesOnSegment = lineSegment.properties.lines;
          segmentWidth = linesOnSegment.length * (lineWeight + 1);
		  
          
		  // Gera o linha ao redor do buffer
		  L.polyline(segmentCoords, {
            color: '#000',
            weight: segmentWidth + 5,
            opacity: 1
          }).addTo(outlines);

         
		 // Gera Buffer ao redor das linhas
		 L.polyline(segmentCoords, {
            color: '#fff',
            weight: segmentWidth + 3,
            opacity: 1
          }).addTo(lineBg);

          // Organiza as linhas por segmento, definindo cor, largura, opacidade e offset
		  for(var j=0;j<linesOnSegment.length;j++) {
            alert(lineColors[linesOnSegment[j]]);
			L.polyline(segmentCoords, {
              color: lineColors[linesOnSegment[j]],
              weight: lineWeight,
              opacity: 1,
              offset: j * (lineWeight + 1) - (segmentWidth / 2) + ((lineWeight + 1) / 2)
            }).addTo(busLines);
          }

          addStop(segmentCoords[0]);
          addStop(segmentCoords[segmentCoords.length - 1]);
        });

Por último, vamos adicionar os pontos de ônibus e as camadas ao mapa:

// Adicionando os pontos de ônibus
        ends.forEach(function(endCoords) {
          L.circleMarker(endCoords, {
            color: '#000',
            fillColor: '#ccc',
            fillOpacity: 1,
            radius: 10,
            weight: 4,
            opacity: 1
          }).addTo(busStops);
        });

	// Adiciona as linhas ao mapa
        outlines.addTo(map);
        lineBg.addTo(map);
        busLines.addTo(map);
        busStops.addTo(map);

5. Resultado

Após executar os passos acima, o resultado obtido deve ser o seguinte:



6. O Código

Para baixar o código completo, clique aqui.