@abi-software/flatmap-viewer 2.6.2 → 2.7.0-a.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.rst CHANGED
@@ -26,7 +26,7 @@ Running
26
26
 
27
27
  ::
28
28
 
29
- $ npm start
29
+ $ npm run dev
30
30
 
31
31
  Maps can then be viewed at http://localhost:3000
32
32
 
package/lib/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ //==============================================================================
2
+
3
+ import {FlatMap, MapManager} from '../src/flatmap-viewer'
4
+
5
+ //==============================================================================
6
+
7
+ export {FlatMap, MapManager}
8
+
9
+ //==============================================================================
package/package.json CHANGED
@@ -1,26 +1,28 @@
1
1
  {
2
2
  "name": "@abi-software/flatmap-viewer",
3
- "version": "2.6.2",
3
+ "version": "2.7.0-a.1",
4
4
  "description": "Flatmap viewer using Maplibre GL",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/AnatomicMaps/flatmap-viewer.git"
8
8
  },
9
+ "type": "module",
9
10
  "main": "src/main.js",
10
11
  "files": [
12
+ "lib",
11
13
  "src",
12
14
  "static"
13
15
  ],
14
16
  "scripts": {
15
17
  "test": "echo \"Error: no test specified\" && exit 1",
16
- "start": "node app",
17
- "build": "webpack --mode production",
18
+ "dev": "vite serve app --port 3000",
19
+ "build": "tsc --p ./tsconfig-build.json && vite build",
20
+ "preview": "vite preview",
18
21
  "docs": "cd docs; poetry run make html"
19
22
  },
20
23
  "author": "David Brooks",
21
24
  "license": "MIT",
22
25
  "dependencies": {
23
- "@babel/runtime": "^7.10.4",
24
26
  "@deck.gl/core": "^8.9.35",
25
27
  "@deck.gl/layers": "^8.9.35",
26
28
  "@deck.gl/mapbox": "^8.9.35",
@@ -33,28 +35,16 @@
33
35
  "colord": "^2.9.3",
34
36
  "core-js-pure": "^3.36.1",
35
37
  "html-es6cape": "^2.0.2",
36
- "maplibre-gl": ">=3.6.0",
38
+ "maplibre-gl": ">=4.1.0",
37
39
  "mathjax-full": "^3.2.2",
38
40
  "minisearch": "^2.2.1",
39
41
  "polylabel": "^1.1.0"
40
42
  },
41
43
  "devDependencies": {
42
- "@babel/core": "^7.5.5",
43
- "@babel/plugin-transform-runtime": "^7.5.5",
44
- "@babel/preset-env": "^7.10.4",
45
- "babel-loader": "^8.1.0",
46
- "browser-sync": "^2.26.7",
47
- "bs-fullscreen-message": "^1.1.0",
48
- "clean-webpack-plugin": "^3.0.0",
49
- "css-loader": "^6.7.3",
44
+ "@types/node": "^20.12.7",
50
45
  "eslint": "^8.7.0",
51
- "express": "^4.17.1",
52
- "html-webpack-plugin": "^4.5.2",
53
- "strip-ansi": "^7.0.1",
54
- "style-loader": "^3.3.2",
55
- "webpack": "^5.16.0",
56
- "webpack-cli": "^4.4.0",
57
- "webpack-dev-middleware": "^4.1.0",
58
- "webpack-node-externals": "^1.7.2"
46
+ "typescript": "^5.2.2",
47
+ "vite": "^5.1.4",
48
+ "vite-plugin-dts": "^3.8.1"
59
49
  }
60
50
  }
@@ -292,7 +292,7 @@ export class AnnotationDrawControl
292
292
  }
293
293
 
294
294
  changeMode(type)
