Issues (14)

dist/app.js (1 issue)

1
/*global: $, mapboxgl, MapboxDirections, turf*/
2
3 View Code Duplication
$(window).on("scroll", function () {
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
4
  var scrollValue = $(window).scrollTop();
5
  if (scrollValue > 220) {
6
    $(".navbar").addClass("affix");
7
  } else {
8
    $(".navbar").removeClass("affix");
9
  }
10
});
11
12
/*Load location (stores2)*/
13
function loadLocations(asynchr = true) {
14
  var locations = null;
15
  $.ajax({
16
    async: asynchr,
17
    global: false,
18
    url: "https://leipzig-einkaufen.de/location.json",
19
    //"url": "http://localhost/vectortiles/museen.json",
20
    dataType: "json",
21
    success: function (data) {
22
      locations = data;
23
    }
24
  });
25
  return locations;
26
}
27
28
var stores2 = null; // = loadLocations();
29
// Set bounds to Leipzig, Germany
30
var bounds = [
31
  [12.179, 51.227], // Southwest coordinates
32
  [12.6, 51.459] // Northeast coordinates
33
];
34
mapboxgl.accessToken = 'pk.eyJ1Ijoic2hldWIiLCJhIjoiWGtobTNPNCJ9.v2JwlNSGBm_KxJUKE_WLig';
35
// declare map
36
var map = new mapboxgl.Map({
37
  container: "map",
38
  style: "https://leipzig-einkaufen.de/json/style-local.json",
39
  // style: "http://localhost/vectortiles/json/style-local.json",
40
  // style: "mapbox://styles/sheub/cjk46bdbx1ijd2rqlapjwhvtx",
41
  //style: "mapbox://styles/sheub/cjiiex8lj1lnv2so3ampx1483",  
42
  //style: "mapbox://styles/sheub/cjk6lp2096l222rscgzygdxq4",
43
44
  center: [12.3722, 51.3272],
45
  zoom: 11,
46
  //attributionControl: true,
47
  hash: false//,
48
  //maxZoom: 14.9,
49
  //maxBounds: bounds // Sets bounds as max
50
});
51
52
// var map = new mapboxgl.Map({
53
//   container: 'map',
54
//   style: 'mapbox://styles/mapbox/light-v9',
55
56
//   center: [12.3722, 51.3272],
57
//   zoom: 11,
58
//   attributionControl: true,
59
//   hash: false,
60
//   maxZoom: 14.9,
61
//   maxBounds: bounds // Sets bounds as max
62
// });
63
64
/*Declare MapDirections*/
65
var mapDirections = new MapboxDirections();
66
/*MapDirections Settings*/
67
mapDirections.accessToken =
68
  "pk.eyJ1Ijoic2hldWIiLCJhIjoiWGtobTNPNCJ9.v2JwlNSGBm_KxJUKE_WLig";
