Situation:
I use Mapbox to create a map with three markers + a linestring connecting them. Following this exmaple I created a button that zooms the camera to the bounds of the linestring.
This works as intended.
Whenever the function is called (on click and on first map-load), the camera zooms to the linestring bounds correctly.
Problem/Goal:
I would like to only display the button whenever:
the user has changed position of the campera, after linestring was brought into view
the user has changed the zoom, after linestring was brought into view
This can be simply done by adding / removing a .is-visible class.
However I somehow cant figure out how to listen to these two possible user interactions after linestring was brought into view.
I've tried some approaches that seemed overly complex and did not work. I have the feeling that the answer is quite simple, only I'm not seeing it.
Any help appreciated!
Code:
<script src='https://unpkg.com/mapbox#1.0.0-beta9/dist/mapbox-sdk.min.js'></script>
<script src='https://api.mapbox.com/mapbox-gl-js/v1.2.0/mapbox-gl.js'></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v1.2.0/mapbox-gl.css' rel='stylesheet' />
<div id='map' class="traveljournal__map"></div>
<div class='traveljournal__map-actions'>
<div id='zoomto' class="traveljournal__map-action traveljournal__map-action--zoomto"></div>
</div>
<script>
mapboxgl.accessToken = 'TOKENHERE';
let client = new MapboxClient(mapboxgl.accessToken);
// DEFINE MAP
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/nilsdannemann/cjz2kdev503jo1dnsr23qoca8',
center: [174.724779, -41.288030], // Coordinates of newest Moment
zoom: 9
});
// DEFINE MOMENTS
var moments = [{
id: "1",
properties: {
title: "moment title",
content: "moment content",
mood: "happy",
date: "01. Aug 2019",
weather: "sunny",
iconSize: [60, 60],
location: [174.800314, -41.317955],
},
camera: {
center: [174.800314, -41.317955],
zoom: 13,
bearing: 20, // Add 20 for every location
pitch: 40
}
}, {
id: "2",
properties: {
title: "moment title",
content: "moment content",
mood: "happy",
date: "01. Aug 2019",
weather: "sunny",
iconSize: [60, 60],
location: [174.773008, -41.282235],
},
camera: {
center: [174.773008, -41.282235],
zoom: 13,
bearing: 40, // Add 20 for every location
pitch: 40
}
}, {
id: "3",
properties: {
title: "moment title",
content: "moment content",
mood: "happy",
date: "01. Aug 2019",
weather: "sunny",
iconSize: [60, 60],
location: [174.724779, -41.288030],
},
camera: {
center: [174.724779, -41.288030],
zoom: 13,
bearing: 60, // Add 20 for every location
pitch: 40
}
}];
// ADD MARKERS
moments.forEach(function(marker, index) {
// Create a DOM element for Marker
var el = document.createElement('div');
el.className = 'traveljournal__map-marker';
el.style.width = marker.properties.iconSize[0] + 'px';
el.style.height = marker.properties.iconSize[1] + 'px';
el.addEventListener('click', function() {
//Move Campera to Marker
map.flyTo(moments[index].camera);
});
// Add Marker to Map
new mapboxgl.Marker(el)
.setLngLat(marker.properties.location)
.addTo(map);
});
// ADD LINE BETWEEN MARKERS
var linestring = [];
moments.forEach(function(item) {
linestring.push(item.properties.location);
});
// ADD ZOOM TO LINESTRING FUNCTION
function zoomToLineString() {
var bounds = linestring.reduce(function(bounds, coord) {
return bounds.extend(coord);
}, new mapboxgl.LngLatBounds(linestring[0], linestring[0]));
map.fitBounds(bounds, {
padding: {top: 30, right: 0, bottom: 75, left: 0},
bearing: 0,
pitch: 0
});
};
// ADD ZOOM TO LINESTRING BUTTON EVENT
document.getElementById('zoomto').addEventListener('click', function() {
zoomToLineString();
});
// LOAD MAP
map.on('load', function() {
// ADD LINE TO MAP
map.addLayer({
"id": "route",
"type": "line",
"source": {
"type": "geojson",
"data": {
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": linestring
}
}
},
"layout": {
"line-join": "round",
"line-cap": "round"
},
"paint": {
"line-color": "#2BABEE",
"line-width": 2
}
});
// ZOOM TO LINESTRING ON MAP LOAD
zoomToLineString();
});
</script>
You can listen to the zoomend and moveend events to perform your bounds check, and update classes as required. You can use queryRenderedFeatures to see if the line is (partially) somewhere in the viewport.
function checkLine() {
if (map.queryRenderedFeatures({layers: 'route'}).length) {
// route is within view, do what you want
}
}
map.on('zoomend', checkLine);
map.on('moveend', checkLine);
Related
I have map where I can draw lines, but after drawing the lines I can drag the line and circles to where I want it and I dont want that. I needs to be blocked so it stays at that place. I tried doing something like draggeble:false or trying to change stuff in the style but I couldn't figure it out
this is how my map.js looks like:
mapboxgl.accessToken = 'myToken';
const map = new mapboxgl.Map({
container: 'map', // container ID
style: 'mapbox://styles/mapbox/streets-v9', // style URL
center: [-96, 37.8], // starting position
zoom: 3, // starting zoom
});
map.addControl(new mapboxgl.FullscreenControl());
const geolocate = new mapboxgl.GeolocateControl()
map.addControl(geolocate)
map.on('load', function()
{
geolocate.trigger();
});
var draw = new MapboxDraw({
displayControlsDefault: false,
controls: {
line_string: true,
trash: true
},
styles: [
// ACTIVE (being drawn)
// line stroke
{
"id": "gl-draw-line",
"type": "line",
"filter": ["all", ["==", "$type", "LineString"], ["!=", "mode", "static"]],
"layout": {
"line-cap": "round",
"line-join": "round"
},
"paint": {
"line-color": "#3b9ddd",
"line-dasharray": [0.2, 2],
"line-width": 4,
"line-opacity": 0.7
}
},
{
"id": "gl-draw-polygon-and-line-vertex-halo-active",
"type": "circle",
"filter": ["all", ["==", "meta", "vertex"], ["==", "$type", "Point"], ["!=", "mode", "static"]],
"paint": {
"circle-radius": 10,
"circle-color": "#FFF"
}
},
// vertex points
{
"id": "gl-draw-polygon-and-line-vertex-active",
"type": "circle",
"filter": ["all", ["==", "meta", "vertex"], ["==", "$type", "Point"], ["!=", "mode", "static"]],
"paint": {
"circle-radius": 6,
"circle-color": "#3b9ddd",
},
}
]
});
// add the draw tool to the map
map.addControl(draw);
// add create, update, or delete actions
map.on('draw.create', updateRoute);
map.on('draw.update', updateRoute);
map.on('draw.delete', removeRoute);
// use the coordinates you just drew to make your directions request
function updateRoute() {
removeRoute(); // overwrite any existing layers
var data = draw.getAll();
var lastFeature = data.features.length - 1;
var coords = data.features[lastFeature].geometry.coordinates;
var newCoords = coords.join(';')
console.log(newCoords);
for (var i = 0; i < coords.length; i++) {
var coordsPoints = coords[i];
// Create a default Marker and add it to the map.
const marker1 = new mapboxgl.Marker()
.setLngLat([coordsPoints[0],coordsPoints[1]])
.addTo(map);
}
//console.log(newCoords);
}
// remove the layer if it exists
function removeRoute () {
if (map.getSource('route')) {
map.removeLayer('route');
map.removeSource('route');
document.getElementById('calculated-line').innerHTML = '';
} else {
return;
}
}
Image of my drawen lines
I am using Leaflet javascript library to show some earthquake information. You are able to see few red circles on map. When you click that red circle it will display few details in popup.
I want to show the popup when you click the link from outside of map. This (answer) is useful. But, I want to achieve it without ID? I mean if i have more than 30 markers on map and also I don't have any ID, just class names in my links. How do I trigger popup when I click a link from outside of map?
This (answer) is good. In this answer they mentioned marker 1, marker 2 and marker 3. But, In my case I don't know how many markers will show. Sometimes 5, 50, 100 or may be more than 150. Thats why I have asked this question.
<html>
<head>
<meta charset="utf-8" />
<title>Leaflet</title>
<link
rel="stylesheet"
href="https://unpkg.com/leaflet#1.5.1/dist/leaflet.css"
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
crossorigin=""
/>
<script
src="https://unpkg.com/leaflet#1.5.1/dist/leaflet.js"
integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og=="
crossorigin=""
></script>
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
</head>
<body>
<div id="map" style="width: 400px; height: 400px;"></div>
<script>
var object = {
type: "FeatureCollection",
metadata: {
generated: 1564051101000,
url:
"https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2019-07-20&endtime=2019-07-21&minmagnitude=5",
title: "USGS Earthquakes",
status: 200,
api: "1.8.1",
count: 4
},
features: [
{
type: "Feature",
properties: {
mag: 5.2000000000000002,
place: "79km ENE of L'Esperance Rock, New Zealand",
time: 1563662132538,
updated: 1563663302040,
tz: -720,
url: "https://earthquake.usgs.gov/earthquakes/eventpage/us70004pu1",
detail: "https://earthquake.usgs.gov/fdsnws/event/1/query?eventid=us70004pu1&format=geojson",
felt: null,
cdi: null,
mmi: null,
alert: null,
status: "reviewed",
tsunami: 0,
sig: 416,
net: "us",
code: "70004pu1",
ids: ",us70004pu1,",
sources: ",us,",
types: ",geoserve,origin,phase-data,",
nst: null,
dmin: 1.9299999999999999,
rms: 1.28,
gap: 70,
magType: "mww",
type: "earthquake",
title: "M 5.2 - 79km ENE of L'Esperance Rock, New Zealand"
},
geometry: { type: "Point", coordinates: [-178.1173, -31.174800000000001, 35] },
id: "us70004pu1"
},
{
type: "Feature",
properties: {
mag: 5.5999999999999996,
place: "23km NNW of Kandrian, Papua New Guinea",
time: 1563655424914,
updated: 1563741959328,
tz: 600,
url: "https://earthquake.usgs.gov/earthquakes/eventpage/us70004psn",
detail: "https://earthquake.usgs.gov/fdsnws/event/1/query?eventid=us70004psn&format=geojson",
felt: 1,
cdi: 4.2999999999999998,
mmi: 4.4779999999999998,
alert: "green",
status: "reviewed",
tsunami: 1,
sig: 483,
net: "us",
code: "70004psn",
ids: ",us70004psn,",
sources: ",us,",
types: ",dyfi,geoserve,losspager,moment-tensor,origin,phase-data,shakemap,",
nst: null,
dmin: 3.2029999999999998,
rms: 0.89000000000000001,
gap: 28,
magType: "mww",
type: "earthquake",
title: "M 5.6 - 23km NNW of Kandrian, Papua New Guinea"
},
geometry: { type: "Point", coordinates: [149.5069, -6.0086000000000004, 59.789999999999999] },
id: "us70004psn"
},
{
type: "Feature",
properties: {
mag: 5.0999999999999996,
place: "Easter Island region",
time: 1563647034336,
updated: 1563892918040,
tz: -420,
url: "https://earthquake.usgs.gov/earthquakes/eventpage/us70004pra",
detail: "https://earthquake.usgs.gov/fdsnws/event/1/query?eventid=us70004pra&format=geojson",
felt: null,
cdi: null,
mmi: null,
alert: null,
status: "reviewed",
tsunami: 0,
sig: 400,
net: "us",
code: "70004pra",
ids: ",us70004pra,",
sources: ",us,",
types: ",geoserve,origin,phase-data,",
nst: null,
dmin: 2.7559999999999998,
rms: 0.71999999999999997,
gap: 118,
magType: "mb",
type: "earthquake",
title: "M 5.1 - Easter Island region"
},
geometry: { type: "Point", coordinates: [-111.38379999999999, -29.3232, 10] },
id: "us70004pra"
},
{
type: "Feature",
properties: {
mag: 5.0999999999999996,
place: "136km ESE of Pangai, Tonga",
time: 1563635789233,
updated: 1563636880040,
tz: -720,
url: "https://earthquake.usgs.gov/earthquakes/eventpage/us70004pp5",
detail: "https://earthquake.usgs.gov/fdsnws/event/1/query?eventid=us70004pp5&format=geojson",
felt: null,
cdi: null,
mmi: null,
alert: null,
status: "reviewed",
tsunami: 0,
sig: 400,
net: "us",
code: "70004pp5",
ids: ",us70004pp5,",
sources: ",us,",
types: ",geoserve,origin,phase-data,",
nst: null,
dmin: 3.2749999999999999,
rms: 1.3100000000000001,
gap: 116,
magType: "mww",
type: "earthquake",
title: "M 5.1 - 136km ESE of Pangai, Tonga"
},
geometry: { type: "Point", coordinates: [-173.15700000000001, -20.294899999999998, 10] },
id: "us70004pp5"
}
],
bbox: [-178.1173, -31.1748, 10, 149.5069, -6.0086, 59.79]
};
var i = 0;
document.writeln("<div>");
for (i = 0; i < object.features.length; i++) {
var timestamp = object.features[i].properties.time / 1000;
date = new Date(timestamp * 1000);
dateString = date.toUTCString();
var today = date;
today.setHours(today.getHours() + 4);
// maps
document.writeln("<div class='list'>");
document.writeln("<div>" + object.features[i].properties.mag + " Mag</div>");
document.writeln("</div>");
}
document.writeln("</div>");
var map = L.map("map").setView([-31.174800000000001, -178.1173], 2);
// load a tile layer
L.tileLayer(
"https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}",
{
attribution:
"Tiles © Esri — Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012",
maxZoom: 18
}
).addTo(map);
// load GeoJSON from an external file
$.getJSON(
"https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2019-07-20&endtime=2019-07-21&minmagnitude=5",
function(data) {
var geojsonMarkerOptions = {
opacity: 0.8,
fillOpacity: 0.6
};
// color indication by magnitude
geoLayer = L.geoJson(data, {
// popup div content
onEachFeature: function(feature, layer) {
// variable1 = L.marker([-31.174800000000001,-178.1173]).bindPopup('The html content').addTo(map);
var popupText =
"<b>Magnitude:</b> " +
feature.properties.mag +
"<br><b>Location:</b> " +
feature.properties.place;
layer.bindPopup(popupText, {
closeButton: true,
offset: L.point(0, -20)
});
layer.on("click", function() {
layer.openPopup();
});
},
style: function(feature) {
var mag = feature.properties.mag;
if (mag >= 4.0) {
return { color: "red" };
} else if (mag >= 3.0) {
return { color: "orange" };
} else if (mag >= 2.0) {
return { color: "yellow" };
} else {
return { color: "black" };
}
},
// add GeoJSON layer to the map once the file is loaded
pointToLayer: function(feature, latlng) {
return L.circleMarker(latlng, geojsonMarkerOptions);
}
}).addTo(map);
}
);
</script>
</body>
</html>
Here is an example of a possible solution using es6:
create for instance a div which will hold the anchor tags to be added dynamically:
<div id="anchors"></div>
Now in the js file:
let markersArray = {}; // create the associative array
let magsArray = {}; // here hold the ids that correspond to the mags
// load GeoJSON from an external file
$.getJSON("https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2019-07-20&endtime=2019-07-21&minmagnitude=5", data => {
// color indication by magnitude
L.geoJson(data, {
// add GeoJSON layer to the map once the file is loaded
pointToLayer: function(feature, latlng) {
const mag = feature.properties.mag;
const geojsonMarkerOptions = {
opacity: 0.8,
fillOpacity: 0.6,
// here define the style using ternary operators for circles
color: mag >= 4.0 ? 'red' : mag >= 3.0 ? 'orange' : mag >= 2.0 ? 'yellow' : 'black'
};
// here store the circle markers in the array
markersArray[feature.id] = L.circleMarker(latlng, geojsonMarkerOptions)
.addTo(map)
.bindPopup(
`<b>Magnitude:</b> " ${feature.properties.mag}
<br><b>Location:</b> ${feature.properties.place}`, {
closeButton: true,
offset: L.point(0, -20)
});
// here record the mags
magsArray[feature.id] = feature.properties.mag;
return L.circleMarker(latlng, geojsonMarkerOptions);
},
})
// add dynamically anchor tags
let markup = '';
for (let i in markersArray) {
markup += `<b>${magsArray[i]} Mag</b><br/>`;
}
document.getElementById('anchors').innerHTML = markup;
});
Last but not least I do not see the reason of fetching the data using the API and both have it also as a constant variable in your file.
Demo
i have to change the color or borders of buildings on click.
HTML & JS
Like the examples to hover countries, but with click and not countries -> buildings.
If it's easier with another plugin, please say so. 3D is not a 'must have'.
My Code:
<script>
mapboxgl.accessToken = 'hidden';
var map = new mapboxgl.Map({
style: 'mapbox://styles/mapbox/light-v9',
center: [7.3337859, 50.8403206],
zoom: 19.8,
pitch: 60,
bearing: -70,
hash: true,
container: 'map'
});
// The 'building' layer in the mapbox-streets vector source contains building-height
// data from OpenStreetMap.
map.on('load', function() {
// Insert the layer beneath any symbol layer.
var layers = map.getStyle().layers;
var labelLayerId;
for (var i = 0; i < layers.length; i++) {
if (layers[i].type === 'symbol' && layers[i].layout['text-field']) {
labelLayerId = layers[i].id;
break;
}
}
map.addLayer({
'id': '3d-buildings',
'source': 'composite',
'source-layer': 'building',
'filter': ['==', 'extrude', 'true'],
'type': 'fill-extrusion',
'minzoom': 15,
'paint': {
'fill-extrusion-color': '#aaa',
// use an 'interpolate' expression to add a smooth transition effect to the
// buildings as the user zooms in
'fill-extrusion-height': [
"interpolate", ["linear"], ["zoom"],
15, 0,
15.05, ["get", "height"]
],
'fill-extrusion-base': [
"interpolate", ["linear"], ["zoom"],
15, 0,
15.05, ["get", "min_height"]
],
'fill-extrusion-opacity': .6
}
}, labelLayerId);
map.on('click', '3d-buildings', function(e) {
console.log(e.features[0]);
//map.setPaintProperty('3d-buildings', 'fill-extrude-color', '#FF0000');
map.setPaintProperty('3d-buildings', 'fill-color', '#faafee');
});
});
Thanks :)
You need to add a layer on which to display the selected buildings. For example:
map.addSource('currentBuildings', {
type: 'geojson',
data: {
"type": "FeatureCollection",
"features": []
}
});
map.addLayer({
"id": "highlight",
"source": "currentBuildings",
'type': 'line',
'minzoom': 15,
'paint': {
'line-color': '#f00',
'line-width': 3
}
}, labelLayerId);
map.on('click', '3d-buildings', function(e) {
map.getSource('currentBuildings').setData({
"type": "FeatureCollection",
"features": e.features[0]]
});
});
[ https://jsfiddle.net/o50vy8jc/ ]
I'm using MapQuest Leaflet Api to draw a route with multiple stops (Custom Markers). Everything is almost done. I'm getting a route multiple markers and a poly line.
I have two question
How to draw a optimized route onClick of
button code for route optimization is something like this
dir = MQ.routing.directions();
dir.optimizedRoute({
locations: [
'33.703507, 73.053702',
'33.714328, 73.050625',
'33.730497, 73.077898',
'33.732863, 73.088078'
]
});
How to get distance of total route and time for driving?
My code is given below
<script>
window.onload = function () {
var map,
dir;
var custom_icon,
marker;
map = L.map('map', {
layers: MQ.mapLayer(),
center: [40.045049, -105.961737],
zoom: 7
});
dir = MQ.routing.directions();
dir.route({
locations: [
'33.703507, 73.053702',
'33.714328, 73.050625',
'33.730497, 73.077898',
'33.732863, 73.088078'
],
options: { avoids: ['toll road'] }
});
CustomRouteLayer = MQ.Routing.RouteLayer.extend({
createStopMarker: function (location, stopNumber) {
custom_icon = L.divIcon({
iconSize: [26, 36],
popupAnchor: [0, -18],
html: '<span class="notification">' + stopNumber + '</span>'
});
marker = L.marker(location.latLng, { icon: custom_icon }).bindPopup(location.adminArea5 + ' ' + location.adminArea3).openPopup().addTo(map);
marker.on('click', onMarkerClick);
return marker;
}
});
map.addLayer(new CustomRouteLayer({
directions: dir,
fitBounds: true,
draggable: false,
ribbonOptions: {
draggable: false,
ribbonDisplay: { color: '#CC0000', opacity: 0.3 },
widths: [15, 15, 15, 15, 14, 13, 12, 12, 12, 11, 11, 11, 11, 12, 13, 14, 15]
}
}));
}
</script>
<body style='border:0; margin: 0'>
<div id='map' style='position: absolute; top: 0; bottom: 0; width: 100%;'></div>
</body>
Please help. Thanks :)
That button will call a function with the MQ.routing.directions.optimizedRoute() code in it.
function optimize() {
dir = MQ.routing.directions();
dir.optimizedRoute({
locations: [
'syracuse ny',
'springfield ma',
'ithaca ny',
'hartford ct'
]
});
map.addLayer(MQ.routing.routeLayer({
directions: dir,
fitBounds: true
}));
}
To get the mileage, use the success event to get to the whole directions response.
dir = MQ.routing.directions()
.on('success', function(data) {
console.log(data.route.distance);
});
I want to draw a map with few routes drawn on it.
I want to have a dropbox with numbers 1,..,n
when an item in the dropbox is chosen, the corresponding route is highlighted on the map.
I have started using "leaflet".
how do I highlight a line? I have used "weight" but it's more a border to a line. I would like to see the line is getting bolder.
here is my code:
document.onload = loadMap();
function loadMap() {
var map = L.map('map').setView([37.8, -96], 4);
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
attribution: 'Map data © OpenStreetMap contributors,CC-BY-SA, Imagery © Mapbox',
maxZoom: 18,
id: 'mapbox.streets',
accessToken: 'pk.eyJ1IjoiZW======V6ZTdlb2V5cyJ9.3HqHQ4BMRvSPaYe8ToA7YQ'
}).addTo(map);
var marker = L.marker([51.5, -0.09]).addTo(map);
var myLines = [{
"type": "LineString",
"properties": {
"id": "1"
}
"coordinates": [
[-100, 40],
[-105, 45],
[-110, 55]
]
}, {
"type": "LineString",
"properties": {
"id": "2"
}
"coordinates": [
[-105, 40],
[-110, 45],
[-115, 55]
]
}];
var myLayer = L.geoJson().addTo(map);
myLayer.addData(myLines);
geojson = L.geoJson(myLines, {
onEachFeature: onEachFeature
}).addTo(map);
}
function highlightFeature(e) {
var layer = e.target;
layer
layer.setStyle({
weight: 25,
color: '#ff3300',
dashArray: '',
fillOpacity: 0.7
});
if (!L.Browser.ie && !L.Browser.opera) {
layer.bringToFront();
}
}
function resetHighlight(e) {
geojson.resetStyle(e.target);
layer.setStyle({
weight: 5,
color: '#0000ff',
dashArray: '',
fillOpacity: 0.7
});
}
function onEachFeature(feature, layer) {
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight,
// click: zoomToFeature
});
}
$('select[name="dropdown"]').change(function() {
var item = $(this).val();
alert("call the do something function on option " + item);
//how to make the chosen line highlighted ??
});
weight property is not changing line border, it changes stroke width in pixels. You get border effect because you are adding lines twice. Here:
myLayer.addData(myLines);
And here:
geojson = L.geoJson(myLines, {
onEachFeature: onEachFeature
}).addTo(map);
When a polyline is hovered, top layer's style is changed, but because you are adding polylines twice, there still remains a polyline from the lower layer. As it is described here, default stroke opacity is 0.5 (setting fillOpacity is redundant for the polyline by the way, for changing stroke-opacity opacity property is used). Polyline from the top layer becomes semi-transparent, and that makes the illusion of the border effect.
So, you can just remove this line myLayer.addData(myLines); and get the expected result.
I've made a fiddle, where your example is corrected.