295
- //===============
295
+ //==============
296
296
  {
297
297
  // Change the mode directly without listening to modes callback
298
298
  this.__draw.changeMode(type.mode, type.options)
@@ -33,12 +33,14 @@ import '../static/css/flatmap-viewer.css';
33
33
 
34
34
  //==============================================================================
35
35
 
36
- import {MapServer, loadJSON} from './mapserver.js';
36
+ import {MapServer} from './mapserver.js';
37
37
  import {SearchIndex} from './search.js';
38
38
  import {UserInteractions} from './interactions.js';
39
39
 
40
40
  import {APINATOMY_PATH_PREFIX} from './pathways';
41
41
 
42
+ import {loadClusterIcons} from './layers/cluster'
43
+
42
44
  import * as images from './images.js';
43
45
  import * as utils from './utils.js';
44
46
 
@@ -61,7 +63,7 @@ export const UNCLASSIFIED_TAXON_ID = 'NCBITaxon:2787823'; // unclassified entr
61
63
  * Maps are not created directly but instead are created and loaded by
62
64
  * :meth:`LoadMap` of :class:`MapManager`.
63
65
  */
64
- class FlatMap
66
+ export class FlatMap
65
67
  {
66
68
  constructor(container, mapBaseUrl, mapDescription, resolve)
67
69
  {
@@ -204,6 +206,9 @@ class FlatMap
204
206
  await this.addImage(image.id, image.url, '', image.options);
205
207
  }
206
208
 
209
+ // Load icons used for clustered markers
210
+ await loadClusterIcons(this._map)
211
+
207
212
  // Layers have now loaded so finish setting up
208
213
  this._userInteractions = new UserInteractions(this);
209
214
  }
@@ -966,6 +971,21 @@ class FlatMap
966
971
  return -1;
967
972
  }
968
973
 
