PageSourceSearch

https://sanderd17.github.io/loadData.js

js sanderd17.github.io collected 2026-10-03 09:53:54 UTC 10,711 bytes, 389 lines download raw bytes

1// vim: tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab
2
3// GLOBAL VARIABLES
4var overpassapi = "http://overpass-api.de/api/interpreter?data=";
5var datasets = {
6	"Belgium": {
7		"http://raw.githubusercontent.com/sanderd17/sanderd17.github.io/master/haltes_de_lijn_dataset.json": "Haltes De Lijn",
8	},
9};
10var datasetSettings = {};
11var tiledData = {};
12var appSettings = {};
13var queryStatus = {"busy": false, "waiting": false};
14
15function loadDatasets()
16{
17	for (var country in datasets)
18	{
19		for (var dataset in datasets[country])
20		{
21			htmlHelper.addDataset(country, datasets[country][dataset], dataset);
22			var req = new XMLHttpRequest();
23			req.overrideMimeType("application/json");
24			req.onreadystatechange = function()
25			{
26				if (req.readyState != 4)
27					return;
28				var settings = JSON.parse(req.responseText);
29				settings.layer = new L.LayerGroup();
30				datasetSettings[dataset] = settings;
31				var el = document.getElementById(dataset + "Dataset");
32				if (el.checked)
33					toggleDataset(dataset, el);
34			}
35			req.open("GET", dataset, true);
36			req.send(null);
37		}
38	}
39}
40
41function toggleDataset(dataset, element)
42{
43	if (!datasetSettings[dataset] || !datasetSettings[dataset].layer)
44		return; // wait until the page is loaded
45
46	if (element.checked)
47		mapObj.addLayer(datasetSettings[dataset].layer);
48	else
49		mapObj.removeLayer(datasetSettings[dataset].layer);
50	loadData();
51}
52
53function changeSetting(element)
54{
55	var id = element.id;
56	if (id.slice(-7) != "Setting")
57		return;
58	id = id.slice(0, -7);
59	appSettings[id] = element.checked;
60	if (id == "hideCompletePOI") // re-render points
61		for (var dataset in tiledData)
62			for (var tileName in tiledData[dataset])
63				if (tiledData[dataset][tileName].data)
64					for (var p = 0; p < tiledData[dataset][tileName].data.length; p++)
65						displayPoint(dataset, tileName, p)
66	saveAppState();
67}
68
69function loadData()
70{
71	var mapBounds = mapObj.getBounds();
72	var mapCenter = mapObj.getCenter();
73	var mapZoom = mapObj.getZoom();
74	// get center in tile coordinates
75	for (var datasetName in datasetSettings)
76	{
77		if (!tiledData[datasetName])
78			tiledData[datasetName] = {};
79		var settings = datasetSettings[datasetName];
80
81		if (!mapBounds.intersects([[settings.bbox.b,settings.bbox.l],[settings.bbox.t,settings.bbox.r]]))
82			continue;
83		if (!mapObj.hasLayer(settings.layer))
84			continue;
85
86		if (!settings.icons)
87			loadIcons(settings);
88
89		var tileCoordinates = geoHelper.latlonToTilenumber(
90			mapCenter.lat,
91			mapCenter.lng,
92			settings.data.zoom);
93
94		// Load 9 tiles around the center
95		for (var x = tileCoordinates.x - 1; x <= tileCoordinates.x + 1; x++)
96		{
97			for (var y = tileCoordinates.y - 1; y <= tileCoordinates.y + 1; y++)
98			{
99				var tileName = x + "_" + y;
100				if (tiledData[datasetName][tileName])
101					continue; // tile already loaded or loading
102				tiledData[datasetName][tileName] = {};
103
104				(function(datasetName, tileName, source)
105				{
106					var req = new XMLHttpRequest();
107					req.overrideMimeType("application/json");
108					req.onreadystatechange = function()
109					{
110						if (req.readyState != 4)
111							return;
112						var data = geojsonToPointlist(JSON.parse(req.responseText));
113						tiledData[datasetName][tileName].data = data;
114						for (var p = 0; p < data.length; p++)
115							displayPoint(datasetName, tileName, p);
116						loadOverpass();
117					}
118					req.open("GET", source + "/" + tileName + ".json", true);
119					try { req.send(null); } catch (e) {}
120				})(datasetName, tileName, settings.data.source);
121			}
122		}
123		loadOverpass();
124	}
125	saveAppState();
126}
127
128function loadOverpass()
129{
130	// Overpass only accepts one query at a time, schedule new query if query is waiting
131	if (queryStatus.busy)
132	{
133		queryStatus.waiting = true;
134		return;
135	}
136	queryStatus.waiting = false;
137	var mapZoom = mapObj.getZoom();
138	var mapBounds = mapObj.getBounds();
139	var mapCenter = mapObj.getCenter();
140	// Make query
141	var query = "[out:json];\n";
142	var types = ["node"/*,"way","rel"*/];
143	var queriedDatasets = [];
144	for (var datasetName in datasetSettings)
145	{
146		if (!tiledData[datasetName])
147			continue;
148		var settings = datasetSettings[datasetName];
149		var tileCoordinates = geoHelper.latlonToTilenumber(
150			mapCenter.lat,
151			mapCenter.lng,
152			settings.data.zoom);
153
154		for (var x = tileCoordinates.x - 1; x <= tileCoordinates.x + 1; x++)
155		{
156			for (var y = tileCoordinates.y - 1; y <= tileCoordinates.y + 1; y++)
157			{
158				var tileName = x + "_" + y;
159				var tileBbox = geoHelper.tilenumberToBbox(x, y, settings.data.zoom);
160				// add a margin to the bbox
161				geoHelper.padBbox(tileBbox, settings.dist);
162
163				if (!tiledData[datasetName][tileName] ||
164					!tiledData[datasetName][tileName].data ||
165					!tiledData[datasetName][tileName].data.length)
166						continue;
167				if (tiledData[datasetName][tileName].overpassQueried)
168					continue;
169				tiledData[datasetName][tileName].overpassQueried = true;
170
171				query += "(";
172				for (var t = 0; t < types.length; t++)
173					query += types[t] + settings.query + "(" + tileBbox.b + "," + tileBbox.l + "," + tileBbox.t + "," + tileBbox.r + ");";
174				query += "); out center; out count;\n";
175
176				queriedDatasets.push({"tileName": tileName, "datasetName": datasetName});
177			}
178		}
179	}
180
181	if (!queriedDatasets.length)
182		return;
183
184	console.log("overpass query:\n" + query);
185
186	// Send query to overpass
187	var req = new XMLHttpRequest();
188	req.onreadystatechange = function()
189	{
190		if (req.readyState != 4)
191			return;
192		if (req.status != 200)
193			return;
194		var osmData = JSON.parse(req.responseText).elements;
195		compareData(queriedDatasets, osmData);
196		queryStatus.busy = false;
197		if (queryStatus.waiting)
198			loadOverpass();
199	}
200	queryStatus.busy = true;
201	req.open("GET", overpassapi + encodeURIComponent(query), true);
202	req.send(null);	
203}
204
205function displayPoint(datasetName, tileName, idx)
206{
207	var point = tiledData[datasetName][tileName].data[idx];
208	var settings = datasetSettings[datasetName];
209	// add marker to the data for future reference
210	if (!point.marker)
211		point.marker = L.marker(point.coordinates, {icon: settings.greyIcon})
212			.addTo(settings.layer);
213
214	if (point.score == undefined)
215		return; // only initial display
216
217	// if the point has been compared, change its colour
218	if (point.score == point.maxScore && appSettings.hideCompletePOI)
219		point.marker.setOpacity(0);
220	else
221		point.marker.setOpacity(1);
222
223	point.marker.setIcon(settings.icons[Math.floor(10 * point.score/point.maxScore)]);
224	point.marker.bindPopup(htmlHelper.getPopup(datasetName, tileName, idx), {"maxWidth": 900});
225}
226
227function loadIcons(settings)
228{
229	// create the icon in 11 colours from red to green + a grey one
230	settings.icons = [];
231	for (var i = 0; i <= 10; i++)
232	{
233		var colour = hslToRgb(i / 30, 1, 0.5);
234		settings.icons.push(L.MakiMarkers.icon({icon: settings.icon, color: colour, size: "m"}));
235	}
236	settings.greyIcon = L.MakiMarkers.icon({icon: settings.icon, color: "#808080", size: "m"});
237}
238
239function geojsonToPointlist(geojson)
240{
241	var results = [];
242	if (!geojson.features)
243		return results;
244
245	for (var i = 0; i < geojson.features.length; i++)
246	{
247		// TODO skip non-point features
248		var point = {};
249		// switch lat and lon to the OSM standard order
250		point.coordinates = {};
251		point.coordinates.lat = geojson.features[i].geometry.coordinates[1];
252		point.coordinates.lon = geojson.features[i].geometry.coordinates[0];
253		point.properties = geojson.features[i].properties;
254		results.push(point);
255	}
256	return results;
257}
258
259/**
260 * Converts an HSL color value to RGB. Conversion formula
261 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
262 * Assumes h, s, and l are contained in the set [0, 1] and
263 * returns r, g, and b in the set [0, 255].
264 *
265 * @param   Number  h       The hue
266 * @param   Number  s       The saturation
267 * @param   Number  l       The lightness
268 * @return  Array           The RGB representation
269 */
270function hslToRgb(h, s, l){
271	var r, g, b;
272
273	if(s == 0)
274	{
275		r = g = b = l; // achromatic
276	}
277	else
278	{
279		function hue2rgb(p, q, t) {
280			if(t < 0) t += 1;
281			if(t > 1) t -= 1;
282			if(t < 1/6) return p + (q - p) * 6 * t;
283			if(t < 1/2) return q;
284			if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
285			return p;
286		}
287
288		var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
289		var p = 2 * l - q;
290		r = hue2rgb(p, q, h + 1/3);
291		g = hue2rgb(p, q, h);
292		b = hue2rgb(p, q, h - 1/3);
293	}
294
295	function toHex(n) {
296		var h = Math.round(n * 255).toString(16);
297		if (h.length < 2)
298			h = "0" + h;
299		return h;
300	}
301
302	return "#" + toHex(r) + toHex(g) + toHex(b);
303}
304
305
306function escapeXML(str)
307{
308	return str.replace(/&/g, "&amp;")
309		.replace(/'/g, "&apos;")
310		.replace(/>/g, "&gt;")
311		.replace(/</g, "&lt;");
312}
313
314function saveAppState()
315{
316	var date = new Date();
317	date.setFullYear(date.getFullYear() + 1);
318	var stateString = getStateString();
319	document.cookie = "state=" + encodeURIComponent(stateString) + ";expires=" + date.toUTCString();
320	window.location.hash = stateString;
321}
322
323function loadAppState()
324{
325	var stateString;
326	if (window.location.hash)
327		stateString = window.location.hash.slice(1);
328	else if (document.cookie)
329		stateString = decodeURIComponent(document.cookie.slice(6));
330	if (stateString)
331		applyStateString(stateString);
332}
333
334function getStateString()
335{
336	var r = "";
337	var center = mapObj.getCenter();
338	r += "map=" + mapObj.getZoom() + "/" + center.lat.toFixed(4) + "/" + center.lng.toFixed(4);
339
340	var loadedDatasets = [];
341	for (var dataset in datasetSettings)
342		if (mapObj.hasLayer(datasetSettings[dataset].layer))
343			loadedDatasets.push(encodeURIComponent(dataset));
344	if (loadedDatasets.length > 0)
345		r+= "&datasets=" + loadedDatasets.join(";");
346
347	var activatedSettings = [];
348	for (var key in appSettings)
349		if (appSettings[key] == true)
350			activatedSettings.push(encodeURIComponent(key));
351	if (activatedSettings.length > 0)
352		r += "&settings=" + activatedSettings.join(";");
353	return r;
354}
355
356function applyStateString(state)
357{
358	var splitState = state.split("&");
359	for (var i = 0; i < splitState.length; i++)
360	{
361		if (splitState[i].indexOf("map=") == 0)
362		{
363			var mapState = splitState[i].match(/[0-9\.]+/g);
364			mapObj.setView([+mapState[1], +mapState[2]], +mapState[0]);
365		}
366		else if (splitState[i].indexOf("datasets=") == 0)
367		{
368			var loadedDatasets = splitState[i].substr(9).split(";"); 
369			for (var d = 0; d < loadedDatasets.length; d++)
370			{
371				var id = decodeURIComponent(loadedDatasets[d])
372				var el = document.getElementById(id + "Dataset");
373				el.checked = true;
374				toggleDataset(id, el);
375			}
376		}
377		else if (splitState[i].indexOf("settings=") == 0)
378		{
379			var activatedSettings = splitState[i].substr(9).split(";");
380			for (var s = 0; s < activatedSettings.length; s++)
381			{
382				var id = decodeURIComponent(activatedSettings[s]);
383				var el = document.getElementById(id + "Setting");
384				el.checked = true;
385				changeSetting(el);
386			}
387		}
388	}
389}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.