69
mapDirections.unit = "metric";
70
mapDirections.proximity = false; /*proximity ??*/
71
mapDirections.interactive = false;
72
mapDirections.profile = "driving"; //, "walking", "cycling";
73
// UI controls
74
mapDirections.controls = {
75
  inputs: true,
76
  instructions: false
77
};
78
79
/*Add mapDirections Controls*/
80
map.addControl(new MapboxDirections(mapDirections), "top-left");
81
82
map.addControl(
83
  new mapboxgl.ScaleControl({
84
    maxWidth: 80,
85
    unit: "metric"
86
  })
87
);
88
89
var directionControl = document.getElementsByClassName(
90
  "mapboxgl-ctrl-directions"
91
);
92
directionControl["0"].hidden = true;
93
var ptsWithin = null;
94
95
var filterEl = document.getElementById("feature-filter");
96
var listingsEl = document.getElementById("listings");
97
var txtCategoriesEl = document.getElementById("txtCategories");
98
99
// Empty Geojson Data
100
var bufferedLinestring = {
101
  id: "0",
102
  type: "Feature",
103
  geometry: {
104
    type: "Point",
105
    coordinates: [0, 0]
106
  },
107
  properties: {}
108
};
109
110
// Functions
111
function normalizeString(string) {
112
  return string.trim().toLowerCase();
113
}
114
115
function createPopUp(currentFeature) {
116
  new mapboxgl
117
    .Popup({
118
      closeOnClick: true
119
    })
120
    .setLngLat(currentFeature.geometry.coordinates)
121
    .setHTML(
122
      "<h3>" +
123
      currentFeature.properties.name +
124
      "</h3>" +
125
      "<h4>" +
126
      currentFeature.properties.description +
127
      "</h4>"
128
    )
129
    .addTo(map);
130
}
131
132
function getUniqueFeatures(array, comparatorProperty) {
133
  var existingFeatureKeys = {};
134
  // Because features come from tiled vector data, feature geometries may be split
135
  // or duplicated across tile boundaries and, as a result, features may appear
136
  // multiple times in query results.
137
  var uniqueFeatures = array.filter(function (el) {
138
    if (existingFeatureKeys[el.properties[comparatorProperty]]) {
139
      return false;
140
    }
141
    existingFeatureKeys[el.properties[comparatorProperty]] = true;
142
    return true;
143
  });
144
145
  return uniqueFeatures;
146
}
147
148
function colorLocationList(data) {
149
  // Iterate through the list of stores
150
  // WITHIN THE CALCULATED ROUTE !! and color in green
151
  if (data.length) {
152
    data.forEach(function (feature) {
153
      // Shorten data.feature.properties to just `prop`.
154
      var prop = feature.properties;
155
      var cardHeader = document.getElementById("heading" + prop.id);
156
      if (cardHeader === null) {
157
        return;
158
      }
159
160
      var cardTitle = cardHeader.getElementsByClassName("title");
161
      cardTitle[0].style.color = "#608BC7";
162
    });
163
  }
164
}
165
166
function buildLocationList(data) {
167
  // Iterate through the list of stores
168
  listingsEl.innerHTML = "";
169
  if (data.length) {
170
    data.forEach(function (feature) {
171
      // Shorten feature.properties to just `prop` so we're not writing this long form over and over again.
172
      var prop = feature.properties;
173
174
      // Select the listing container in the HTML and append a div  with the class 'item' for each store
175
      var card = listingsEl.appendChild(document.createElement("div"));
176
      card.className = "item card cardList";
177
      card.id = prop.id;
178
179
      var cardHeader = card.appendChild(document.createElement("div"));
180
      cardHeader.className = "card-header";
181
      cardHeader.setAttribute("role", "tab");
182
183
      cardHeader.setAttribute("id", "heading" + card.id);
184
      cardHeader.id = "heading" + card.id;
185
186
      var cardMb0 = cardHeader.appendChild(document.createElement("h5"));
187
      cardMb0.className = "mb-0";
188
189
      // Create a new link with the class 'title' for each store and fill it with the store address
190
      var link = cardMb0.appendChild(document.createElement("a"));
191
      link.setAttribute("data-toggle", "collapse");
192
      link.href = "#collapse" + card.id;
193
      link.setAttribute("aria-expanded", "false");
194
      link.setAttribute("aria-controls", "collapse" + card.id);
195
      link.className = "title";
196
      link.textContent = prop.name;
197
      link.title =  "Brief description of " + prop.name;
198
      link.dataPosition = card.id;
199
200
      var cardCollapse = card.appendChild(document.createElement("div"));
201
      cardCollapse.className = "collapse";
202
      cardCollapse.setAttribute("id", "collapse" + card.id);
203
      cardCollapse.setAttribute("role", "tabpanel");
204
      cardCollapse.setAttribute("aria-labelledby", "heading" + card.id);
205
      cardCollapse.setAttribute("data-parent", "#listings");
206
207
      if (prop.image) {
208
        var cardImg = cardCollapse.appendChild(document.createElement("img"));
209
        cardImg.className = "img-responsive img-listing lazyload";
210
        cardImg.src = prop.image;
211
        cardImg.alt = prop.name;
212
        cardImg.title = prop.name;
213
      }
214
215
      var cardBody = cardCollapse.appendChild(document.createElement("div"));
216
      cardBody.className = "card-body";
217
      cardBody.textContent = prop.description;
218
      cardBody.appendChild(document.createElement("br"));
219
220
      if (prop.url) {
221
        var linkBody = cardBody.appendChild(document.createElement("a"));
222
        linkBody.textContent = prop.name;
223
        linkBody.href = prop.url;
224
        linkBody.target = "_blank";
225
        linkBody.title = prop.name;
226
        linkBody.rel = "noopener";
227
      }
228
229
      // Add an event listener for the links in the sidebar listing
230
      link.addEventListener("click", function () {
231
        // Update the currentFeature to the store associated with the clicked link
232
        var clickedListing = stores2.features[this.dataPosition];
233
234
        var popUps = document.getElementsByClassName("mapboxgl-popup");
235
        // Check if there is already a popup on the map and if so, remove it
236
        if (popUps[0]) {
237
          popUps[0].parentNode.removeChild(popUps[0]);
238
        }
239
240
        // 1. Close all other popups and display popup for clicked store
241
        createPopUp(clickedListing);
242
243
        // 2. Highlight listing in sidebar (and remove highlight for all other listings)
244
        var activeItem = document.getElementsByClassName("is-active");
245
        if (activeItem[0]) {
246
          activeItem[0].classList.remove("is-active");
247
        }
248
        this.classList.add("is-active");
249
      });
250
    });
251
  } else {
252
    var empty = document.createElement("p");
253
    empty.textContent = "Ziehen Sie die Karte, um die Ergebnisse zu füllen";
254
    listingsEl.appendChild(empty);
255
256
    // remove features filter
257
    map.setFilter("locations", ["has", "Categories"]);
258
  }
259
260
  // Populate features for the listing overlay.
261
  if (ptsWithin) {
262
    colorLocationList(ptsWithin.features);
263
  }
264
}
265
266
function filterOnRoute() {
267
  var mapDirectionsSource = map.getSource("directions");
268
  var radius = 0.6;
269
  var unit = "kilometers";
270
271
  //var distDuration = mapDirections.getDistanceAndDuration();
272
273
  // buffer the route with a area of radius 'radius'
274
  if (mapDirectionsSource._data.features.length < 2) {
275
    return;
276
  }
277
  var bufferedLinestring = turf.buffer(
278
    mapDirectionsSource._data.features[2].geometry,
279
    radius, {
280
      units: unit
281
    }
282
  );
283
284
  // update bufferedTraceSource
285
  map.getSource("bufferedTraceSource").setData(bufferedLinestring);
286
287
  // Get locations rendered on the map
288
  var features = map.queryRenderedFeatures({
289
    layers: ["locations"]
290
  });
291
292
  // use featureCollection to convert features (array of features) into a collection of features (Object type FeatureCollection);
293
  var collection = turf.featureCollection(features);
294
295
  // Filter the points to the area around the direction
296
  ptsWithin = turf.pointsWithinPolygon(collection, bufferedLinestring);
297
298
  // Populate features for the listing overlay.
299
  if (ptsWithin) {
300
    buildLocationList(features);
301
  }
302
}
303
304
function displayDirectionControls() {
305
  var directionControl = document.getElementsByClassName(
306
    "mapboxgl-ctrl-directions"
307
  );
308
  if (directionControl["0"].hidden) {
309
    directionControl["0"].hidden = false;
310
    map.setLayoutProperty("bufferedTraceLayer", "visibility", "visible");
311
312
    map.setLayoutProperty("directions-origin-point", "visibility", "visible");
313
    map.setLayoutProperty(
314
      "directions-destination-point",
315
      "visibility",
316
      "visible"
317
    );
318
    map.setLayoutProperty("directions-origin-label", "visibility", "visible");
319
    map.setLayoutProperty(
320
      "directions-destination-label",
321
      "visibility",
322
      "visible"
323
    );
324
325
    map.setLayoutProperty("directions-hover-point", "visibility", "visible");
326
    map.setLayoutProperty("directions-waypoint-point", "visibility", "visible");
327
    map.setLayoutProperty("directions-route-line", "visibility", "visible");
328
    map.setLayoutProperty("directions-route-line-alt", "visibility", "visible");
329
    filterOnRoute();
330
  } else {
331
    directionControl["0"].hidden = true;
332
    // reinitialize ptsWithin
333
    ptsWithin = null;
334
335
    map.setLayoutProperty("bufferedTraceLayer", "visibility", "none");
336
    map.setLayoutProperty("directions-origin-point", "visibility", "none");
337
    map.setLayoutProperty("directions-destination-point", "visibility", "none");
338
    map.setLayoutProperty("directions-origin-label", "visibility", "none");
339
    map.setLayoutProperty("directions-destination-label", "visibility", "none");
340
341
    map.setLayoutProperty("directions-hover-point", "visibility", "none");
342
    map.setLayoutProperty("directions-waypoint-point", "visibility", "none");
343
    map.setLayoutProperty("directions-route-line", "visibility", "none");
344
    map.setLayoutProperty("directions-route-line-alt", "visibility", "none");
345
346
    var features = map.queryRenderedFeatures({
347
      layers: ["locations"]
348
    });
349
350
    if (features) {
351
      // Populate features for the listing overlay.
352
      buildLocationList(features);
353
    }
354
  }
355
}
356
357
// Call buildlist function on initialization
358
if (!stores2) {
359
  stores2 = loadLocations(false);
360
  buildLocationList(stores2.features);
361
}
362
363
364
// Load map
365
map.on("load", function (e) {
366
367
  map.loadImage(
368
    "https://leipzig-einkaufen.de/media/diagonal-noise.png",
369
    function (error, image) {
370
      if (error) {
371
        throw error;
372
      }
373
      map.addImage("background_pattern", image);
374
    }
375
  );
376
377
  map.loadImage(
378
    "https://leipzig-einkaufen.de/media/Marker_with_Shadow.png",
379
    function (error, image) {
380
      if (error) {
381
        throw error;
382
      }
383
      map.addImage("marker_z", image);
384
385
386
387
      // Add the stores2 (locations_source) to the map
388
      map.addSource("locations_source", {
389
        type: "geojson",
390
        data: "https://leipzig-einkaufen.de/location.json"
391
      });
392
393
      // Add the locations_source to the map as a layer
394
      map.addLayer({
395
        id: "locations",
396
        type: "symbol",
397
        // Add a GeoJSON source containing place coordinates and information.
398
        source: "locations_source",
399
        layout: {
400
          visibility: "visible",
401
          "icon-image": "marker_z",
402
          "icon-size": 0.9,
403
          "icon-allow-overlap": true
404
        }
405
      });
406
    });
407
408
  // Add the bufferedLinestring to the map as a layer
409
  map.addSource("bufferedTraceSource", {
410
    type: "geojson",
411
    data: bufferedLinestring,
412
    maxzoom: 13
413
  });
414
415
  map.addLayer({
416
    id: "bufferedTraceLayer",
417
    type: "fill",
418
    source: "bufferedTraceSource",
419
    layout: {
420
      visibility: "visible"
421
    },
422
    paint: {
423
      "fill-color": "rgb(0,0,0)",
424
      "fill-opacity": 1,
425
      "fill-translate": [0, 2.5],
426
      "fill-pattern": "background_pattern"
427
    }
428
  });
429
430
  // Add Fullscreen control to the map.
431
  //map.addControl(new mapboxgl.FullscreenControl());
432
433
  // Add geolocate control to the map.
434
  map.addControl(
435
    new mapboxgl.GeolocateControl({
436
      positionOptions: {
437
        enableHighAccuracy: true
438
      },
439
      trackUserLocation: true
440
    })
441
  );
442
443
  // When a click event occurs on a feature in the places layer, open a popup at the
444
  // location of the feature, with description HTML from its properties.
445
  map.on("click", "locations", function (e) {
446
    var currentFeature = e.features[0];
447
    // 1. Create Popup
448
    createPopUp(currentFeature);
449
450
    // 2. Highlight listing in sidebar (and remove highlight for other listing)
451
    var activeItem = document.getElementsByClassName("is-active");
452
    if (activeItem[0]) {
453
      activeItem[0].classList.remove("is-active");
454
    }
455
456
    var headingElement = document.getElementById(
457
      "heading" + currentFeature.properties.id
458
    );
459
    if (headingElement) {
460
      headingElement.classList.add("is-active");
461
    }
462
    var collapseElement = document.getElementById(
463
      "collapse" + currentFeature.properties.id
464
    );
465
    if (collapseElement) {
466
      $(collapseElement).collapse("show");
467
    }
468
  });
469
470
  map.on("moveend", function () {
471
    // Query all the rendered points in the view
472
    var features = map.queryRenderedFeatures({
473
      layers: ["locations"]
474
    });
475
476
    if (features) {
477
      //var uniqueFeatures = getUniqueFeatures(features, "Categories");
478
479
      // Populate features for the listing overlay.
480
      buildLocationList(features);
481
482
      // Clear the input container
483
      filterEl.value = "";
484
485
      // Store the current features in sn `locations_on_map` variable to later use for filtering on `keyup`.
486
      //locations = features;
487
    }
488
  });
489
490
  map.on("mousemove", "locations", function () {
491
    // Change the cursor style as a UI indicator.
492
    map.getCanvas().style.cursor = "pointer";
493
  });
494
495
  map.on("mouseleave", "locations", function () {
496
    map.getCanvas().style.cursor = "";
497
  });
498
499
  $(".dropdown-item").click(function () {
500
501
    var value = normalizeString($(this).text());
502
    var filtered = map.querySourceFeatures("locations_source");
503
504
    if (value !== "alle") {
505
      // Filter visible features that don't match the input value.
506
      filtered = filtered.filter(function (feature) {
507
        var name = normalizeString(feature.properties.name);
508
        var Categories = normalizeString(feature.properties.Categories);
509
        return name.indexOf(value) > -1 || Categories.indexOf(value) > -1;
510
      });
511
    }
512
    if (!filtered) {
513
      return;
514
    }
515
516
    var uniqueFeatures = getUniqueFeatures(filtered, "Categories");
517
    // Populate the sidebar with filtered results
518
    buildLocationList(uniqueFeatures);
519
520
    // Set the filter to populate features into the layer.
521
    map.setFilter(
522
      "locations", ["in", "name"].concat(
523
        uniqueFeatures.map(function (feature) {
524
          return feature.properties.name;
525
        })
526
      )
527
    );
528
529
    txtCategoriesEl.value = value;
530
  });
531
});
532
533
// Direction event listener
534
mapDirections.on("route", function () {
535
  filterOnRoute();
536
});
537
538
// Display Direction
539
$("#btnDisplayControls").on("click", function () {
540
  displayDirectionControls();
541
});