974
+ addMarkers(anatomicalIds, options={})
975
+ //====================================
976
+ {
977
+ const markerIds = []
978
+ for (const anatomicalId of anatomicalIds) {
979
+ if (this._userInteractions !== null) {
980
+ markerIds.push(this._userInteractions.addMarker(anatomicalId, options))
981
+ } else {
982
+ markerIds.push(-1)
983
+ }
984
+ }
985
+ return markerIds
986
+ }
987
+
988
+
969
989
  /**
970
990
  * Remove a marker from the map.
971
991
  *
@@ -975,7 +995,7 @@ class FlatMap
975
995
  removeMarker(markerId)
976
996
  //====================
977
997
  {
978
- if (this._userInteractions !== null) {
998
+ if (markerId > -1 && this._userInteractions !== null) {
979
999
  this._userInteractions.removeMarker(markerId);
980
1000
  }
981
1001
  }
@@ -1046,19 +1066,17 @@ class FlatMap
1046
1066
  'alert',
1047
1067
  'biological-sex'
1048
1068
  ];
1049
- const jsonProperties = [
1050
- 'hyperlinks'
1051
- ];
1052
1069
  for (const property of exportedProperties) {
1053
1070
  if (property in properties) {
1054
1071
  const value = properties[property];
1055
1072
  if (value !== undefined) {
1056
- if (jsonProperties.includes(property)) {
1057
- data[property] = JSON.parse(properties[property])
1073
+ if ((Array.isArray(value) && value.length)
1074
+ || (value.constructor === Object && Object.keys(value).length)) {
1075
+ data[property] = value
1058
1076
  } else if (property === 'featureId') {
1059
- data[property] = +properties[property]; // Ensure numeric
1077
+ data[property] = +value; // Ensure numeric
1060
1078
  } else {
1061
- data[property] = properties[property];
1079
+ data[property] = value;
1062
1080
  }
1063
1081
  }
1064
1082
  }
@@ -1373,11 +1373,13 @@ export class UserInteractions
1373
1373
  }
1374
1374
  const markerPosition = this.__markerPosition(featureId, annotation);
1375
1375
  if (options.cluster && this._layerManager) {
1376
- this._layerManager.addMarkers([markerPosition])
1376
+ this._layerManager.addMarker(markerId, markerPosition, annotation)
1377
1377
  } else {
1378
1378
  const marker = new maplibregl.Marker(markerOptions)
1379
1379
  .setLngLat(markerPosition)
1380
1380
  .addTo(this._map);
1381
+
1382
+
1381
1383
  markerElement.addEventListener('mouseenter',
1382
1384
  this.markerMouseEvent_.bind(this, marker, anatomicalId));
1383
1385
  markerElement.addEventListener('mousemove',
@@ -1441,28 +1443,46 @@ export class UserInteractions
1441
1443
  return anatomicalIds;
1442
1444
  }
1443
1445
 
1446
+ // Separate out MapLibre specific code and result of mouse event (tooltip,
1447
+ // client message, etc) so clustering code can also use this to process
1448
+ // events.
1449
+
1444
1450
  markerMouseEvent_(marker, anatomicalId, event)
1445
1451
  //============================================
1446
1452
  {
1447
1453
  // No tooltip when context menu is open
1448
1454
  if (this._modal
1449
1455
  || (this.__activeMarker !== null && event.type === 'mouseleave')) {
1450
- return;
1456
+ return
1451
1457
  }
1452
1458
 
1453
1459
  if (['mouseenter', 'mouseleave', 'click'].includes(event.type)) {
1454
- this.__activeMarker = marker;
1460
+ this.__activeMarker = marker
1455
1461
 
1456
- // Remove any existing tooltips
1457
- this.removeTooltip_();
1458
- marker.setPopup(null);
1462
+ // Remove any tooltip
1463
+ marker.setPopup(null)
1459
1464
 
1460
1465
  // Reset cursor
1461
1466
  marker.getElement().style.cursor = 'default';
1462
1467
 
1468
+
1469
+ const markerId = this.__markerIdByMarker.get(marker)
1470
+ const annotation = this.__annotationByMarkerId.get(markerId)
1471
+
1472
+ this.markerEvent_(event, markerId, marker.getLngLat(),
1473
+ anatomicalId, annotation)
1474
+ }
1475
+ }
1476
+
1477
+ markerEvent_(event, markerId, markerPosition, anatomicalId, annotation)
1478
+ //=====================================================================
1479
+ {
1480
+ if (['mouseenter', 'mouseleave', 'click'].includes(event.type)) {
1481
+
1482
+ // Remove any existing tooltips
1483
+ this.removeTooltip_();
1484
+
1463
1485
  if (['mouseenter', 'click'].includes(event.type)) {
1464
- const markerId = this.__markerIdByMarker.get(marker);
1465
- const annotation = this.__annotationByMarkerId.get(markerId);
1466
1486
  // The marker's feature
1467
1487
  const feature = this.mapFeature(annotation.featureId);
1468
1488
  if (feature !== undefined) {
@@ -1476,13 +1496,12 @@ export class UserInteractions
1476
1496
  }
1477
1497
  // Show tooltip
1478
1498
  const html = this.tooltipHtml_(annotation, true);
1479
- this.__showToolTip(html, marker.getLngLat());
1499
+ this.__showToolTip(html, markerPosition);
1480
1500
 
1481
1501
  // Send marker event message
1482
1502
  this._flatmap.markerEvent(event.type, markerId, anatomicalId);
1483
1503
  }
1484
1504
  }
1485
- event.stopPropagation();
1486
1505
  }
1487
1506
 
1488
1507
  __clearActiveMarker()
@@ -1531,7 +1550,7 @@ export class UserInteractions
1531
1550
  .setLngLat(location)
1532
1551
  .setDOMContent(element);
1533
1552
 
1534
- // Set the merker tooltip and show it
1553
+ // Set the marker tooltip and show it
1535
1554
  marker.setPopup(this._tooltip);
1536
1555
  marker.togglePopup();
1537
1556
 
@@ -0,0 +1,191 @@
1
+ /******************************************************************************
2
+
3
+ Flatmap viewer and annotation tool
4
+
5
+ Copyright (c) 2019 - 2023 David Brooks
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
18
+
19
+ ******************************************************************************/
20
+
21
+ import {SvgManager, SvgTemplateManager} from '../../thirdParty/maplibre-gl-svg/src'
22
+
23
+ //==============================================================================
24
+
25
+ const markerLargeCircle = `<svg xmlns="http://www.w3.org/2000/svg" width="calc(28 * {scale})" height="calc(39 * {scale})" viewBox="-1 -1 27 42">
26
+ <ellipse style="fill: rgb(0, 0, 0); fill-opacity: 0.2;" cx="12" cy="36" rx="8" ry="4"/>
27
+ <path d="M12.25.25a12.254 12.254 0 0 0-12 12.494c0 6.444 6.488 12.109 11.059 22.564.549 1.256 1.333 1.256 1.882 0
28
+ C17.762 24.853 24.25 19.186 24.25 12.744A12.254 12.254 0 0 0 12.25.25Z"
29
+ style="fill:{color};stroke:{secondaryColor};stroke-width:1"/>
30
+ <circle cx="12.5" cy="12.5" r="9" fill="{secondaryColor}"/>
31
+ <text x="12" y="17.5" style="font-size:14px;fill:#000;text-anchor:middle">{text}</text>
32
+ </svg>`
33
+
34
+ const markerSmallCircle = `<svg xmlns="http://www.w3.org/2000/svg" width="calc(28 * {scale})" height="calc(39 * {scale})" viewBox="-1 -1 27 42">
35
+ <ellipse style="fill: rgb(0, 0, 0); fill-opacity: 0.2;" cx="12" cy="36" rx="8" ry="4"/>
36
+ <path d="M12.25.25a12.254 12.254 0 0 0-12 12.494c0 6.444 6.488 12.109 11.059 22.564.549 1.256 1.333 1.256 1.882 0
37
+ C17.762 24.853 24.25 19.186 24.25 12.744A12.254 12.254 0 0 0 12.25.25Z"
38
+ style="fill:{color};stroke:{secondaryColor};stroke-width:1"/>
39
+ <circle cx="12.5" cy="12.5" r="5" fill="{secondaryColor}"/>
40
+ </svg>`
41
+
42
+ //==============================================================================
43
+
44
+ export async function loadClusterIcons(map)
45
+ {
46
+ SvgTemplateManager.addTemplate('marker-large-circle', markerLargeCircle, false)
47
+ SvgTemplateManager.addTemplate('marker-small-circle', markerSmallCircle, false)
48
+
49
+ const svgManager = new SvgManager(map)
50
+ await svgManager.createFromTemplate('clustered-marker', 'marker-large-circle', '#EE5900', '#fff')
51
+ await svgManager.createFromTemplate('unclustered-marker', 'marker-small-circle', '#005974', '#fff')
52
+ }
53
+
54
+ //==============================================================================
55
+
56
+ export class ClusteredMarkerLayer
57
+ {
58
+ #flatmap
59
+ #map
60
+ #points = {
61
+ type: 'FeatureCollection',
62
+ features: []
63
+ }
64
+ #ui
65
+
66
+ seenmove = false
67
+
68
+ constructor(flatmap, ui)
69
+ {
70
+ this.#flatmap = flatmap
71
+ this.#ui = ui
72
+ this.#map = flatmap.map
73
+
74
+ this.#map.addSource('markers', {
75
+ type: 'geojson',
76
+ data: this.#points,
77
+ cluster: true, // Adds the ``point_count`` property to source data
78
+ clusterMaxZoom: 9, // Max zoom to cluster points on
79
+ clusterRadius: 50 // Radius of each cluster when clustering points (defaults to 50)
80
+ })
81
+
82
+ this.#map.addLayer({
83
+ id: 'clustered-markers',
84
+ type: 'symbol',
85
+ source: 'markers',
86
+ filter: ['has', 'point_count'],
87
+ 'layout': {
88
+ 'icon-image': 'clustered-marker',
89
+ 'icon-allow-overlap': true,
90
+ 'icon-ignore-placement': true,
91
+ 'icon-offset': [0, -17],
92
+ 'icon-size': 0.8,
93
+ 'text-field': '{point_count_abbreviated}',
94
+ 'text-size': 10,
95
+ 'text-offset': [0, -1.93]
96
+ }
97
+ })
98
+
99
+ this.#map.addLayer({
100
+ id: 'single-points',
101
+ type: 'symbol',
102
+ source: 'markers',
103
+ filter: ['!', ['has', 'point_count']],
104
+ 'layout': {
105
+ 'icon-image': 'unclustered-marker',
106
+ 'icon-allow-overlap': true,
107
+ 'icon-ignore-placement': true,
108
+ 'icon-offset': [0, -17],
109
+ 'icon-size': 0.6
110
+ }
111
+ })
112
+
113
+ // inspect a cluster on click
114
+ this.#map.on('click', 'clustered-markers', async (e) => {
115
+ const features = this.#map.queryRenderedFeatures(e.point, {
116
+ layers: ['clustered-markers']
117
+ })
118
+ console.log('Cluster marker', features)
119
+ const clusterId = features[0].properties.cluster_id
120
+ const zoom = await this.#map.getSource('markers').getClusterExpansionZoom(clusterId)
121
+ this.#map.easeTo({
122
+ center: features[0].geometry.coordinates,
123
+ zoom
124
+ })
125
+ })
126
+
127
+ this.#map.on('click', 'single-points', this.singleMarkerEvent.bind(this))
128
+ this.#map.on('mouseenter', 'single-points', this.singleMarkerEvent.bind(this))
129
+ this.#map.on('mousemove', 'single-points', this.singleMarkerEvent.bind(this))
130
+ // this.#map.on('mouseleave', 'single-points', this.singleMarkerEvent.bind(this))
131
+
132
+ this.#map.on('mouseenter', 'clustered-markers', () => {
133
+ this.#map.getCanvas().style.cursor = 'pointer'
134
+ })
135
+
136
+ this.#map.on('mouseleave', 'clustered-markers', () => {
137
+ this.#map.getCanvas().style.cursor = ''
138
+ })
139
+
140
+ // Also 'mousemove'...
141
+ }
142
+
143
+ singleMarkerEvent(event)
144
+ //======================
145
+ {
146
+ const features = this.#map.queryRenderedFeatures(event.point, {
147
+ layers: ['single-points']
148
+ })
149
+ for (const feature of features) {
150
+ if (event.type === 'mousemove' && !this.seenMove) {
151
+ console.log('Single marker', event.type, feature)
152
+ this.seenMove = true
153
+ } else if (event.type !== 'mousemove') {
154
+ console.log('Single marker', event.type, feature)
155
+ this.seenMove = false
156
+ }
157
+
158
+ const properties = feature.properties
159
+ const position = properties.markerPosition.slice(1, -1).split(',').map(p => +p)
160
+ this.#ui.markerEvent_(event, feature.id, position, properties.models, properties)
161
+ }
162
+ event.originalEvent.stopPropagation()
163
+ }
164
+
165
+ addMarker(id, position, properties={})
166
+ //====================================
167
+ {
168
+ this.#points.features.push({
169
+ type: 'Feature',
170
+ id,
171
+ properties,
172
+ geometry: {
173
+ type: 'Point',
174
+ coordinates: position
175
+ }
176
+ })
177
+ this.#map.getSource('markers')
178
+ .setData(this.#points)
179
+ }
180
+
181
+ clearMarkers()
182
+ //============
183
+ {
184
+ this.#points.features = []
185
+ this.#map.getSource('markers')
186
+ .setData(this.#points)
187
+ }
188
+ }
189
+
190
+ //==============================================================================
191
+
@@ -344,14 +344,12 @@ export class FlightPathLayer
344
344
  id: `arc-${pathType}`,
