PageSourceSearch

https://openbomenkaart.org/scripts/active_6/circles.js

js openbomenkaart.org collected 2026-09-25 06:42:49 UTC 66,778 bytes, 2,024 lines download raw bytes

1//tellen hoeveel er geen of ongeldig plantjaar hebben, en hoeveel wel
2//kleuren per genus weer in zijpaneel tonen
3
4
5// https://leafletjs.com/reference-1.0.0.html#tooltip 
6// performance: consider .circleMarker instead of .circle  
7// https://stackoverflow.com/questions/43015854/large-dataset-of-markers-or-dots-in-leaflet/43019740#43019740 -->
8
9console.log ('====================================', crlf) ;
10
11
12
13var count_trees   = 0 ;
14var count_shrubs  = 0 ;
15var count_public  = 0 ;
16var count_private = 0 ;
17var count_parks   = 0 ;
18var count_streets = 0 ;
19var count_young   = 0 ;
20var count_old     = 0 ;
21
22var old_radius_modifier = 0 ;
23var log_circles = 0 ;
24var circles_trees  = [] ;
25var species_trees  = [] ;
26var circles_parks  = [] ;
27var markers_parks  = [] ;
28
29
30var color_border_blink  = '#000000' ;
31var color_msec_elapsed  = '#AAAAAA' ;
32var color_stop_blinking = '#FFFF00' ;
33
34var year_marked = 0 ;
35var planted_year_known = 0 ; 
36var planted_year_in_range = 0 ; 
37
38var grow_radius_per_zoom_level = 1.25 ;
39
40var dfg = 0 ;
41var blink_cycles = 0 ;
42var planted2000 ;
43
44var showing_parks    = false ;
45var showing_one_park = false ;
46
47if (mobile_device)
48{ circle_radius_min = 0.14 * radius_multiplier ; } 
49else
50{ circle_radius_min = 0.14 * radius_multiplier  ; } // smallest radius to cover OSM tree circle
51
52if (mode_arboretum)
53{ circle_radius_min = 0.20 * radius_multiplier  ; } 
54
55console.log ('Radius settings: ' + crlf + 
56						 'radius_multiplier: ' + radius_multiplier + crlf + 
57						 'circle_radius_min: ' + circle_radius_min + crlf + crlf);
58
59
60
61// this debug function allows keeping focus on circles.js in devtools, with 'preserve log' chacked  
62function circles_log (text) 
63{ console.log (text) ; }
64
65function addCircleToMap (tags, id, ref, index, type, planted,lat,lon,ispublic,   
66                         color_fill,color_border,radius,weight,opacity,hover_content,popup_content,species_sc, tree_dead)
67{
68  //if ((tags.context != null) && (tags.context != 'park')) 
69  //{ return ('') ; }      
70
71// hover_content and popup_content are no longer supplied to this function
72// prerender_dialog_hover and prerender_dialog_popup are always false
73
74  if (! prerender_dialog_hover) // always passes, prerender_dialog_hover is always false 
75  { hover_content = index.toString () ; } 
76
77  if (! prerender_dialog_popup) // always passes, prerender_dialog_hover is always false 
78  { popup_content = index.toString () ; }
79
80  countCircles (tags) ;   
81    
82//console.log ('qqq addCircleToMap species_sc:',species_sc,', color_fill:',color_fill) ;    
83  // radius is in meters
84  if ((lat == null) || (lon == null))
85  { 
86    console.log ('addCircleToMap: !!! coord err ','lat: ',lat,', lon: ',lon) ;  
87    return ; 
88  }
89
90  if ((map == null) || (map == map_prev))
91  { alert ('addCircleToMap map not loaded') ; }
92
93  if (type == null)
94  { type = text_tree ; }
95
96  if (type == text_shrub)
97  { 
98    color_border = '#FFFFFF' ; // AAAA44' ; 
99    weight += 1 ; 
100
101    if (index == -1) // white circle
102    { 
103      color_border = '#000000' ; 
104      color_fill   = '#888888' ;
105    }
106  }  
107
108  var circle ;
109
110  if (isNaN (radius)) 
111  { 
112    if (species_sc != null) 
113    { console.log ('Error on id ' + id + ':' + species_sc + ' radius NaN') ; }
114    radius = 1 ;
115  } 
116	
117	if (radius < 1.0)
118	{ radius = 1.0 ; }
119
120  // console.log ('addCircleToMap not mobile_mode ' + index + crlf + 
121  //  ', radius: ' + radius +crlf + 
122  //  ', color: '  + color_border + crlf +  
123  //  ', fillColor: ' + color_fill + crlf + 
124  //  ', opacity: ' + opacity + crlf + 
125  //  ', weight: ' + weight + crlf +
126  //  ', hover ' + hover_content) ; 
127  if (tree_dead)
128  { weight = 10 ; }
129
130  if (! color_fill_circles)
131  { color_fill = '#FFFFFF' ; } // bij kaarten voor opmeten
132
133	if (color_fill == '#FFFFFF')
134	{ 
135		color_border = '#000000' ; 
136		weight = 0.2 ;
137	}
138
139  /* find out how to make this work: put gay circle for trees with known species
140  console.log ('SSS1' + species_sc, color_fill) ;
141	if ((! color_fill_circles) && (species_sc == ''))
142  { color_fill = '#8888888' ; } // markeer bij zwart witte kaart welke bomen al bekend zijn
143  console.log ('SSS2' + species_sc, color_fill) ;
144	*/
145
146  circle = new customCircle ([lat, lon],
147  {
148    //test        : 'xyxyxyxyxyxxyy',
149    index       : index,  // index into json array  
150    id          : id,
151    ref         : ref,
152    radius      : radius,
153    color       : color_border,
154    color2      : color_border,
155    fillColor   : color_fill,
156    fillColor2  : color_fill,
157    fillOpacity : opacity,
158    weight      : weight,
159    dead        : false, // tree_dead,      
160    planted     : planted,
161    place       : place.toLowerCase (),
162		species_sc  : species_sc,
163		type        : type,
164  //  context     : tags.context,
165   
166 // fillPattern : pattern,
167 // dashArray   : "2",
168  }) ;
169
170  indexed_circles [index] = circle ; 
171  
172  if (hover_content != '')
173  { circle.bindTooltip (hover_content,) ; }
174
175  if (popup_content != '')
176  {   
177    circle.bindPopup (popup_content, {maxWidth:maxWidthPopupBox}) 
178    circle.bindPopup (popup_content, {autoPan:true})
179    circle.bindPopup (popup_content, {autoPanSpeed:1})
180    circle.bindPopup (popup_content, {autoPanPadding:[100,250]}) ; 
181  }  
182    
183  var inc_counts = 1 ;
184  if (index == -1) // white circle
185  { inc_counts = 0 ;}  
186
187  if (index != -1)
188  {      
189    if ((type == text_tree) && (layer_trees != null))
190    { circle.addTo (layer_trees) ;   count_trees += inc_counts ; }
191    if ((type == text_shrub) && (layer_shrubs != null))
192    { circle.addTo (layer_shrubs) ;  count_shrubs += inc_counts ; }
193    if (ispublic && (layer_public != null))
194    { circle.addTo (layer_public) ;  count_public += inc_counts ; }
195    if (! ispublic && (layer_private != null))
196    { circle.addTo (layer_private) ; count_private+= inc_counts ;}
197    if (((tags.context != null) && (tags.context == 'park')) ||
198       ((tags.data_from != null) && (tags.data_from == 'osm')))
199    { circle.addTo (layer_parks) ; count_parks += inc_counts ;}
200    if ((tags.context != null) && (tags.context == 'street')) 
201    { circle.addTo (layer_streets) ; count_streets += inc_counts ;}
202
203    if (split_trees_on_age) // only for huge trees collections (Amsterdam)
204    {
205      if (planted != null)
206      {
207        if (planted < 1940) 
208        { circle.addTo (layer_old)   ; count_old   += inc_counts ;}
209        else 
210        { circle.addTo (layer_young) ; count_young += inc_counts ;}
211      }
212    } 
213  }
214  else
215  {
216    if (layer_blink != null)
217    { circle.addTo (layer_blink) ; }
218  }      
219  
220  if (index == -1) // white circle, no further processing needed
221  {
222    //circle.radius_base = radius ;
223    return (circle) ;
224  }  
225
226  //if (tree_count2 % 10000 == 0)
227  //{ addTimeline ('Circles drawn: ' + tree_count2) ; }
228
229  tree_count2 ++ ;
230  
231  
232
233  circle.radius_base = radius ;
234  tags.radius_base   = radius ;
235  return (circle) ;
236    
237  //.on("click", circleClick);
238  //circle.popup_content = popup_content ;  
239
240  //.on('mouseenter', function (e) {   markTaxon (genus)} );
241  //.bindTooltip (hover_content,{permanent:false, offset:L.point({x:0,y:-50})}) 
242  
243  //map.on("popupopen", function(e) 
244  //{ 
245  //   console.log ('event popupopen') ;  
246  //  alert ('popupopen');$("#"+myid+" img").one("load", function(){ e.popup.update(); })}); // !!! hook popup open
247  //https://gis.stackexchange.com/questions/91355/leaflet-center-marker-and-popup-on-map-viewport
248  
249  /*
250  if (0) // adjust window position 
251  {
252    map.on('popupopen', function(e) 
253    {
254      // console.log ('event popupopen') ;  
255      // find the pixel location on the map where the popup anchor is
256      var px = map.project(e.popup._latlng);
257      // find the height of the popup container, divide by 2 to centre, subtract from the Y axis of marker location
258      px.y -= e.popup._container.clientHeight/2;
259      // pan to new center
260      map.panTo(map.unproject(px),{animate: true, duration: 2});
261    });
262  }
263  */
264}
265
266function countCircles (tags)
267{
268  var planted = tags.planted ;
269
270  planted_decennium = -1 ; 
271  //console.log (crlf, 'countCircles planted: ', planted) ;
272  
273  if (! planted)
274  { 
275    planted = tags.plantjaar ; 
276    if (! planted)
277    { 
278      //console.log ('tags.planted & tags.plantjaar -> undefined') ;
279      planted = '----' ;
280    }
281  }    
282
283  
284  if (planted != '----')
285  {  
286    planted = planted.replace (/\'/g, ''); 
287    //console.log (crlf + 'countCircles planted: ', planted) ;
288
289    if (planted != Number (planted))
290    {
291      console.log ('Make numeric', planted) ; 
292      planted = Number (planted) ; 
293      console.log ('Made numeric?', planted) ; 
294    }
295    if (isNaN (planted))
296    { 
297      console.log ('Not a number:', planted) ;
298      planted = '----' ; 
299    } // probably invalid year
300    else if (planted < 1800)
301    { 
302      //console.log ('Older than 1800', planted) ;
303      planted = '----' ; 
304    }
305    else
306    { planted_decennium = planted - planted % 10 ; }
307
308    //console.log ('planted_decennium:', planted_decennium) ;  
309    //  console.log ('now newest_year:',newest_year, ', planted:',planted) ;
310		
311  	if ((planted > newest_year) && (planted <= this_year)) 
312    { newest_year = planted ; }    
313    //if (planted < 1800)
314    //{ console.log ('id:',id,'parseTagsMetrics age < 1800: ' + planted) ; }    
315
316    planted = planted - 1800 ; 
317 
318    planted_year_known ++ ;
319  }
320
321  if (! trees_planted [planted])
322  { trees_planted [planted] = 1 ; }    
323  else
324  { trees_planted [planted]++ ; } 
325 
326  //console.log ('planted:',planted,planted_decennium) ;
327
328  if (! trees_planted_decennium [planted_decennium])
329  { trees_planted_decennium [planted_decennium] = 1 ; }    
330  else
331  { trees_planted_decennium [planted_decennium]++ ; } 
332}
333
334function addTextMarkerToMap (circle,hover_content,popup_content,species_sc)
335{
336  var genus  = '' ;
337  var family = '' ;
338  var font = "<font class='tree-labels' color=black style='text-align:right;font-weight:500;'>" ;   
339  var code ;
340  // add decluttering code ? see : https://github.com/MazeMap/Leaflet.LayerGroup.Collision
341
342  labels_count++ ;
343  if (labels_count > 10000)
344  {
345    alert ('Too many trees to show labels for all (performance issue)') ;       
346  //show_text_markers = false ;      // 2024_05_03 now also shown text markers on large cities, but not all
347  }
348            
349  var name = species_original [species_sc] ;
350
351  //console.log ("addTextMarkerToMap:'",show_text_markers_mode,"'",show_text_markers_local) ;
352  if (show_text_markers_mode == show_text_markers_local)
353  {
354    name = taxonLocalName (ui, species_sc) ;
355    if (name != null)
356    { 
357      name = name.replace (/undefined/ig,'').replace (/\?/g,'').replace (/~/g, "'") ; 
358      name = '&nbsp;' + ucfirst (name) + '&nbsp;';
359			name = name.replace (/ /g, "&nbsp &nbsp;") ;
360      font = "<font class='tree-labels' color=#660000 style='text-align:right;font-weight:900;font-style:italic;'>" ;   
361      // these labels with white background and position to the right works only on some maps
362      //font = "<font class='tree-labels' color=#660000; style='font-size:10px;background-color:white; text-align:right;font-weight:200;font-style:italic;line-height:1'>" ;   
363    }
364  }      
365
366  if ((name == null) || onlySpaces (name))
367  {    
368    font = "<font class='tree-labels' color=black style='text-align:right;font-weight:500;'>" ;   
369
370    if (mode_arboretum) // only show one letter for family 
371    {
372      font = "<font class='tree-labels-large' color=black>" ;   
373      
374      genus = species_sc.replace (/_.*$/,'') ;
375      family = db [sc][family_name][genus] ;     
376      if (family != null)
377    //{ name = ucfirst (family.substring (0,1)) ; }
378      { name = family_codes [family] ; }
379      else
380      { name = 'X' ; 
381        family = '?' ;
382      }        
383      //console.log ('species_sc: ',species_sc,', genus: ', genus, ', family: ', family, ', name: ', name) ;
384    }  
385    else
386    {   
387      name = species_sc.replace (/_/g, ' ') ; // no local name -> use scientific name
388      if (name != null)
389      { 
390        if ((name.indexOf (' × ') > -1) ||
391            (name.indexOf (' x ') > -1))
392        { name = name.replace (/ [×x]/, '&nbsp;×').replace (/,.*$/g, '') ; }
393        else 
394        { name = name.replace (/ /, ' ').replace (/,.*$/g, '') ; }
395      } 
396      else
397      { name = '??' ; }
398      name = ucfirst (name) ;
399    }
400  }  
401  
402  var delta_y = 30 ; //35 ;
403  if (/ .* /.test (name) ) // 3 or more separate parts in name?
404  { delta_y = 35 ; } // 40    
405//name = name.toUpperCase ().substring (0,3) + '<br>99' ;  
406
407  var center = circle.getLatLng();  
408  
409  name = name.replace (/\~/g,"'") ;
410  
411  name = name.replace (/ ×/,'&nbsp;×') ;
412
413  // if this option is specified , a threshold in centimeters can be added: e.g. stem=20
414  if (show_stem_circumference)
415  {
416    if (typeof tags.circumference != 'undefined')
417    { tags.circumference = tags.circumference.replace (/[^\.\d].*$/,'') ; }
418
419    if ((typeof tags.diameter != 'undefined') && (tags.diameter > show_stem_threshold / 100))
420    { tags.circumference = tags.diameter * 3.14 ; }
421    if ((typeof tags.circumference != 'undefined') && (tags.circumference > 3.14 * show_stem_threshold / 100)) 
422    { name = name + '<br><b><font color=blue size=+1>' + Number (tags.circumference).toFixed (2) + '</font></b>'; }
423    else
424    { name = '' ;  }  
425  }
426  
427  // set font size at layout.css .leaflet-container 
428  var marker = (L.marker (center, 
429                         {icon: L.divIcon (
430                            {
431                              iconSize: [1,1],
432                            //iconAnchor: [-20 - circle.options.radius * 3, delta_y],
433                              iconAnchor: [0, delta_y],
434                              className: 'label',
435                              //  html: "<div>" + font + "<p style='line-height:0.7'>
435" + name + "</span></font></div>"  // ; margin-top:0px
436                                html: "<div>" + font + "<p style='font-size: 8pt;line-height:0.9'>" + name + "</span></font></div>"  // ; margin-top:0px
437                              }), 
438                            species_sc: species_sc
439                         },
440                         ).addTo(map));                                    // https://github.com/Leaflet/Leaflet.label/issues/146
441                         //}).bindTooltip (hover_content,).addTo(map));     // https://github.com/Leaflet/Leaflet.label/issues/146
442                                                                          // https://stackoverflow.com/questions/48872298/add-static-labels-to-circle-markers-in-leaflet    
443  //console.log (marker.options.species_sc) ; !!! use to switch local <-> scientific name
444                                                                          
445  if (popup_content != '')
446  { 
447    marker.bindPopup   (popup_content, {maxWidth:maxWidthPopupBox}) 
448    marker.bindPopup   (popup_content, {autoPan:true})
449    marker.bindPopup   (popup_content, {autoPanSpeed:1})
450    marker.bindPopup   (popup_content, {autoPanPadding:[100,250]}) ; 
451    marker.bindTooltip (popup_content, {direction: 'center'});   
452  }  
453
454  markers_trees.addLayer (marker) ;
455  if (! map.hasLayer (markers_trees)) 
456  {  map.addLayer (markers_trees) ; }
457
458  species_trees.push (marker) ;
459}
460
461function addTextmarkersToNearCircles (map_center)
462{
463	if ((! show_text_markers_some) && (! show_text_markers))
464	{ return ; }
465
466  if (typeof map_center === 'undefined')
467	{ return ; }
468		
469  var zoom = Math.round (100 * map.getZoom ()) / 100  ;
470  if (zoom < 19)
471	{ return ; }
472
473  labels_count = 0 ;
474
475  markers_trees.clearLayers () ;
476//	if (map.hasLayer(species_trees)) 
477//  { map.removeLayer (species_trees) ;	}	
478
479  var count = 0 ;
480
481  for (var circle of circles_trees) // was circles_parks 
482  { 
483    if (circle.options.type == text_tree)
484		{	
485  		var lat = circle.getLatLng().lat ;
486      var lon = circle.getLatLng().lng ;
487		
488		  if ((Math.abs (lat - map_center.lat) < 0.0020) &&
489		      (Math.abs (lon - map_center.lng) < 0.0020))
490      { 
491        count ++ ;
492        addTextMarkerToMap (circle, '', '', circle.options.species_sc) ; 
493 	    }
494		}	
495	}
496	
497	if (count > 0)
498	{ console.log ('addTextmarkersToNearCircles markers added : ' + count) ; }
499}
500
501function addTextMarkerAtPosition (point, text, count)
502{
503  var marker = L.marker (point, 
504                         {icon: L.divIcon (
505                                {
506                                  iconSize: null,
507                                  className: 'label',
508                                  html: '<div>
508' + text + '</div>' 
509                                })
510                         }) ;
511  if (count >= threshold_large_parks)
512  { marker.addTo (map) ; }
513  //alert ('addTextMarkerAtPosition') ;
514  return (marker) ;
515}
516
517function addLargeTextMarkerAtPosition (point, text, count)
518{
519  
520  var marker = L.marker (point, 
521                         {icon: L.divIcon (
522                                {
523                                  iconSize: [0,0],
524                                  iconAnchor: [0,0],
525                                  popupAnchor: [0,0],
526                                  className: 'submit',
527                                //html: "<input type='submit' value=' " + text + " ' style='color:green; margin:0px; padding: 20px'>" 
528                                  html: "<input type='text' value='" + text + "' style='color:green; margin-right:0px; padding-right:10px;'>" 
529                                })   
530                         }) ;
531  if (count >= threshold_large_parks)
532  { 
533    marker.addTo (map) ; 
534    markers_parks.push (marker) ;
535  }
536  
537  //alert ('addTextMarkerAtPosition') ;
538  return (marker) ;
539}
540
541function showMarkedCirclesOnMap () 
542{
543  var count = 0 ;
544  // console.log ('circles_marked.length:      ',circles_marked.length) ;
545  for (var layer of circles_marked)
546  { 
547    count++ ; 
548    map.addLayer (layer) ; 
549  }
550
551  //console.log (timeStamp (), count + ' circles added to map');
552  
553  return (count) ;
554    
555//  circles_marked.length = 0 ;
556}
557
558function blinkMarkedCircles ()
559{
560  var msec_start = performance.now () ;
561
562  var count ;  
563  //console.log ('blinkMarkedCircles ()') ;
564  
565  if (do_mark)
566  { 
567    blink_cycles++ ;
568    count = showMarkedCirclesOnMap () ; 
569    
570    if (blink_cycles <= 1)
571    { console.log ('showMarkedCirclesOnMap ():      ' + count + 'x') ;  }
572  }      
573  else
574  { count = hideMarkedCirclesFromMap () ;
575  //console.log ('hideMarkedCirclesFromMap (): ' + count + 'x') ; 
576  } 
577
578  do_mark = ! do_mark ;    
579
580  if (count > 0)
581  { timer_blink_taxon = setTimeout (function(){ blinkMarkedCircles () ; }, interval_blink); }
582
583  reportStatsMarkedCirclesUpdate (msec_start, count) ;
584}
585
586// returns boolean for success 
587function createCirclesForTaxon (taxon)  
588{
589  var zoom = Math.round (100 * map.getZoom ()) / 100  ;
590  var weight ;
591
592  blink_cycles = 0 ;
593  old_radius_modifier   = 0 ;
594  do_mark = true ; // always begin with adding circles, not removing them
595  
596  var color_border_blink = '' ;
597  
598  circles_marked.length = 0 ; // delete old circles
599
600  var msec_animation_started = performance.now () ;
601  var found = 0 ;
602  var match = false ;
603  var ref ;
604
605  // make sure both taxon_original (with spaces and quotes) and internal format can be handled here
606  var taxon2 = taxon.replace (/ /g, '_') ;
607  console.log ('createCirclesForTaxon (taxon=' + taxon + '/ taxon2=' + taxon2 + ')') ;
608  
609  var genus = taxon2.split ('_') [0].toLowerCase () ;
610  
611  
612  var color_fill = genera_color [genus] ; 
613
614  if ((color_fill == null) || (color_fill == ''))
615  { alert ('genus: ' + genus) ; }      
616//console.log (genus, color_fill) ;
617
618  taxon = taxon.replace (/×/g, "x").toLowerCase () ;
619  
620  var radius_modifier = calcRadiusModifierBasedOnZoomlevel (1) ;
621  if (zoom <= 16) // here number of marked circles is not yet known
622                  // so make circles even easier to detect on low zoom levels is not applicable
623  { radius_modifier *= 2 ; }
624  
625       if (zoom >= 17) { weight = 0.6 ; }
626  else if (zoom >= 15) { weight = 0.4 ; }
627  else if (zoom >= 13) { weight = 0.2 ; }
628  else                 { weight = 0.1 ; }   
629
630  for (var key in data.elements) // https://stackoverflow.com/questions/9329446/for-each-over-an-array-in-javascript
631  { 
632    tree = data.elements [key] ; 
633    //console.log ('key:',key,', tree:', tree) ; // 2022_05_15
634
635  //if (tree && tree.tags && tree.tags.species)   // instead use optional chaining? is it widely enough supported in 2022?
636  //{ species = tree.tags.species.replace (/'/g,'~').toLowerCase () ; }
637  //else { continue ; }
638
639    if (tree && tree.tags && tree.tags.species_sc)   // instead use optional chaining? is it widely enough supported in 2022?
640    { species = tree.tags.species_sc ; }
641    else { continue ; }
642
643    var find_genus = (taxon2.indexOf ('_') == -1) ;
644    // .indexOf (...) == 0, means string should begin with ...
645    if (find_genus)
646    { match = (species.indexOf (genus) == 0) ; } //console.log ("ACTL genus: '" + species + "', taxon: '" + taxon + "': " +
647                                                 //            ((match == true)? 'match' : 'nope') + crlf) ; 
648    else
649    { match = (species.indexOf (taxon) == 0) ; } //console.log ("ACTL match taxon '" + taxon + "' and '" + species + "' <- '" +
650                                                 //             tree.tags.species + "': " + ((match == true)? 'match' : 'nope')) ; 
651    //if (species.indexOf ('quercus') > -1)
652    //{ console.log ('test_string:',species,', pattern:',taxon, ', match:',match) ; }        
653
654
655    if (match)                                   
656    { 
657      found ++ ;
658
659      /*
660      radius = tree_radius [tree.id] ;
661      if (radius == null)
662      { radius = radius_unknown_tree ; }
663      */
664      radius = tree.tags.radius_base * radius_modifier ;
665      // some species that match may be tree other shrubs, 
666      // also db [sc][object_type][taxon] uses internal format, here external format is tested for match
667
668      type = tree.tags.natural ;
669
670      if (type == null)
671      { type = text_tree ; }
672    
673      if (type == text_tree)
674      {
675        color_border_blink = (zoom < 17) ? '#FF0000' : '#000000' ;  
676        if (index == -1) // white circle
677        { color_border_blink = '#000000' ; color_fill   = '#888888' ; }  // CCCCCCCCCC find this line 
678      }
679      
680      if (type == text_shrub)
681      { 
682        color_border_blink = (zoom < 17) ? '#FF0000' : '#FFFFFF' ;  
683        if (index == -1) // white circle
684        { color_border_blink = '#000000' ; color_fill   = '#888888' ; }
685      }  
686      
687      ref  = tree.tags.admin_ref ;
688      if (ref == null)
689      { ref = tree.tags.reference ; }
690
691    //color_border_blink = hue_shift_bright (color_fill,180) ;
692      color_fill2 = hue_shift_bright (color_fill,180) ;
693			color_fill2 = color_fill ;
694
695      if (color_fill == '#FF0000')
696			{ color_fill = '#FFFFFF' ; }
697	    else	
698			{ color_fill = '#FF0000' ; }
699			color_fill = '#FFFFFF' ; 
700		
701			fill_opacity = 0 ;
702      color_border_blink = '#FFFFFF' ;
703			color_fill_blink = '#FF0000' ; 
704			
705			//if (touch_device)
706			//{ color_fill2      = '#FF00FF' ; }
707      //else
708			//{ color_fill2      = '#000000' ; }
709			var color_line = '#FFFF00' ;
710      color_fill2 = '#000000' ;
711      circle = L.circle ([tree.lat, tree.lon],
712      {
713        id          : tree.id,
714        ref         : ref,
715        radius      : radius / 2,
716        radius_base : radius,
717        color : '#FFFF00', // color_line,
718        color_border_blink,
719        fillColor   : color_fill2,
720        fillOpacity : 1,
721        weight      : 4, // weight,
722        //dashArray   : '2,2',
723      }) ;
724    
725      // radius -= 0.3 ;
726      
727      circles_marked.push (circle) ; 
728      circle.addTo (map) ;
729      circle.bringToFront () ;
730    }
731
732    // return if exceededAnimationRuntime () ;
733  }
734
735  old_radius_modifier = radius_modifier ;
736  el_navbar_stop_blinking.style.display = 'block' ;
737
738  
739  return (found) ;
740}
741
742// returns boolean for success 
743function createCirclesForPlantYearOrPark (year, range, park)  
744{
745  var new_radius_modifier = calcRadiusModifierBasedOnZoomlevel (1) ;
746  if (zoom <= 16) // here number of marked circles is not yet known
747                  // so make circles even easier to detect on low zoom levels is not applicable
748  { new_radius_modifier *= 2 ; }
749  var weight = adjustWeight (new_radius_modifier) ;
750
751  var planted ;
752  var color_fill2 ;
753  var age ;
754  
755  blink_cycles = 0 ;
756  old_radius_modifier   = 0 ;
757  do_mark = true ; // always begin with adding circles, not removing them
758  
759  var color_border_blink = '' ;
760  
761  circles_marked.length = 0 ; // delete old circles
762
763  var msec_animation_started = performance.now () ;
764  var found = 0 ;
765  var match = false ;
766  var ref ;
767
768  age = newest_year - year ;
769
770  color_fill = colorByYear (year, color_circles_by_year_range) ; 
771  color_fill2 = hue_shift_bright (color_fill,45) ; // shift hue by 1/8 of color circle
772
773  for (var key in data.elements) // https://stackoverflow.com/questions/9329446/for-each-over-an-array-in-javascript
774  { 
775    tree = data.elements [key] ; 
776    planted = 0 ;
777    
778    if (! tree || ! tree.tags)   // instead use optional chaining? is it widely enough supported in 2022?
779    { continue ; }
780    
781    if (park == '')
782    {  
783      planted = tree.tags.planted ; 
784      if (isNaN (planted))
785      { planted = tree.tags.plantjaar ; }          
786      if (isNaN (planted) || (planted < 1800))
787      { continue ; }          
788    
789      if (range == 999) // test for decennium
790      { planted = planted - planted % 10 ; }     
791
792      found ++ ;
793      markCircle (tree, new_radius_modifier, color_fill2, weight) ;      
794    }  
795    else
796    {
797      if ((! tree.tags.place) || (tree.tags.place.indexOf (park) == -1))
798      { continue ; }
799
800      found ++ ;
801      tot_lat += tree.lat ;
802      tot_lon += tree.lon ;
803      tot_coords ++ ;
804      markCircle (tree, new_radius_modifier, '#FF0000', weight) ; 
805    }
806  }
807
808  old_radius_modifier = new_radius_modifier ;
809  el_navbar_stop_blinking.style.display = 'block' ;
810  var avg_lat = tot_lat / tot_coords ;
811  var avg_lon = tot_lon / tot_coords ;
812  doMapSetView (new L.LatLng(avg_lat, avg_lon), map.getZoom ()); // upd_map
813//  reportCircleCount () ;
814  return (found) ;
815}
816
817function markCircle (tree, radius_modifier, color_fill, weight)
818{
819  ref  = tree.tags.admin_ref ;
820  if (ref == null)
821  { ref = tree.tags.reference ; }
822
823  radius = tree.tags.radius_base * radius_modifier ;
824  var circle = L.circle ([tree.lat, tree.lon],
825  {
826    id          : tree.id,
827    ref         : ref,
828    radius      : radius,
829    radius_base : radius,
830    color_border_blink,
831    fillColor   : color_fill,
832    fillOpacity : 1,
833    weight      : weight,
834    //dashArray   : '2,2',
835  }) ;
836
837  // radius -= 0.3 ;
838  
839  circles_marked.push (circle) ; 
840  circle.addTo (map) ;
841  circle.bringToFront () ;
842}
843
844function reportCircleCount ()
845{
846  var count = 0;
847  map.eachLayer (function(layer) 
848  {
849    // check if the layer has any objects
850    if (typeof layer.getLatLng === 'function') 
851    { count++; }
852    else if (typeof layer.getLayers === 'function') 
853    { // layer is a group of markers or circles
854      count += layer.getLayers().length;
855    }
856  }) ;
857  alert (count) ;
858}
859
860function adjustWeight (radius_modifier)
861{
862  var zoom = Math.round (100 * map.getZoom ()) / 100  ;
863  
864       if (zoom >= 17) { weight = 0.6 ; }
865  else if (zoom >= 15) { weight = 0.4 ; }
866  else if (zoom >= 13) { weight = 0.2 ; }
867  else                 { weight = 0.1 ; }   
868
869  return (weight) ;
870}
871
872function reportStatsMarkedCirclesUpdate (msec_start, found)
873{
874//console.log ('reportStatsMarkedCirclesUpdate (msec_start:',msec_start,', found:',found) ;
875  if (color_msec_elapsed == '#AAAAAA')
876  { color_msec_elapsed = '#888888' ; }      
877  else
878  { color_msec_elapsed = '#AAAAAA' ; }      
879
880  if (color_stop_blinking == '#CCCC00')
881  { color_stop_blinking = '#FFFF00' ; }      
882  else
883  { color_stop_blinking = '#CCCC00' ; }      
884
885  el = el_navbar_stop_blinking ;
886  el.style.display = 'block' ;
887  el.style.color = color_stop_blinking ;
888
889  var msec_animation_elapsed = performance.now () - msec_start ;
890  var element = Element('msec_animation_elapsed') ;
891  if (element)
892  { element.innerHTML = '<small><small><font color=' + color_msec_elapsed + '>' + found + ' trees<br>' + msec_animation_elapsed + ' msec</font></small></small>' ; }
893}  
894
895function exceededAnimationRuntime () 
896{
897  if (checked % 250 == 0)
898  {
899    var msec_animation_elapsed = performance.now () - msec_animation_started ;
900    if (msec_animation_elapsed > 1000)  
901    {
902      hideMarkedCirclesFromMap () ;
903        
904      if (element)
905      { element.innerHTML = "<font color=white><small><small></b>Too many trees</b><br>" + 
906                            "animation cancelled<br> after " + msec_animation_elapsed +  
907                      " msec</small></small></font>" ; }
908
909      return true ;
910    }  
911  }       
912  return false ;
913}
914
915function findCircleById (id)
916{
917  for (var circle of circles_trees)
918  { 
919    if ((String (circle.options.id) .indexOf (id) > -1) ||
920        (String (circle.options.ref).indexOf (id) > -1)) 
921    { 
922      circle.setRadius (2) ;
923      circle.options.weight = 3 ;
924      mark_circle = circle ;
925      
926      doMapFlyTo (circle._latlng, 19) ;
927
928      return ;
929    }
930    //console.log ('circle: ', circle.options.id, circle.options.ref) ; 
931  }
932  alert (ui_text_find_tree_failed + ' ' + id) ;
933}
934
935function jumpToPark (content)
936{
937  var index = content; 
938  index = index.replace (/>.*$/gm,'') ; // '<x123>abc' -> '<x123' ;
939  index = index.replace (/[^\d]/gm,'') ; // '<x123' -> 123
940
941  if (! index)
942  { 
943    console.log ("jumpToPark: no index '" + index + "'") ; 
944    return (index) ;
945  }    
946
947  var tree = data.elements [index]; 
948  if (tree == null)
949  { 
950    console.log ("jumpToPark: no tree data for index '" + index + "'") ; 
951    return (index) ;
952  } 
953
954  var type = tree.tags.place ;
955  
956  console.log ('jumpToPark place:', place) ;
957  
958  showing_all_parks = false ;
959}
960
961function markTreesInPark (park)
962{
963  showing_one_park  = true ;    
964  showing_all_parks = false ;
965  el_header_h1.innerHTML = titleCase (park) ;
966//el_navbar.style.height = '60px' ;
967
968  park = park.replace (/^\s*/,'') ;
969  park = park.replace (/\s*$/,'') ;
970
971  console.log (crlf + "markTreesInPark + '" + park + "'") ;
972
973  var count = 0 ;
974  var tot_lat = 0 ;
975  var tot_lon = 0 ;
976  var tot_coords = 0 ;
977  var min_lat =  99999 ;
978  var max_lat = -99999 ;
979  var min_lng =  99999 ;
980  var max_lng = -99999 ;
981  
982  /*
983  layer_parks.eachLayer (function(layer) 
984  {
985      console.log ('layer.context:',layer.context) ;
986  });  
987  */
988
989  for (var circle of circles_trees)
990  { 
991  //if ((circle.options.place.indexOf (park) > -1) ||
992  //   (((file_json.indexOf ('leiden') > -1) &&
993  //    (park.match (singelpark_park_names))))) 
994    if (circle.options.place.indexOf (park) > -1)
995    { 
996      count ++ ;
997      var lat = circle.getLatLng().lat ;
998      var lon = circle.getLatLng().lng ;
999      tot_lat += lat ;
1000      tot_lon += lon ;
1001      tot_coords ++ ;
1002      if (lat < min_lat) { min_lat = lat ; }        ;
1003      if (lat > max_lat) { max_lat = lat ; }        ;
1004      if (lon < min_lng) { min_lng = lon ; }        ;
1005      if (lon > max_lng) { max_lng = lon ; }        ;
1006
1007      //circle.options.weight = circle.options.weight + 1 ;
1008      //circle.fillColor = '#FF0000' ;
1009      //circle.redraw () ;
1010      circle.setStyle({fillColor: 'green'});
1011    }
1012    else
1013    {
1014    //  circle.setStyle({fillColor: 'white'});
1015    //  circle.setStyle({color: circle.options.color2});
1016      circle.setStyle({opacity: 0.0});
1017    }      
1018    //console.log ('circle: ', circle.options.id, circle.options.ref) ; 
1019  }
1020  //console.log ('min/max lat/lng', min_lat, max_lat, min_lng, max_lng);
1021if (1)
1022{  
1023  map.fitBounds ([
1024    [max_lat + 0.0001, min_lng - 0.0001], // max_lat extra to deal with panel overlapping
1025    [min_lat - 0.0001, max_lng + 0.0001]
1026  ]);
1027}  
1028  //showArea2 (max_lat, min_lat, max_lng, min_lng) ; // place red dots around area, only for debugging
1029
1030  //console.log (crlf + "map.fitBounds ([" + max_lat +',' + min_lng + "],[" + min_lat + ',' + max_lng + "])") ;
1031
1032  if (count == 0)
1033  { 
1034    alert ("Park '" + titleCase (park) + "' niet gevonden.") ;
1035    return ;
1036  }
1037  
1038  timer_reset_tree_colors = setTimeout (function(){ unmarkTreesInPark (park) ; }, interval_unmark_trees); 
1039
1040  var avg_lat = tot_lat / tot_coords ;
1041  var avg_lon = tot_lon / tot_coords ;
1042//x  map.setView (new L.LatLng(avg_lat, avg_lon), map.getZoom ());  // upd_map
1043  
1044  reportCountsOnePark (park) ;
1045
1046  /*
1047  if (touch_device)
1048  { toggleExpandParksList () ;}  
1049  else
1050  { toggleExpandParksList () ;}  
1051  */
1052//alert (count) ;
1053
1054/*
1055  el_navbar_header2.innerHTML = "<a href='#' alt='Toon alle parken' onclick='openPanelParks();return false' style='position:absolute;right:220px;'><font color=yellow>&nbsp;&nbsp;Terug =></font></a><img  id='tooptip124' class='icon' src='assets/park3.png' width=40 bgcolor=#00bb00 alt='Toon alle parken' onclick='openPanelParks();return false' style='position:absolute;right:175px;'>" ;
1056*/
1057//  el_navbar_header2.style.display = 'block' ; 
1058
1059}
1060
1061function markTreesAllParks ()
1062{
1063  console.log (crlf + "markTreesAllParks") ;
1064  text_list_topic = 'parken' ;  // can switch between paken <> plantenfamilies
1065
1066  var count = 0 ;
1067  var count_plantsoen = 0 ;
1068  var tot_lat_plantsoen = 0 ;
1069  var tot_lon_plantsoen = 0 ;
1070  var tot_lat = 0 ;
1071  var tot_lon = 0 ;
1072  var tot_coords = 0 ;
1073  var min_lat =  99999 ;
1074  var max_lat = -99999 ;
1075  var min_lng =  99999 ;
1076  var max_lng = -99999 ;
1077  var place ;
1078
1079  var points = [] ;
1080
1081  showing_all_parks = true ;
1082  select_trees_park = false ;
1083  circles_recolored = true ;
1084  
1085  //closeSidebar () ;
1086  easy_button_park_labels_state = 1 ;  
1087  
1088  //console.log ('blacklist_park_names:', blacklist_park_names) ;
1089  //console.log ('whitelist_park_names:', whitelist_park_names) ;
1090  for (var circle of circles_trees) // was circles_parks 
1091  { 
1092    place = circle.options.place ;
1093		//place = 'park' ;  // force content for debugging
1094    // console.log ('place:', place) ;
1095    if ((! place.match (blacklist_park_names)) &&
1096        (place.match (whitelist_park_names)))
1097    { 
1098      count ++ ;
1099      var lat = circle.getLatLng().lat ;
1100      var lon = circle.getLatLng().lng ;
1101
1102      //var marker = addLargeTextMarkerAtPosition ([lat,lon], place) ;
1103
1104      if (place.indexOf ('park') > -1)
1105      { 
1106        tot_lat_plantsoen += lat ;
1107        tot_lon_plantsoen += lon ;
1108        count_plantsoen ++ ;
1109        
1110        points.push ([lat, lon]) ; 
1111        //console.log (place, lat, lon) ;
1112      }        
1113
1114      tot_lat += lat ;
1115      tot_lon += lon ;
1116      tot_coords ++ ;
1117      if (lat < min_lat) { min_lat = lat ; }        ;
1118      if (lat > max_lat) { max_lat = lat ; }        ;
1119      if (lon < min_lng) { min_lng = lon ; }        ;
1120      if (lon > max_lng) { max_lng = lon ; }        ;
1121
1122      //circle.options.weight = circle.options.weight + 1 ;
1123      //circle.fillColor = '#FF0000' ;
1124      //circle.redraw () ;
1125      circle.setStyle({fillColor: 'green'});
1126    }
1127    else
1128    {
1129      //circle.setStyle({fillColor: 'red'});
1130      circle.setStyle({fillOpacity: 0.0,
1131                       opacity:     0.0}) ;
1132   }   
1133  }
1134  console.log ('All circles green or white') ;
1135  el_navbar_img_expand.src = 'assets/expand_up_blue.png' ;
1136  el_navbar_submit_hint.value = 'Verberg overzicht' ; 
1137  parks_list_showing  = true ;
1138
1139  
1140/*
1141    // Define the Leaflet icon
1142var myIcon = L.icon({
1143    iconUrl: 'assets/zoom_bw2.png',
1144    iconSize: [32, 32], // size of the icon
1145    iconAnchor: [16, 32], // point of the icon which will correspond to marker's location
1146    popupAnchor: [0, -32], // point from which the popup should open relative to the iconAnchor
1147    className: 'zoom_to_park' // class name for styling the icon
1148});
1149
1150// Define the Leaflet marker with the icon and text
1151var myMarker = L.marker([52.15734, 4.50356], {
1152    icon: myIcon,
1153    title: 'Groenesteeg'
1154}).bindPopup('Zoom to Groenesteeg').on('click',markTreesInPark ('Park'));
1155  
1156    //iconsMonument.push (L.marker([52.157174, 4.50387], {icon: iconMonument}) .addTo(map).addTo (markers_icons).
1157    //addTo (markers_icons2).on('click',markTreesInPark ('Park'))) ; 
1158
1159// Add the marker to the map
1160myMarker.addTo(map);
1161*/
1162
1163
1164
1165    //console.log ('circle: ', circle.options.id, circle.options.ref) ; 
1166
1167  //console.log ('min/max lat/lng', min_lat, max_lat, min_lng, max_lng);
1168  
1169  map.fitBounds ([
1170    [max_lat + 0.001, min_lng - 0.001], // max_lat extra to deal with panel overlapping
1171    [min_lat - 0.001, max_lng + 0.001]
1172  ]);
1173
1174  //if (count == 0)
1175  //{ 
1176  //  alert ("Park '" + titleCase (park) + "' niet gevonden.") ;
1177  //  return ;
1178  //}
1179    
1180  var avg_lat = tot_lat / tot_coords ;
1181  var avg_lon = tot_lon / tot_coords ;
1182
1183  //console.log ('tot_lat:', tot_lat, ', tot_lon:',tot_lon, ', tot_coords:', tot_coords) ;
1184  if (tot_coords == 0) // (! avg_lat) || (! avg_lon) )
1185  {
1186    console.log ("markTreesAllParks: No valid coordinates found -> avg_lat and avg_lon cannot be calculated") ;
1187		
1188    if (blacklist_park_names == '') 
1189		{ alert ("function markTreesAllParks: regexp var 'blacklist_park_names' is empty") ; }
1190    else 
1191		if (whitelist_park_names == '') 
1192		{ alert ("function markTreesAllParks: regexp var 'whitelist_park_names' is empty") ; }
1193    else 
1194		{ alert ("No park information found for any tree (field 'place' in json input)") ; }
1195  }  
1196
1197//  map.setView (new L.LatLng(avg_lat, avg_lon), map.getZoom ());  // upd_map
1198  var marker = new L.Marker(avg_lat, avg_lon);
1199
1200/*
1201//var polygon = L.polygon(turf.polygon ([points]),{          // error 'turf is not defined'
1202//var test = turf.polygon (points) ;
1203var polygon = L.polygon(points,{
1204  color: 'black',
1205  fillColor: 'red', // green',
1206  fillOpacity: 1, // 0.2,
1207  weight: 0.1,
1208  opacity: 0.7
1209});
1210
1211polygon.on('click', function(e) {alert('You clicked the polygon!');});
1212
1213// add the polygon to the map
1214polygon.addTo(map);
1215*/
1216  
1217  
1218  var avg_lat_plantsoen = tot_lat_plantsoen / count_plantsoen ;
1219  var avg_lon_plantsoen = tot_lon_plantsoen / count_plantsoen ;
1220
1221  Object.values(coords_per_park).forEach((park) => 
1222  {
1223    var marker = addLargeTextMarkerAtPosition ([park.avg_lat,park.avg_lon], titleCase (park.name) + " (" + park.count + ")", park.count) ;
1224
1225    if (park.count > threshold_large_parks)
1226    { buttons_parks_large.addLayer (marker) ; }
1227    else
1228    { buttons_parks_other.addLayer (marker) ; }
1229
1230    marker.on('click', function() 
1231    {
1232      marker.remove () ;
1233      markTreesInPark (park.name);
1234    });
1235  });
1236
1237  //setTimeout (function(){ unmarkTreesAllParks () ; }, 5000); 
1238/*
1239var marker = addLargeTextMarkerAtPosition ([avg_lat_plantsoen,avg_lon_plantsoen], "Plantsoen&nbsp;&nbsp;(" + count_plantsoen + ")") ; // , park.count) ;
1240marker.on('click', function() {
1241  openSidebar () ;
1242  marker.remove () ;
1243  markTreesInPark ('all');
1244});
1245console.log ('points1:', points) ;
1246  points.sort (function(a, b) 
1247  { return a.lng - b.lng; });
1248console.log ('points2:', points) ;
1249*/
1250  
1251  
1252//alert (count) ;
1253}
1254
1255function unmarkTreesAllParks ()
1256{
1257  unmarkAllTrees () ; // obsolete (remove?) : better to restore colors for all trees, not only in parks
1258  return ;
1259
1260  console.log (crlf + "unmarkTreesAllParks") ;
1261  
1262
1263  //closeSidebar () ;
1264  for (var circle of circles_trees) // was circles_parks 
1265  { 
1266    circle.setStyle({fillOpacity: 1,
1267                     color: circle.options.color2});
1268  }
1269  console.log ('All circle colors in parks are reapplied') ;
1270
1271  circles_recolored = false ;
1272  
1273  reportStatus (copy_to_thumbs, 'Verleg focus') ; 
1274}
1275
1276function unmarkTreesInPark (park)
1277{
1278  console.log (crlf + "unmarkTreesInPark '" + park + "'") ;
1279  for (var circle of circles_trees)
1280  { 
1281    if ((circle.options.place.indexOf (park) > -1) ||
1282        (((file_json.indexOf ('leiden') > -1) &&
1283        (circle.options.place.match (singelpark_park_names))))) 
1284    { 
1285      count ++ ;
1286    //circle.options.weight = circle.options.weight + 1 ;  // !!!!
1287      circle.setStyle({fillColor: circle.options.fillColor2, 
1288                       color:     circle.options.color2});
1289    }
1290  }  
1291}
1292
1293function unmarkAllTrees ()
1294{
1295  console.log (crlf + 'unmarkAllTrees') ;
1296  for (var circle of circles_trees)
1297  { 
1298    circle.setStyle ({fillColor: circle.options.fillColor2,
1299                      fillOpacity: 1,
1300                      opacity: 1});
1301  }
1302  console.log (crlf + 'unmarkAllTrees : all circle colors have been reapplied') ;
1303}
1304
1305function markTaxon (taxon, index_taxon)
1306{
1307  if (++console_log_mark_taxon < 50)
1308  { console.log (crlf + '***********'  + crlf + "markTaxon: '" + taxon + "', ndx: " + index_taxon + crlf + '***********') ; }
1309  
1310  if ((navbar.style.display == 'block') && (innerWidth < 800))
1311  { navbar.style.display = 'none' ; }	  
1312  
1313  log_mark_taxon = true ;
1314
1315  if (index_taxon_selected != index_taxon)
1316  { showTaxonSelected (taxon, index_taxon) ; }
1317  //showTaxonomy (taxon) ;
1318
1319  if (taxon_selected != prev_taxon)
1320  { blink_iterations = 0 ; }
1321
1322  prev_taxon = taxon ;
1323  taxon_selected = taxon ;
1324	  
1325  clearTimeout (timer_blink_taxon) ; 
1326
1327  hideMarkedCirclesFromMap () ;
1328  var trees_found = createCirclesForTaxon (taxon) ;
1329
1330  if (trees_found)	
1331  { 
1332    console.log ('Trees_found for taxon: ' + taxon + ' = ' + trees_found) ; 
1333    timer_blink_taxon = setTimeout (function(){ blinkMarkedCircles () ; }, interval_blink); 
1334  }
1335  else
1336  { console.log ('No trees found for taxon: ',taxon,'!!!!!') ; }
1337}
1338
1339function markYearPlanted (year, range)
1340{
1341  console.log ('markYearPlanted: ' + year + ',' + range) ;   
1342
1343  stopBlinking () ;
1344  const collection = document.getElementsByClassName("markYear" + year);
1345  for (let i = 0; i < collection.length; i++) 
1346  { collection[i].style.fontWeight = '900' ; }
1347  year_marked = year ;
1348  
1349//  if ((navbar.style.display == 'block') && (innerWidth < 800))
1350//  { navbar.style.display = 'none' ; }	  
1351  
1352  log_mark_taxon = true ;
1353
1354  if (index_taxon_selected != index_taxon)
1355  { showTaxonSelected (taxon, index_taxon) ; }
1356
1357//showYearPlanted (year) ;
1358
1359  if (taxon_selected != prev_taxon)
1360  { blink_iterations = 0 ; }
1361
1362  prev_taxon = taxon ;
1363  taxon_selected = taxon ;
1364	  
1365  clearTimeout (timer_blink_taxon) ; 
1366
1367  hideMarkedCirclesFromMap () ;
1368  var trees_found = createCirclesForPlantYearOrPark (year, range, '') ;
1369
1370  if (trees_found)	
1371  { 
1372    //console.log ('Trees marked: ' + trees_found) ; 
1373    timer_blink_taxon = setTimeout (function(){ blinkMarkedCircles () ; }, interval_blink); 
1374  }
1375  else
1376  { console.log ('No trees found for year: ',year,'!!!!!') ; }
1377}
1378
1379function hideMarkedCirclesFromMap () 
1380{
1381  var count  = 0 ;  
1382  for (var layer of circles_marked)
1383  { 
1384    count ++ ;
1385    map.removeLayer (layer) ; 
1386  }
1387
1388  //console.log (timeStamp(),count + ' circles removed from map') ;
1389  
1390  return (count) ;
1391
1392//  circles_marked.length = 0 ;
1393}
1394
1395function stopBlinking ()
1396{
1397  console.log ('stopBlinking') ;    
1398  clearTimeout (timer_blink_taxon) ; 
1399  hideMarkedCirclesFromMap () ;
1400  circles_marked.length = 0 ;
1401  el_navbar_stop_blinking.style.display = 'none' ;
1402  if (year_marked != 0)
1403  {
1404    const collection = document.getElementsByClassName("markYear" + year_marked);
1405    for (let i = 0; i < collection.length; i++) 
1406    { collection[i].style.fontWeight = '400' ; } // normal
1407    year_marked = 0 ;
1408  }
1409}
1410
1411function adjustCircleBorderToZoomLevel (radius_modifier) 
1412{
1413	return ;
1414  var zoom = Math.round (100 * map.getZoom ()) / 100  ;
1415  var weight, weight_shrub ;
1416  //console.log ('adjustCircleBorderToZoomLevel zoom:',zoom,', radius_modifier',radius_modifier) ;
1417  
1418       if (zoom >= 17) { weight = 0.6 ; }
1419  else if (zoom >= 15) { weight = 0.4 ; }
1420  else if (zoom >= 13) { weight = 0.2 ; }
1421  else                 { weight = 0.1 ; }   
1422//var new_radius_modifier = calcRadiusModifierBasedOnZoomlevel (2) ;
1423  circle_ndx = 0 ;
1424  for (var circle of circles_trees)
1425  { 
1426    circle.setRadius (circle.radius_base * radius_modifier) ; 
1427    circle.bringToFront () ;
1428    
1429    if ((circle.options ['color'] == "#FFFFFF") || 
1430        (circle.options ['color'] == "#ffffff"))
1431    { 
1432      if (circle.radius_base < 1)
1433      { weight_shrub = 1.2 ; }  
1434      else
1435      { weight_shrub = 1.5 ; }  
1436      
1437      circle.options ['weight'] = weight_shrub ; 
1438    }
1439    else
1440    { circle.options ['weight'] = weight ; }
1441
1442    if (circle.options ['dead'])
1443    { circle.options ['weight'] = 3 ; }
1444  }
1445}
1446
1447function adjustCircleRadiusToZoomLevel (radius_modifier)
1448{
1449// I am totally confused why this doesn't work 
1450// So do not alter circle radius and weight depending on zoom level
1451// It's quite allright without this cosmetic adjustment
1452
1453  return;
1454
1455  var zoom = Math.round (100 * map.getZoom ()) / 100 ;
1456	var weight, radius ;
1457  //new_radius_modifier = calcRadiusModifierBasedOnZoomlevel (3) ;
1458  if ((zoom <= 16) && (circles_marked.length <= 10)) // make circles even larger on low zoom levels
1459  //{ new_radius_modifier *= 2 ; }    
1460  { radius_modifier *= 2 ; }    
1461
1462  //console.log ('adjustCircleRadiusToZoomLevel zoom:',zoom,', radius_modifier',radius_modifier) ;
1463
1464       if (zoom >= 19) { weight = 0.1 ;  radius = 1.4 }
1465  else if (zoom >= 18) { weight = 0.1 ;  radius = 1.8 }
1466  else if (zoom >= 17) { weight = 0.1 ;  radius = 2 }
1467  else if (zoom >= 16) { weight = 0.1 ;  radius = 3 }
1468  else if (zoom >= 15) { weight = 0.2 ;  radius = 5 }
1469  else if (zoom >= 14) { weight = 0.2 ;  radius = 7 }
1470  else if (zoom >= 13) { weight = 0.3 ;  radius = 10 }
1471  else                 { weight = 0.4 ; radius = 15 }   
1472  weight = radius / 2 ;
1473
1474  console.log ('www adjustCircleRadiusToZoomLevel zoom ' + zoom + ', weight ' + weight + ', radius ' + radius) ;
1475  if (circles_marked.length > 0)
1476  { 
1477  //console.log ('radius_modifier old: ',round (old_radius_modifier,2),' ->
1477 new ',round (new_radius_modifier,2)) ;
1478    console.log ('radius_modifier old: ',round (old_radius_modifier,2),' -> new ',round (radius_modifier,2)) ;
1479 
1480 //   if (new_radius_modifier != old_radius_modifier)
1481 //   {    
1482      //old_radius_modifier = new_radius_modifier ;    
1483      old_radius_modifier = radius_modifier ;    
1484
1485      for (var circle of circles_marked)
1486      { 
1487        //circle.setRadius (circle.options.radius_base * new_radius_modifier) ; 
1488        //circle.setRadius (circle.options.radius_base * radius_modifier) ; 
1489        circle.setRadius (radius) ; 
1490        circle.bringToFront () ;
1491        //console.log ('circle.options.radius:',circle.options.radius,'radius_base',circle.radius_base) ;
1492    
1493      /*
1494      if ((circle.options ['color'] == "#FFFFFF") || 
1495          (circle.options ['color'] == "#ffffff"))
1496      { 
1497        if (circle.radius_base < 1)
1498        { weight_shrub = 1.2 ; }  
1499        else
1500        { weight_shrub = 1.5 ; }  
1501      
1502        circle.options ['weight'] = weight_shrub ; 
1503      }
1504      else
1505      { circle.options ['weight'] = weight ; }
1506  */
1507        circle.options ['weight'] = weight ; 
1508    //  }
1509    }
1510  }  
1511}
1512
1513function adjustMarkedCircle ()
1514{
1515  if (mark_circle != null) // from findCircleById
1516  {
1517    mark_circle.bringToFront () ;  
1518    mark_circle.setRadius (3) ;
1519    mark_circle.options.weight = 5 ;
1520  }
1521}
1522
1523// https://gis.stackexchange.com/questions/258515/show-hide-markers-depending-on-zoom-level
1524
1525function adjustTextMarkers ()
1526{
1527  var zoom = map.getZoom () ;
1528	if (show_text_markers || mode_arboretum)
1529  {   
1530    if (map.hasLayer(markers_trees)) 
1531    {
1532			// keep markers visible on touch screens even for zoom levels below 20 
1533			// the idea is lowering zoom level is essential on small screens, to find out where you are
1534			// also the texts will overlap more on lower zoom levels, but one can enlarge the screen temporarily with pinch-to-zoom gesture
1535			var show_markers_until_zoom = 19 ;
1536			var show_markers_from_zoom  = 19.5 ;
1537			if (small_touch_device)
1538			{ show_markers_until_zoom = 18.5 ; }
1539				
1540      if ((zoom < show_markers_until_zoom) && (! mode_arboretum))
1541      { 
1542        console.log ('adjustCircleRadiusToZoomLevel: map has layers markers_trees -> remove on zoom ' + zoom) ;  
1543        map.removeLayer (markers_trees) ; 
1544      }
1545      if (mobile_device) 
1546      { setThumbs ('&nbsp;&nbsp;&nbsp;' + '<small>' + ui_text_header_thumbs_mobile + '</small>') ; }
1547      else
1548      { setThumbs ('&nbsp; ' + ui_text_header_thumbs) ; }
1549    }
1550    else
1551    {
1552      if (zoom >= show_markers_from_zoom)
1553      { 
1554        console.log ('adjustCircleRadiusToZoomLevel: map does not have layers markers_trees -> add on zoom ' + zoom) ;  
1555        map.addLayer (markers_trees) ; 
1556        setThumbs ('&nbsp; ' + ui_text_header_explain_labels) ; 
1557      }
1558    }
1559  } 
1560}
1561
1562function adjustTreeLayersToZoomlevel (zoom) 
1563{  
1564  var threshold = 14 ;
1565  var element ;
1566  zoom = round (zoom, 1) ;
1567
1568  element = el_info_box ;
1569  if (! element)
1570  { return ; }
1571
1572  if (split_trees_on_age)
1573  {      
1574    if (map.hasLayer(layer_young) && (zoom < threshold))       
1575    { 
1576      console.log ('zoom ' + zoom + ' -> adjustTreeLayersToZoomlevel remove layer_young') ;
1577      
1578      if (++ count_msg_zoom_threshold <= 2) 
1579      { reportStatus (copy_to_thumbs, 'Zoom: ' + zoom + '<br>Toon alleen bomen met plantjaar tot 1940.<br>Vanaf zoom ' + threshold + 
1580      ' toon alle bomen.') ; }
1581      map.removeLayer (layer_young) ; 
1582      console.log ('1 map.removeLayer (layer_streets)') ; 
1583      
1584    }
1585    else 
1586    if (! map.hasLayer(layer_young) && (zoom >= threshold))       
1587    { 
1588      if (++ count_msg_zoom_threshold <= 3) 
1589      { reportStatus (copy_to_thumbs, 'Zoom: ' + zoom + '<br>Toon alle bomen vanaf zoom ' + threshold +  '. Daaronder alleen parken en plantsoenen.') ; }
1590      console.log ('zoom ' + zoom + ' -> adjustTreeLayersToZoomlevel add layer_young') ;
1591      map.addLayer (layer_young) ; 
1592      console.log ('2 map.addLayer (layer_streets)') ;
1593    }  
1594    else
1595    { reportStatus (! copy_to_thumbs, 'Zoom: ' + zoom) ; }
1596  }  
1597  else 
1598  if (split_trees_on_context)
1599  {
1600    if (map.hasLayer(layer_streets) && (zoom < threshold))       
1601    { 
1602      console.log ('zoom ' + zoom + ' -> adjustTreeLayersToZoomlevel remove layer_streets') ;
1603      if (++ count_msg_zoom_threshold <= 2) 
1604      { reportStatus (copy_to_thumbs, 'Zoom: ' + zoom + '<br>Toon alleen bomen in parken, plantsoenen tot zoom ' + threshold) ; }
1605      map.removeLayer (layer_streets) ; 
1606      console.log ('3 map.removeLayer (layer_streets)') ; ; 
1607    }
1608    else  
1609    if (! map.hasLayer(layer_streets) && (zoom >= threshold))       
1610    { 
1611      if (++ count_msg_zoom_threshold <= 3) 
1612      { reportStatus (copy_to_thumbs, 'Zoom :' + zoom + '<br>Toon alle bomen vanaf zoom ' + threshold + '.' + 
1613      ' Daaronder alleen parken en plantsoenen') ; }
1614      console.log ('zoom ' + zoom + ' -> adjustTreeLayersToZoomlevel add layer_streets') ;
1615      map.addLayer (layer_streets) ; 
1616      console.log ('4 map.addLayer (layer_streets)') ;
1617    }
1618    else
1619    { reportStatus (! copy_to_thumbs, '<center>Zoom: ' + zoom + '</center>') ; }
1620  }  
1621  else
1622  { 
1623    if (zoom == 12)
1624    { reportStatus (! copy_to_thumbs, '<center>Zoom: ' + zoom + ' (min)</center>') ; }  
1625    else 
1626    if (zoom < 20)
1627    { 
1628    	// do not show every zoom level on small screens, it is already in bottom-left corner
1629			// for really large screens that would not be noticable enough
1630			if (! small_touch_device)
1631	    { reportStatus (! copy_to_thumbs, '<center>Zoom: ' + zoom + '</center>') ; }
1632		}  
1633    else 
1634    if (! show_text_markers)
1635    { reportStatus (! copy_to_thumbs, '<center>Zoom: ' + zoom + ' (max)</center>') ; }      
1636    else
1637    { 
1638      count_msg_zoom_20 ++ ;
1639      if (count_msg_zoom_20 <= 1)
1640      { reportStatus (copy_to_thumbs, 'Zoom: ' + zoom + '. Toon soortnamen, alleen voor bomen') ; }
1641    }      
1642  }
1643}
1644
1645// range x is 'last x years'
1646// range 999 is 'all ages'
1647function colorCirclesByYearPlanted (range) 
1648{
1649  circles_recolored = true ;
1650    
1651  var count = 0 ;
1652  var count_unknown = 0 ;
1653  var color = '#000000' ;
1654  var count_plant_year = 0 ;
1655  var age ;
1656  var html_button1, html_button2, html_button3, html_button4 ;
1657  var oldest_year_shown = 9999 ;
1658  
1659  color_circles_by_year_range = range ;
1660  
1661  for (var circle of circles_trees)
1662  { 
1663    count ++ ;
1664    planted = circle.options.planted ;
1665    ref = circle.options.ref ;
1666    //console.log ('ref:', ref) ;
1667    if (isNaN (planted)) // ((planted == null) || (planted == '????') || (planted == '----'))
1668    { 
1669      if ((range == 0) && (! isNaN (ref)))   // only mark trees without plant year if municipal
1670      { circle.options.fillColor = '#FF0000' ; }
1671      else
1672      { circle.options.fillColor = '#FFFFFF' ; }
1673      count_unknown++ ;
1674    }
1675    else
1676    {        
1677      if (range == 0)
1678      { circle.options.fillColor = '#FFFFFF' ; }
1679      else
1680      { circle.options.fillColor = colorByYear (planted, range) ; }
1681    }    
1682  }  
1683
1684  var delta_i = 1 ;
1685  if (range == 999)
1686  { delta_i = 10 ; } 
1687
1688  var i0 = trees_planted.length - 1 ;
1689  if (range == 999)
1690  { i0 = i0 - i0 % 10 ; }
1691
1692  planted_year_in_range = 0 ; 
1693  html_planted_year = "<table width=100%>" + crlf ;
1694
1695  for (var i = i0 ; i > -1 ; i = i - delta_i)
1696  { 
1697    if (trees_planted [i] != null)
1698    { 
1699      year = i + 1800 ;
1700      age = newest_year - year ;
1701      if (age < range)
1702      {
1703        if (year < oldest_year_shown)
1704        { oldest_year_shown = year ; }  
1705        html_planted_year += htmlPlantedTableRow (year, range) ; 
1706      }                            
1707    }      
1708  }
1709  
1710  planted_year_out_range = planted_year_known - planted_year_in_range ;
1711  if (range < 999)
1712  { html_planted_year += "<tr><td bgcolor='#FFFFFF' border='1px'><div border=1px>&nbsp;</div></td><td>&lt;" + oldest_year_shown + "</td><td align=right>" + planted_year_out_range + "</td></tr>" ; }
1713
1714
1715  if (count_unknown > 0)
1716  { html_planted_year += "<tr><td bgcolor='#888888'>&nbsp;</td><td>----</td><td align=right>" + count_unknown + "</td></tr>" ; }
1717
1718  html_planted_year += "</table>" ;
1719  
1720  
1721  
1722  if (range==999)
1723  { 
1724    html_button1 = "<button" + 
1725                              " type='button' style='background-color:#888888' width=100%><b>" + 
1726                           ui_show_per_decade + "</b></button>" ; 
1727    html_button2 = "<button onclick='colorCirclesByYearPlanted(50);'" + 
1728                              " type='button' style='background-color:#00AA00' width=100%><b>" + 
1729                           ui_show_last_50_years + "</b></button>" ; 
1730    html_button3 = "<button onclick='colorCirclesByYearPlanted(15);'" + 
1731                              " type='button' style='background-color:#00AA00' width=100%><b>" + 
1732                           ui_show_last_15_years + "</b></button>"  
1733    html_button4 = "<button onclick='colorCirclesByYearPlanted(0);'" + 
1734                              " type='button' style='background-color:#00AA00' width=100%><b>" + 
1735                           ui_text_no_year + "</b></button>"  
1736  }
1737  else
1738  if (range==50)
1739  { 
1740    html_button1 = "<button onclick='colorCirclesByYearPlanted(999);'" + 
1741                              " type='button' style='background-color:#00AA00' width=100%><b>" + 
1742                           ui_show_per_decade + "</b></button>" ; 
1743    html_button2 = "<button onclick='colorCirclesByYearPlanted(50);'" + 
1744                              " type='button' style='background-color:#888888' width=100%><b>" + 
1745                           ui_show_last_50_years + "</b></button>" ; 
1746    html_button3 = "<button onclick='colorCirclesByYearPlanted(15);'" + 
1747                              " type='button' style='background-color:#00AA00' width=100%><b>" + 
1748                           ui_show_last_15_years + "</b></button>"  
1749    html_button4 = "<button onclick='colorCirclesByYearPlanted(0);'" + 
1750                              " type='button' style='background-color:#00AA00' width=100%><b>" + 
1751                           ui_text_no_year + "</b></button>"  
1752  }
1753  else 
1754  if (range == 15)
1755  { 
1756    html_button1 = "<button onclick='colorCirclesByYearPlanted(999);'" + 
1757                              " type='button' style='background-color:#00AA00' width=100%><b>" + 
1758                           ui_show_per_decade + "</b></button>" ; 
1759    html_button2 = "<button onclick='colorCirclesByYearPlanted(50);'" + 
1760                              " type='button' style='background-color:#00AA00' width=100%><b>" + 
1761                           ui_show_last_50_years + "</b></button>"  
1762    html_button3 = "<button onclick='colorCirclesByYearPlanted(15);'" + 
1763                              " type='button' style='background-color:#888888' width=100%><b>" + 
1764                           ui_show_last_15_years + "</b></button>"  
1765    html_button4 = "<button onclick='colorCirclesByYearPlanted(0);'" + 
1766                              " type='button' style='background-color:#00AA00' width=100%><b>" + 
1767                           ui_text_no_year + "</b></button>"  
1768  }
1769  else // range == 0
1770  { 
1771    html_button1 = "<button onclick='colorCirclesByYearPlanted(999);'" + 
1772                              " type='button' style='background-color:#00AA00' width=100%><b>" + 
1773                           ui_show_per_decade + "</b></button>" ; 
1774    html_button2 = "<button onclick='colorCirclesByYearPlanted(50);'" + 
1775                              " type='button' style='background-color:#00AA00' width=100%><b>" + 
1776                           ui_show_last_50_years + "</b></button>"  
1777    html_button3 = "<button onclick='colorCirclesByYearPlanted(15);'" + 
1778                              " type='button' style='background-color:#00AA00   ' width=100%><b>" + 
1779                           ui_show_last_15_years + "</b></button>"  
1780    html_button4 = "<button onclick='colorCirclesByYearPlanted(0);'" + 
1781                              " type='button' style='background-color:#888888' width=100%><b>" + 
1782                           ui_text_no_year + "</b></button>"  
1783  }
1784
1785  html_button5 = "<button onclick='location.reload();' type='button' style='background-color:#00AA00'><b>" + 
1786                           ui_show_normal_colors + "</b></button>" ; 
1787                           
1788  var html_buttons = "<table width=100%>" +
1789                     "<tr>
1789<td width=100%>" + html_button1 + "</td></tr>" +
1790                     "<tr><td>" + html_button2 + "</td></tr>" +
1791                     "<tr><td>" + html_button3 + "</td></tr>" +
1792                     "<tr><td>" + html_button4 + "</td></tr>" +
1793                     "<tr><td>" + html_button5 + "</td></tr>" +
1794                     "</table>" ;
1795
1796  showSortSequence (10, 'planted') ;
1797  el_sidebar.innerHTML = html_buttons + html_planted_year ;
1798  SetNavbarHeader (1, "<b>" + ucfirst (ui_text_planting_year) + "</b>") ; 
1799  openSidebar () ;
1800  repaintMap () ;
1801}
1802
1803function htmlPlantedTableRow(year, range)
1804{
1805  var entry = '' ;  
1806  var i = year - 1800 ;
1807  var count ;
1808  var decennium = 0 ;
1809
1810  if (range == 999)
1811  { 
1812    if (! isNaN (year))
1813    { 
1814      decennium = year ;
1815      if (decennium == 0)
1816      { console.log ('htmlPlantedTableRow:', year, range) ; }
1817      count = trees_planted_decennium [decennium] ; 
1818    }  
1819  }    
1820  else 
1821  { count = trees_planted [i] ; }    
1822
1823  planted_year_in_range += count ;
1824
1825  var color = colorByYear (year, range) ; 
1826  
1827  var year_label = year ;
1828  if (range == 999)
1829  { year_label = year_label + "'s" ; }   
1830  
1831  entry = "<tr><td bgcolor=" + color + ">&nbsp;</td>" + 
1832              "<td align=left>" + 
1833                         "<span class='markYear" + year + "' onclick=\"markYearPlanted(" + year + "," + range + ");\">" + year_label + "</span>" + 
1834              "</td>" + 
1835              "<td align=right>" + "<span class='markYear" + year + "' >" + count + "</span>" + "</td>" +
1836          "</tr>\n" ; 
1837  return (entry) ;                                 
1838}
1839
1840function test ()
1841{
1842}
1843
1844function calcRadiusModifierBasedOnZoomlevel (caller)
1845{
1846  zoom = round (map.getZoom (), 2) ; 
1847
1848  var radius_modifier = 1 ;
1849
1850  if (zoom <= 19) { radius_modifier *= grow_radius_per_zoom_level ; }
1851  if (zoom <= 18) { radius_modifier *= grow_radius_per_zoom_level ; }
1852  if (zoom <= 17) { radius_modifier *= grow_radius_per_zoom_level ; }
1853  if (zoom <= 16) { radius_modifier *= grow_radius_per_zoom_level ; }
1854  if (zoom <= 15) { radius_modifier *= grow_radius_per_zoom_level ; }
1855  if (zoom <= 14) { radius_modifier *= grow_radius_per_zoom_level ; }
1856  if (zoom <= 13) { radius_modifier *= grow_radius_per_zoom_level ; }
1857
1858  //console.log ('caller ' + caller +', adjust to zoom:', zoom, ', radius_modifier', round (radius_modifier,2)) ; 
1859  return (radius_modifier) ;
1860}  
1861
1862function adjustToNewZoomLevel (reason)
1863{
1864  if (! load_trees_completed)
1865  { 
1866    console.log ('adjustToNewZoomLevel skipped: too early') ;
1867    return; 
1868  }    
1869  //console.log ('adjustToNewZoomLevel on ', reason) ;
1870
1871  var radius_modifier = calcRadiusModifierBasedOnZoomlevel (0) ;   
1872  console.log ('radius_modifier radius_modifier: ', radius_modifier) ;
1873  adjustCircleBorderToZoomLevel (radius_modifier) ;   // adjust circles border weight to zoom level
1874  adjustCircleRadiusToZoomLevel (radius_modifier) ;   // adjust circle size to zoom level
1875  adjustMarkedCircle ()
1876	adjustTextMarkers () ;
1877//adjustMarkerIconsToZoomlevel () ;    // show or hide different icon sets depending on zoom level
1878  adjustTreeLayersToZoomlevel (zoom) ;
1879  old_radius_modifier = radius_modifier ;
1880}
1881
1882function updateTaxonNames ()
1883{
1884  var font = "<font class='tree-labels' color=black style='text-align:right;font-weight:500;'>" ;   
1885
1886  console.log ('updateTaxonNames ()') ;
1887  for (var marker of species_trees)
1888  {
1889    var species_sc = marker.options.species_sc ;
1890
1891    if (show_text_markers_mode == show_text_markers_local)
1892    {
1893      ui = 'nl' ;  // to do : generalize to all supported language
1894      name = taxonLocalName (ui, species_sc) ;
1895      if (name != null)
1896      { 
1897        name = name.replace (/undefined/ig,'').replace (/\?/g,'').replace (/~/g, "'") ; 
1898        name = ucfirst (name) ;
1899        font = "<font class='tree-labels' color=#660000 style='text-align:right;font-weight:900;font-style:italic;'>" ;   
1900      }
1901    }      
1902    else
1903    {    
1904      var name = species_sc.replace (/_/g, ' ') ; // no local name -> use scientific name
1905      if (name != null)
1906      { 
1907        if ((name.indexOf (' × ') > -1) ||
1908            (name.indexOf (' x ') > -1))
1909        { name = name.replace (/ [×x]/, '&nbsp;×').replace (/,.*$/g, '') ; }
1910        else 
1911        { name = name.replace (/ /, ' ').replace (/,.*$/g, '') ; }
1912      } 
1913      else
1914      { name = '??' ; }
1915      name = ucfirst (name) ;
1916    }
1917
1918    //console.log ('marker:', marker.options) ;    
1919    //console.log (marker.options.species_sc) ; //!!! use to switch local <-> scientific name
1920    //console.log (marker) ;
1921		if (marker._icon != null)
1922    { marker._icon.innerHTML = "<div>" + font + "<p style='line-height:0.9'>" + name + "</span></font></div>" ; }
1923  }      
1924}
1925
1926function makeAllCirclesTransparent ()
1927{
1928  console.log ('makeAllCirclesTransparent') ;
1929  var count = 0 ;
1930  for (var circle of circles_trees)
1931  { 
1932    count ++ ;
1933    circle.setStyle ({fillColor: 'white ',
1934                      fillOpacity: 0, 
1935                      weight: 2 });  
1936  }    
1937}
1938
1939function uncheckCircles (latlngs)
1940{
1941  var lon, lat, tmp ; 
1942  var index, count = 0 ;
1943  if (! latlngs)
1944  { return ; }    
1945
1946  var minlat = latlngs [0][0] ;
1947  var maxlat = latlngs [1][0] ;
1948  var minlon = latlngs [0][1] ;
1949  var maxlon = latlngs [1][1] ;
1950  if (minlat > maxlat)
1951  { tmp = minlat ; minlat = maxlat ; maxlat = tmp ; }
1952  if (minlon > maxlon)
1953  { tmp = minlon ; minlon = maxlon ; maxlon = tmp ; }
1954
1955  SetNavbarHeaders ('trees removed') ;
1956
1957  console.log (latlngs, 'minlat:',minlat, ', maxlat:',maxlat, ', minlon:',minlon, ', maxlon:',maxlon) ;
1958  for (var circle of circles_trees)
1959  { 
1960    lat = circle.getLatLng().lat ;
1961    lon = circle.getLatLng().lng ;
1962    
1963    if (lat >= minlat && lat <= maxlat &&
1964        lon >= minlon && lon <= maxlon) 
1965    {
1966      index = parseInt (circle.options.index) ;
1967      
1968      var tree = data.elements [index]; 
1969
1970      if (select_trees_park || 
1971         (select_trees_skip && (tree.data_from == 'gov')))
1972      { 
1973        count ++ ;
1974        uncheckTree (index) ; 
1975      }
1976      else
1977      { map.removeLayer (indexed_circles [index]) ; }
1978    }          
1979  }  
1980  
1981  return (count) ;
1982}
1983  
1984function removeTooltip () 
1985{ 
1986//  map.eachLayer (function (layer)
1987//  { 
1988//    if (layer.options.pane === "tooltipPane") 
1989//    { layer.removeFrom (map) ; }
1990//}) ;	
1991}  
1992
1993function removeWhiteDots () 
1994{ 
1995  for (var layer of circles_marked)
1996  { 
1997    if (layer != null) 
1998    { map.removeLayer (layer) ; } 
1999  }
2000}  
2001	
2002function resetMarkedTree  ()
2003{    
2004  if (mark_circle != null) // from findCircleById
2005  {  
2006    var zoom = Math.round (100 * map.getZoom ()) / 100  ;
2007    var factor_larger = Math.pow (1.3, (19 - zoom)) ;    
2008    mark_circle.setRadius (mark_circle.radius_base * factor_larger) ; 
2009    if (zoom >= 17) { weight = 0.6 ; }
2010    else if (zoom >= 15) { weight = 0.4 ; }
2011    else if (zoom >= 13) { weight = 0.2 ; }
2012    else                 { weight = 0.1 ; } 
2013    mark_circle.options.weight = weight ;
2014  }    
2015}
2016
2017function showUncheckedTrees ()
2018{
2019  return ;
2020  if (list_unchecked && list_unchecked != '')
2021  { alert ('Reference numbers of clicked cirkels (which should be filtered from input):' + crlf + list_unchecked) ; }      
2022  else
2023  { alert ('No selected trees in list') ; }      
2024}

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.