UJHIJL coaches map

by PhilQ

HTML

<!DOCTYPE html>
<html>
	<head>
		<title>ANPR cameras map</title>
		<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
	</head>
	<body>

		<div id="map"></div>
		
		<div id="legend"><h3>Legend</h3></div>

		<div id="search">
			<span class="material-icons md-24">search</span>
			<input type="text" id="search_input" placeholder="Search location" value="">
		</div>

		<button id="btn-current-location" title="Pan to Current Location" class="custom-icon-button"><span class="material-icons">gps_fixed</span></button>

		<button id="btn-last-view" title="Back to Last View" class="custom-icon-button"><span class="material-icons">undo</span></button>

		<!-- See:
			https://developers.google.com/maps/documentation/javascript/overview?hl=nl
			https://developers.google.com/maps/documentation/javascript/reference?hl=nl
		-->
		<!-- Async script executes immediately and must be after any DOM elements used in callback. -->
		<script async src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&v=weekly&channel=2&language=nl"></script>
		<!--<script async
			src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
		</script>-->

		<!-- See: https://googlemaps.github.io/js-markerclusterer/classes/MarkerClusterer.html#algorithm -->
		<script src="https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js"></script>
		
		<!--
			Zendmasten zie: https://antennekaart.nl/kaart/5g?lat=52.22359335&lng=5.42175293&zoom=9
		-->
</body>
</html>

SCSS

@import url("https://fonts.googleapis.com/icon?family=Material+Icons");
.material-icons {
	font-family: 'Material Icons';
	font-weight: normal;
	font-style: normal;
	font-size: 24px;  /* Preferred icon size */
	display: inline-block;
	line-height: 1;
	text-transform: none;
	letter-spacing: normal;
	word-wrap: normal;
	white-space: nowrap;
	direction: ltr;

	/* Support for all WebKit browsers. */
	-webkit-font-smoothing: antialiased;

	/* Support for Safari and Chrome. */
	text-rendering: optimizeLegibility;

	/* Support for Firefox. */
	-moz-osx-font-smoothing: grayscale;

	/* Support for IE. */
	font-feature-settings: 'liga';
}

*, ::before, ::after {
	margin: 0;
	padding: 0;
	box-sizing: border-box;
}

html,
body {
	overflow: hidden;
	height: 100%;
}

/* Always set the map height explicitly to define the size of the div
 * element that contains the map. */
#map {
	height: 100%;
}

.custom-map-control-button {
	background-color: #fff;
	border: 0;
	border-radius: 2px;
	box-shadow: 0 1px 4px -1px rgba(0, 0, 0, 0.3);
	margin: 10px;
	padding: 0 0.5em;
	font: 400 18px Roboto, Arial, sans-serif;
	overflow: hidden;
	height: 40px;
	cursor: pointer;

	&:hover, &:focus {
		background: #ebebeb;
	}
}

.custom-icon-button {
	background-color: #fff;
	border: 0;
	border-radius: 2px;
	box-shadow: 0 1px 4px -1px rgba(0, 0, 0, 0.3);
	text-transform: none;
	appearance: none;
	user-select: none;
	overflow: hidden;
	width: 40px;
	height: 40px;
	cursor: pointer;
	color: #666;

	&:hover, &:focus, &:active {
		color: #111;
		//background: #ebebeb;
	}
}

.margin-t { margin-top: 10px; }
.margin-b { margin-bottom: 10px; }
.margin-l { margin-left: 10px; }
.margin-r { margin-right: 10px; }

#search {
	margin-top: 10px;

	span {
		position: absolute;
		top: 8px;
		left: 8px;
		pointer-events: none;
		color: #5f6368;
	}

	input {
		background-color: #fff;
		border: 0;
		border-radius: 2px;
		box-shadow: 0 1px 4px -1px rgba(0, 0, 0, 0.3);
		text-transform: none;
		appearance: none;
		outline:...

JavaScript

// Colors:
// https://coolors.co/462d5a-5b4a74-70678e-d0ced2-eeeeee-dddfcd-919861-424242-ff6600-ddf0ff

// TODO: More info + link-to (per coach)
// TODO: Same city? variate GPS, so no overlap
let coaches = [
    // { name: "Claudia Fontilus-Erkelens", city2: "Willemstad, Curaçao", city: "Willemstad, Curaçao"},
    // { name: "Masja van Loon", city2: "Kralendijk, Bonaire", city: "Kralendijk, Bonaire"},
    //
    {
        "name": "Tineke Hooijsma",
        "city2": "Sneek",
        "city": "Sneek",
        "location": {
            "label": "Sneek, Nederland",
            "lat": 53.0337476,
            "lng": 5.6556473
        }
    },
    {
        "name": "Tiny Fortezza - Kuiken",
        "city2": "Joure",
        "city": "Joure",
        "location": {
            "label": "Joure, Nederland",
            "lat": 52.9660449,
            "lng": 5.7958646
        }
    },
    //
    {
        "name": "Dineke ten Dolle",
        "city2": "Groningen",
        "city": "Groningen",
        "location": {
            "label": "Groningen, Nederland",
            "lat": 53.2193835,
            "lng": 6.566501799999999
        }
    },
    //
    {
        "name": "Frances van Dinther",
        "city2": "Meppel",
        "city": "Meppel",
        "location": {
            "label": "Meppel, Nederland",
            "lat": 52.6921234,
            "lng": 6.1937187
        }
    },
    {
        "name": "Janneke Huizeling",
        "city2": "Hoogeveen",
        "city": "Hoogeveen",
        "location": {
            "label": "Hoogeveen, Nederland",
            "lat": 52.7286158,
            "lng": 6.490100200000001
        }
    },
    {
        "name": "Anouk Bisschops",
        "city2": "Ruinen",
        "city": "Ruinen",
        "location": {
            "label": "Ruinen, Nederland",
            "lat": 52.763737,
            "lng": 6.3599286
        }
    },
    {
        "name": "Chantal Stegink",
        "city2": "Meppel",
        "city": "Meppel",
        "location": {
    ...