345
345
  data: pathData,
346
346
  pickable: true,
347
- autoHighlight: true,
348
347
  numSegments: 400,
349
348
  // Styles
350
349
  getSourcePosition: f => f.pathStartPosition,
351
350
  getTargetPosition: f => f.pathEndPosition,
352
351
  getSourceColor: this.#pathColour.bind(this),
353
352
  getTargetColor: this.#pathColour.bind(this),
354
- highlightColor: o => this.#pathColour(o.object),
355
353
  opacity: 1.0,
356
354
  getWidth: 3,
357
355
  }
@@ -25,6 +25,7 @@ limitations under the License.
25
25
  import {PATHWAYS_LAYER} from '../pathways.js';
26
26
  import * as utils from '../utils.js';
27
27
 
28
+ import {ClusteredMarkerLayer} from './cluster'
28
29
  import * as style from './styling.js';
29
30
 
30
31
  import {FlightPathLayer} from './flightpaths'
@@ -266,6 +267,7 @@ class MapRasterLayers extends MapStylingLayers
266
267
  export class LayerManager
267
268
  {
268
269
  #featureLayers = new Map()
270
+ #markerLayer = null
269
271
  #flightPathLayer = null
270
272
  #rasterLayer = null
271
273
 
@@ -308,6 +310,9 @@ export class LayerManager
308
310
 
309
311
  // Support flight path view
310
312
  this.#flightPathLayer = new FlightPathLayer(flatmap, ui)
313
+
314
+ // Show clustered markers in a layer
315
+ this.#markerLayer = new ClusteredMarkerLayer(flatmap, ui)
311
316
  }
312
317
 
313
318
  get layers()
@@ -356,6 +361,18 @@ export class LayerManager
356
361
  }
357
362
  }
358
363
 
364
+ addMarker(id, position, properties={})
365
+ //====================================
366
+ {
367
+ this.#markerLayer.addMarker(id, position, properties)
368
+ }
369
+
370
+ clearMarkers()
371
+ //============
372
+ {
373
+ this.#markerLayer.clearMarkers()
374
+ }
375
+
359
376
  featuresAtPoint(point)
360
377
  //====================
361
378
  {
@@ -408,10 +425,14 @@ export class LayerManager
408
425
  mapLayer.setFilter(this.__layerOptions);
409
426
  }
410
427
  if (this.#flightPathLayer) {
428
+ // * @arg options.layerOptions.sckan {string} Show neuron paths known to SCKAN: values are ``valid`` (default),
429
+ // * ``invalid``, ``all`` or ``none``.
430
+
431
+
411
432
  const sckanState = options.sckan || 'valid'
412
433
  const sckanFilter = (sckanState == 'none') ? {NOT: {HAS: 'sckan'}} :
413
434
  (sckanState == 'valid') ? {sckan: true} :
414
- (sckanState == 'invalid') ? {NOT: {sckan: true}} :
435
+ (sckanState == 'invalid') ? {NOT: {sckan: true}} : // {sckan: false} is different...
415
436
  true
416
437
  const featureFilter = new PropertiesFilter(sckanFilter)
417
438
  if ('taxons' in options) {
package/src/main.js CHANGED
@@ -25,6 +25,41 @@ limitations under the License.
25
25
  import { MapManager } from './flatmap-viewer';
26
26
  export { MapManager };
27
27
 
28
+ const ALL_MARKERS = [
29
+ // Most of these are around the heart
30
+ 'UBERON:0003382',
31
+ 'UBERON:0002348',
32
+ 'UBERON:0002349',
33
+ 'UBERON:0003379',
34
+ 'UBERON:0001986',
35
+ 'UBERON:0001074',
36
+ 'UBERON:0003381',
37
+ 'UBERON:0002165',
38
+ 'UBERON:0002080',
39
+ 'UBERON:0000948',
40
+ 'UBERON:0002084',
41
+ 'UBERON:0002078',
42
+ 'UBERON:0002079',
43
+ 'UBERON:0002349',
44
+ 'UBERON:0002408',
45
+ 'UBERON:0007240',
46
+ 'UBERON:0002359',
47
+
48
+ 'UBERON:0001508',
49
+ 'UBERON:0037094',
50
+ 'ILX:0738305',
51
+
52
+ 'UBERON:0000948', // {className: 'heart-marker'}); // Heart
53
+ 'UBERON:0002048', // Lung
54
+ 'UBERON:0000945', // Stomach
55
+ 'UBERON:0001155', // Colon
56
+ 'UBERON:0001255', // Bladder
57
+ 'UBERON:0001759', // Vagus
58
+
59
+ 'UBERON:0016508', // Pelvic ganglion
60
+
61
+ ]
62
+
28
63
  //==============================================================================
29
64
 
30
65
  class DrawControl
@@ -110,6 +145,7 @@ export async function standaloneViewer(map_endpoint=null, options={})
110
145
  showPosition: false,
111
146
  standalone: true,
112
147
  annotator: true,
148
+ flightPaths: true
113
149
  }, options);
114
150
 
115
151
  function loadMap(id, taxon, sex)
@@ -139,15 +175,20 @@ export async function standaloneViewer(map_endpoint=null, options={})
139
175
  mapOptions.background = args[0].value;
140
176
  } else if (eventType === 'annotation') {
141
177
  drawControl.handleEvent(...args)
178
+ } else {
179
+ //console.log(eventType, ...args)
142
180
  }
143
181
  }, mapOptions)
144
182
  .then(map => {
183
+ /*
145
184
  map.addMarker('UBERON:0000948', {className: 'heart-marker'}); // Heart
146
185
  map.addMarker('UBERON:0002048'); // Lung
147
186
  map.addMarker('UBERON:0000945'); // Stomach
148
187
  map.addMarker('UBERON:0001155'); // Colon
149
188
  map.addMarker('UBERON:0001255'); // Bladder
150
189
  map.addMarker('UBERON:0001759'); // Vagus
190
+ */
191
+ map.addMarkers(ALL_MARKERS, {cluster: true})
151
192
  currentMap = map;
152
193
  drawControl = new DrawControl(map)
153
194
  })
package/src/pathways.js CHANGED
@@ -61,7 +61,7 @@ export function pathColourArray(pathType, alpha=255)
61
61
  {
62
62
  const rgb = colord(PathTypeMap.has(pathType)
63
63
  ? PathTypeMap.get(pathType).colour
64
- : '#FF0').toRgb()
64
+ : PathTypeMap.get('other').colour).toRgb()
65
65
  return [rgb.r, rgb.g, rgb.b, alpha]
66
66
  }
67
67
 
package/src/types.ts ADDED
@@ -0,0 +1,26 @@
1
+ /******************************************************************************
2
+
3
+ Flatmap viewer and annotation tool
4
+
5
+ Copyright (c) 2019 - 2024 David Brooks
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
18
+
19
+ ******************************************************************************/
20
+
21
+ export type Constructor<T> = new(...args: any[]) => T
22
+
23
+ export type ObjectRecord = Record<string, any>
24
+
25
+ //==============================================================================
26
+
package/src/utils.js CHANGED
@@ -120,6 +120,7 @@ export function normaliseId(id)
120
120
 
121
121
  export function setDefaults(options, defaultOptions)
122
122
  {
123
+ // c.f. Object.assign({}, defaultOptions, options)
123
124
  if (options === undefined || options === null) {
124
125
  return defaultOptions;
125
126
  }