@foodmarketmaker/mapag 0.0.19 → 0.0.20

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.
@@ -21,6 +21,7 @@ import maplibregl, { Popup } from 'maplibre-gl';
21
21
  import bbox from '@turf/bbox';
22
22
  import bboxPolygon from '@turf/bbox-polygon';
23
23
  import buffer from '@turf/buffer';
24
+ import difference from '@turf/difference';
24
25
 
25
26
  function SaveMap(map) {
26
27
  var mapCanvas = map.getCanvas();
@@ -35,7 +36,6 @@ function AddLayer(map, layer, afterId) {
35
36
  return false;
36
37
  try {
37
38
  if (map.getLayer(layer.id)) {
38
- console.log(`Layer ${layer.id} already exists`);
39
39
  return false;
40
40
  }
41
41
  map.addLayer(layer, afterId);
@@ -14325,99 +14325,43 @@ const mapStyles = {
14325
14325
  },
14326
14326
  };
14327
14327
 
14328
- function isGeoloader(obj) {
14329
- return typeof obj === 'object' &&
14330
- obj !== null &&
14331
- typeof obj.LoadBoundary === 'function';
14332
- }
14333
- function isMultiPolygon(obj) {
14334
- return typeof obj === 'object' &&
14335
- obj !== null &&
14336
- obj.type === 'MultiPolygon';
14337
- }
14338
- function isPolygon(obj) {
14339
- return typeof obj === 'object' &&
14340
- obj !== null &&
14341
- obj.type === 'Polygon';
14342
- }
14343
- function isNumber2DArray(input) {
14344
- return Array.isArray(input) &&
14345
- input.every(row => Array.isArray(row) &&
14346
- row.every(cell => typeof cell === 'number'));
14347
- }
14348
- function isNumber3DArray(input) {
14349
- return Array.isArray(input) &&
14350
- input.every(matrix => Array.isArray(matrix) &&
14351
- matrix.every(row => Array.isArray(row) &&
14352
- row.every(cell => typeof cell === 'number')));
14353
- }
14354
- function toMultiPolygon(geom) {
14355
- if (isMultiPolygon(geom)) {
14356
- return geom;
14357
- }
14358
- if (isPolygon(geom)) {
14359
- return {
14360
- type: 'MultiPolygon',
14361
- coordinates: [geom.coordinates]
14362
- };
14363
- }
14364
- if (isNumber2DArray(geom)) {
14365
- let raw = [geom];
14366
- const coords = raw;
14367
- return {
14368
- type: 'MultiPolygon',
14369
- coordinates: coords
14370
- };
14371
- }
14372
- let raw = geom;
14373
- const coords = raw;
14374
- return {
14375
- type: 'MultiPolygon',
14376
- coordinates: coords
14377
- };
14328
+ class BackgroundMaskSettings {
14329
+ fillColor = 'black';
14330
+ fillOpacity = 0.5;
14331
+ visible = true;
14332
+ beforeLayer = StandardLayersMapper.SELECTIONS;
14333
+ zoom = true;
14334
+ mask;
14335
+ loader = undefined;
14378
14336
  }
14379
-
14380
14337
  class BackgroundMaskMapper {
14381
14338
  map;
14382
14339
  count = 0;
14383
14340
  total = 0;
14384
14341
  source = 'mask';
14385
14342
  layer = 'zmask';
14386
- mask;
14387
- zoom = true;
14388
- _fillColor = 'black';
14389
- _fillOpacity = 0.5;
14390
- _visible = true;
14391
- // private _beforeLayer: string | undefined = 'unclustered-search-results'
14392
- _beforeLayer = StandardLayersMapper.SELECTIONS;
14393
- set fillColor(color) {
14394
- this.fillColor = color;
14395
- this.load();
14396
- }
14397
- get fillColor() {
14398
- return this._fillColor;
14399
- }
14400
- set fillOpacity(opacity) {
14401
- this.fillOpacity = opacity;
14402
- this.load();
14403
- }
14404
- get fillOpacity() {
14405
- return this._fillOpacity;
14406
- }
14407
- set visible(visible) {
14408
- this._visible = visible;
14409
- this.load();
14343
+ settings = signal(new BackgroundMaskSettings(), ...(ngDevMode ? [{ debugName: "settings" }] : []));
14344
+ constructor(settings) {
14345
+ if (settings) {
14346
+ this.settings.set({
14347
+ ...this.settings(),
14348
+ ...settings,
14349
+ });
14350
+ }
14351
+ const _ = effect(() => {
14352
+ const settings = this.settings();
14353
+ this._update(settings);
14354
+ }, ...(ngDevMode ? [{ debugName: "_" }] : []));
14410
14355
  }
14411
- get visible() {
14412
- return this._visible;
14356
+ update(settings) {
14357
+ this.settings.set({ ...this.settings(), ...settings });
14413
14358
  }
14414
- set beforeLayer(layerId) {
14415
- this._beforeLayer = layerId;
14359
+ _update(settings) {
14360
+ if (!this.map) {
14361
+ return;
14362
+ }
14416
14363
  this.load();
14417
14364
  }
14418
- get beforeLayer() {
14419
- return this._beforeLayer;
14420
- }
14421
14365
  onReady(map) {
14422
14366
  this.map = map;
14423
14367
  this.reset();
@@ -14430,11 +14374,12 @@ class BackgroundMaskMapper {
14430
14374
  this.removeLayers();
14431
14375
  }
14432
14376
  flyToBounds() {
14433
- if (this.map && this.mask) {
14377
+ const settings = this.settings();
14378
+ if (this.map && settings.mask) {
14434
14379
  // minX, minY, maxX, maxY order
14435
- let bboxCoords = this.mask.bbox;
14380
+ let bboxCoords = settings.mask.bbox;
14436
14381
  if (!bboxCoords) {
14437
- const f = feature(this.mask);
14382
+ const f = feature(settings.mask);
14438
14383
  bboxCoords = bbox(f);
14439
14384
  }
14440
14385
  const buffered = buffer(bboxPolygon(bboxCoords), 10, { units: 'miles' });
@@ -14450,17 +14395,36 @@ class BackgroundMaskMapper {
14450
14395
  RemoveLayer(this.map, this.layer);
14451
14396
  RemoveSource(this.map, this.source);
14452
14397
  }
14453
- load() {
14398
+ async load() {
14399
+ const settings = this.settings();
14400
+ // Check if we need to load from loader first
14401
+ if (!settings.mask && settings.loader) {
14402
+ const geomask = await settings.loader.LoadBoundary();
14403
+ this.update({ mask: geomask, loader: undefined });
14404
+ return;
14405
+ }
14454
14406
  this.removeLayers();
14455
- if (!this._visible) {
14407
+ if (!settings.visible) {
14456
14408
  return;
14457
14409
  }
14458
- if (!this.map || !this.mask) {
14410
+ if (!this.map) {
14411
+ return;
14412
+ }
14413
+ if (!settings.mask) {
14459
14414
  return;
14460
14415
  }
14461
- // Create a simple world polygon without the mask for now
14462
- // TODO: Implement proper polygon difference
14463
- const shape = bboxPolygon([-180, -90, 180, 90]);
14416
+ const mask = settings.mask;
14417
+ // Use Turf's difference to cut the mask out of the world polygon
14418
+ const worldPolygon = bboxPolygon([-180, -90, 180, 90]);
14419
+ const features = [worldPolygon];
14420
+ for (const part of mask.coordinates) {
14421
+ features.push(polygon(part));
14422
+ }
14423
+ const fc = {
14424
+ type: 'FeatureCollection',
14425
+ features: features
14426
+ };
14427
+ const shape = difference(fc);
14464
14428
  AddSource(this.map, this.source, {
14465
14429
  type: 'geojson',
14466
14430
  data: shape,
@@ -14470,31 +14434,28 @@ class BackgroundMaskMapper {
14470
14434
  source: this.source,
14471
14435
  type: 'fill',
14472
14436
  paint: {
14473
- 'fill-color': this.fillColor,
14474
- 'fill-opacity': this.fillOpacity,
14437
+ 'fill-color': settings.fillColor,
14438
+ 'fill-opacity': settings.fillOpacity,
14475
14439
  },
14476
- }, this.beforeLayer);
14477
- if (this.zoom) {
14440
+ }, settings.beforeLayer);
14441
+ if (settings.zoom) {
14478
14442
  this.flyToBounds();
14479
14443
  }
14480
14444
  }
14481
- async setMask(mask, show = true) {
14482
- if (isGeoloader(mask)) {
14483
- this.mask = await mask.LoadBoundary();
14484
- }
14485
- else {
14486
- this.mask = toMultiPolygon(mask);
14487
- }
14488
- if (!show) {
14489
- return;
14490
- }
14491
- this.visible = true;
14492
- }
14493
- async loadGeoJsonFromLoader(loader) {
14494
- // Load the boundary from the external loader
14495
- const geomask = await loader.LoadBoundary();
14496
- this.mask = geomask;
14497
- this.load();
14445
+ // async setMask(mask: Geoloader | MultiPolygon | Polygon | number[][] | number[][][], show: boolean = true) {
14446
+ // if (isGeoloader(mask)) {
14447
+ // this.update({ mask: await mask.LoadBoundary() });
14448
+ // } else {
14449
+ // this.update({ mask: toMultiPolygon(mask) });
14450
+ // }
14451
+ // }
14452
+ // async loadGeoJsonFromLoader(loader: Geoloader) {
14453
+ // // Load the boundary from the external loader
14454
+ // const geomask = await loader.LoadBoundary();
14455
+ // this.update({ mask: geomask });
14456
+ // }
14457
+ getSettings() {
14458
+ return { ...this.settings() };
14498
14459
  }
14499
14460
  }
14500
14461
 
@@ -15404,6 +15365,7 @@ class HardinessMapper {
15404
15365
  }
15405
15366
  else {
15406
15367
  this.map.setLayoutProperty(this.LAYER_ID, 'visibility', 'none');
15368
+ return;
15407
15369
  }
15408
15370
  // Update paint properties
15409
15371
  this.map.setPaintProperty(this.LAYER_ID, 'fill-color', [
@@ -15611,6 +15573,334 @@ class NAASSettings {
15611
15573
  autoSelectLayer = false;
15612
15574
  }
15613
15575
 
15576
+ class NaicsMapperSettings {
15577
+ type = 'circle';
15578
+ visible = true;
15579
+ radius = 8;
15580
+ color = '#1f77b4';
15581
+ opacity = 0.8;
15582
+ strokeWidth = 1;
15583
+ strokeColor = '#ffffff';
15584
+ iconUrl = '';
15585
+ iconName = 'naics-icon';
15586
+ iconSize = 0.8;
15587
+ iconAllowOverlap = true;
15588
+ iconSdf = false;
15589
+ }
15590
+ class NaicsMapper {
15591
+ legends = [];
15592
+ count = 0;
15593
+ total = 0;
15594
+ map;
15595
+ svc;
15596
+ id;
15597
+ name;
15598
+ static pmtilesUrl = 'https://foodmarketmaker-upload-data.s3.amazonaws.com/tiles/secondary_101124.pmtiles';
15599
+ static sourceLayer = 'secondary_101124';
15600
+ SOURCE_ID = 'naics-pmtiles';
15601
+ sourceLayer = NaicsMapper.sourceLayer;
15602
+ CIRCLE_LAYER_ID;
15603
+ ICON_LAYER_ID;
15604
+ HEATMAP_LAYER_ID;
15605
+ currentFilter;
15606
+ settings = signal(new NaicsMapperSettings(), ...(ngDevMode ? [{ debugName: "settings" }] : []));
15607
+ constructor(settings, id = 'naics-' + Math.random().toString(36)) {
15608
+ this.id = id;
15609
+ this.name = `NAICS Mapper (${id})`;
15610
+ // Make layer IDs unique per instance
15611
+ this.CIRCLE_LAYER_ID = `${id}-circles`;
15612
+ this.ICON_LAYER_ID = `${id}-icons`;
15613
+ this.HEATMAP_LAYER_ID = `${id}-heatmap`;
15614
+ if (settings) {
15615
+ this.settings.set({
15616
+ ...this.settings(),
15617
+ ...settings,
15618
+ });
15619
+ }
15620
+ const _ = effect(() => {
15621
+ const settings = this.settings();
15622
+ this._update(settings);
15623
+ }, ...(ngDevMode ? [{ debugName: "_" }] : []));
15624
+ }
15625
+ update(settings) {
15626
+ this.settings.set({ ...this.settings(), ...settings });
15627
+ }
15628
+ async _update(settings) {
15629
+ if (!this.map) {
15630
+ return;
15631
+ }
15632
+ const map = this.map;
15633
+ this.create();
15634
+ // Update paint/layout properties based on type
15635
+ switch (settings.type) {
15636
+ case 'circle':
15637
+ if (map.getLayer(this.CIRCLE_LAYER_ID)) {
15638
+ map.setLayoutProperty(this.CIRCLE_LAYER_ID, 'visibility', settings.visible ? 'visible' : 'none');
15639
+ map.setPaintProperty(this.CIRCLE_LAYER_ID, 'circle-radius', settings.radius);
15640
+ map.setPaintProperty(this.CIRCLE_LAYER_ID, 'circle-color', settings.color);
15641
+ map.setPaintProperty(this.CIRCLE_LAYER_ID, 'circle-opacity', settings.opacity);
15642
+ map.setPaintProperty(this.CIRCLE_LAYER_ID, 'circle-stroke-width', settings.strokeWidth);
15643
+ map.setPaintProperty(this.CIRCLE_LAYER_ID, 'circle-stroke-color', settings.strokeColor);
15644
+ }
15645
+ break;
15646
+ case 'icon':
15647
+ if (map.getLayer(this.ICON_LAYER_ID)) {
15648
+ await this.loadIcon();
15649
+ map.setLayoutProperty(this.ICON_LAYER_ID, 'visibility', settings.visible ? 'visible' : 'none');
15650
+ map.setLayoutProperty(this.ICON_LAYER_ID, 'icon-image', settings.iconName);
15651
+ map.setLayoutProperty(this.ICON_LAYER_ID, 'icon-size', settings.iconSize);
15652
+ map.setPaintProperty(this.ICON_LAYER_ID, 'icon-color', settings.color);
15653
+ map.setLayoutProperty(this.ICON_LAYER_ID, 'icon-allow-overlap', settings.iconAllowOverlap);
15654
+ }
15655
+ break;
15656
+ case 'heatmap':
15657
+ if (map.getLayer(this.HEATMAP_LAYER_ID)) {
15658
+ map.setLayoutProperty(this.HEATMAP_LAYER_ID, 'visibility', settings.visible ? 'visible' : 'none');
15659
+ map.setPaintProperty(this.HEATMAP_LAYER_ID, 'heatmap-radius', settings.radius);
15660
+ map.setPaintProperty(this.HEATMAP_LAYER_ID, 'heatmap-opacity', settings.opacity);
15661
+ }
15662
+ break;
15663
+ }
15664
+ }
15665
+ onReady(map, svc) {
15666
+ this.map = map;
15667
+ this.svc = svc;
15668
+ this.create();
15669
+ }
15670
+ reset() {
15671
+ this.count = 0;
15672
+ this.total = 0;
15673
+ this.legends = [];
15674
+ this.currentFilter = undefined;
15675
+ }
15676
+ clear() {
15677
+ if (!this.map) {
15678
+ return;
15679
+ }
15680
+ // Remove all layers for this instance
15681
+ RemoveLayer(this.map, this.CIRCLE_LAYER_ID);
15682
+ RemoveLayer(this.map, this.ICON_LAYER_ID);
15683
+ RemoveLayer(this.map, this.HEATMAP_LAYER_ID);
15684
+ this.map.off('click', this.CIRCLE_LAYER_ID, this.onClick);
15685
+ this.map.off('click', this.ICON_LAYER_ID, this.onClick);
15686
+ this.map.off('touchend', this.CIRCLE_LAYER_ID, this.onClick);
15687
+ this.map.off('touchend', this.ICON_LAYER_ID, this.onClick);
15688
+ this.count = 0;
15689
+ this.currentFilter = undefined;
15690
+ }
15691
+ updateLegends() { }
15692
+ // Method to create PMTiles layers
15693
+ create() {
15694
+ if (!this.map) {
15695
+ return;
15696
+ }
15697
+ const map = this.map;
15698
+ const settings = this.settings();
15699
+ // Add PMTiles source only if it doesn't exist (shared across instances)
15700
+ AddSource(map, this.SOURCE_ID, {
15701
+ type: 'vector',
15702
+ url: `pmtiles://${NaicsMapper.pmtilesUrl}`,
15703
+ });
15704
+ // Add visualization layer based on type
15705
+ this.addVisualizationLayer(map);
15706
+ // Add cluster layers if clustering is enabled
15707
+ // this.addClusterLayers(map);
15708
+ // Add popup on click
15709
+ map.on('click', this.CIRCLE_LAYER_ID, this.onClick);
15710
+ map.on('click', this.ICON_LAYER_ID, this.onClick);
15711
+ map.on('touchend', this.CIRCLE_LAYER_ID, this.onClick);
15712
+ map.on('touchend', this.ICON_LAYER_ID, this.onClick);
15713
+ }
15714
+ getActiveLayerId() {
15715
+ const settings = this.settings();
15716
+ switch (settings.type) {
15717
+ case 'circle':
15718
+ return this.CIRCLE_LAYER_ID;
15719
+ case 'icon':
15720
+ return this.ICON_LAYER_ID;
15721
+ case 'heatmap':
15722
+ return this.HEATMAP_LAYER_ID;
15723
+ default:
15724
+ return this.CIRCLE_LAYER_ID;
15725
+ }
15726
+ }
15727
+ async loadIcon() {
15728
+ if (!this.map) {
15729
+ return;
15730
+ }
15731
+ const map = this.map;
15732
+ const settings = this.settings();
15733
+ if (settings.iconUrl && !map.hasImage(settings.iconName)) {
15734
+ await mapboxloadImage(map, {
15735
+ name: settings.iconName,
15736
+ url: settings.iconUrl,
15737
+ sdf: settings.iconSdf,
15738
+ });
15739
+ }
15740
+ }
15741
+ async addVisualizationLayer(map) {
15742
+ const settings = this.settings();
15743
+ // Remove existing layers that ar not needed
15744
+ if (settings.type !== 'circle')
15745
+ RemoveLayer(map, this.CIRCLE_LAYER_ID);
15746
+ if (settings.type !== 'icon')
15747
+ RemoveLayer(map, this.ICON_LAYER_ID);
15748
+ if (settings.type !== 'heatmap')
15749
+ RemoveLayer(map, this.HEATMAP_LAYER_ID);
15750
+ // Add layer based on type
15751
+ switch (settings.type) {
15752
+ case 'circle':
15753
+ AddLayer(map, {
15754
+ id: this.CIRCLE_LAYER_ID,
15755
+ type: 'circle',
15756
+ source: this.SOURCE_ID,
15757
+ 'source-layer': this.sourceLayer,
15758
+ layout: {
15759
+ visibility: settings.visible ? 'visible' : 'none',
15760
+ },
15761
+ paint: {
15762
+ 'circle-radius': settings.radius,
15763
+ 'circle-color': settings.color,
15764
+ 'circle-opacity': settings.opacity,
15765
+ 'circle-stroke-width': settings.strokeWidth,
15766
+ 'circle-stroke-color': settings.strokeColor,
15767
+ },
15768
+ }, StandardLayersMapper.POINTS);
15769
+ break;
15770
+ case 'icon':
15771
+ // Load icon image if URL is provided and not already loaded
15772
+ await this.loadIcon();
15773
+ AddLayer(map, {
15774
+ id: this.ICON_LAYER_ID,
15775
+ type: 'symbol',
15776
+ source: this.SOURCE_ID,
15777
+ 'source-layer': this.sourceLayer,
15778
+ layout: {
15779
+ visibility: settings.visible ? 'visible' : 'none',
15780
+ 'icon-image': settings.iconName,
15781
+ 'icon-size': settings.iconSize,
15782
+ 'icon-allow-overlap': settings.iconAllowOverlap,
15783
+ },
15784
+ paint: {
15785
+ // Additional paint properties can be added here if needed
15786
+ 'icon-color': settings.color,
15787
+ }
15788
+ }, StandardLayersMapper.POINTS);
15789
+ break;
15790
+ case 'heatmap':
15791
+ AddLayer(map, {
15792
+ id: this.HEATMAP_LAYER_ID,
15793
+ type: 'heatmap',
15794
+ source: this.SOURCE_ID,
15795
+ 'source-layer': this.sourceLayer,
15796
+ layout: {
15797
+ visibility: settings.visible ? 'visible' : 'none',
15798
+ },
15799
+ paint: {
15800
+ 'heatmap-weight': 1,
15801
+ 'heatmap-intensity': 1,
15802
+ 'heatmap-color': [
15803
+ 'interpolate',
15804
+ ['linear'],
15805
+ ['heatmap-density'],
15806
+ 0, 'rgba(0, 0, 255, 0)',
15807
+ 0.1, 'royalblue',
15808
+ 0.3, 'cyan',
15809
+ 0.5, 'lime',
15810
+ 0.7, 'yellow',
15811
+ 1, 'red',
15812
+ ],
15813
+ 'heatmap-radius': settings.radius,
15814
+ 'heatmap-opacity': settings.opacity,
15815
+ },
15816
+ }, StandardLayersMapper.POLYGONS_ACTIVE);
15817
+ break;
15818
+ }
15819
+ // Reapply filter if exists
15820
+ if (this.currentFilter) {
15821
+ this.filterByNaicsCode(map, this.currentFilter);
15822
+ }
15823
+ }
15824
+ // Method to filter by NAICS code (startsWith match)
15825
+ filterByNaicsCode(map, naicsCode) {
15826
+ this.currentFilter = naicsCode;
15827
+ const filter = naicsCode ? ['==', ['slice', ['get', 'NAICS_CODE'], 0, naicsCode.length], naicsCode] : null;
15828
+ const layerId = this.getActiveLayerId();
15829
+ if (map.getLayer(layerId)) {
15830
+ map.setFilter(layerId, filter);
15831
+ }
15832
+ }
15833
+ // Get current settings
15834
+ getSettings() {
15835
+ return { ...this.settings() };
15836
+ }
15837
+ onClick = (e) => {
15838
+ if (!this.map) {
15839
+ return;
15840
+ }
15841
+ const map = this.map;
15842
+ let features;
15843
+ const settings = this.settings();
15844
+ if (settings.type == 'circle') {
15845
+ features = this.map.queryRenderedFeatures(e.point, {
15846
+ layers: [this.CIRCLE_LAYER_ID],
15847
+ });
15848
+ }
15849
+ else if (settings.type == 'icon') {
15850
+ features = this.map.queryRenderedFeatures(e.point, {
15851
+ layers: [this.ICON_LAYER_ID],
15852
+ });
15853
+ }
15854
+ if (!features || features.length == 0) {
15855
+ return;
15856
+ }
15857
+ if (features.length > 0) {
15858
+ const item = features[0];
15859
+ if (item) {
15860
+ this.renderPopup(item).then((html) => {
15861
+ if (!html) {
15862
+ return;
15863
+ }
15864
+ new Popup().setLngLat(e.lngLat).setHTML(html).addTo(map);
15865
+ });
15866
+ }
15867
+ }
15868
+ };
15869
+ async renderPopup(f) {
15870
+ if (!f || !f.properties) {
15871
+ return undefined;
15872
+ }
15873
+ const item = f.properties;
15874
+ let html = `<b>${item['COMPANY_NAME'] || 'MISSING NAME'}</b><br>`;
15875
+ if (item?.['WEB_ADDRESS']) {
15876
+ let url = item['WEB_ADDRESS'].startsWith('http') ? item['WEB_ADDRESS'] : 'https://' + item['WEB_ADDRESS'];
15877
+ html += `<a href='${url}' target='_blank'>${url}</a><br />`;
15878
+ }
15879
+ if (item?.['ADDRESS']) {
15880
+ html += `${item?.['ADDRESS']}<br />`;
15881
+ }
15882
+ if (item?.['CITY'] && item?.['STATE']) {
15883
+ html += `${item?.['CITY']}, ${item?.['STATE']}<br />`;
15884
+ }
15885
+ if (item?.['PHONE']) {
15886
+ html += `${item?.['PHONE']}<br /><br />`;
15887
+ }
15888
+ if (item?.['SALES_VOLUME']) {
15889
+ html += `<b>Sales: </b>${item?.['SALES_VOLUME']}<br />`;
15890
+ }
15891
+ if (item?.['NUMBER_OF_EMPLOYEES']) {
15892
+ html += `<b>Employees: </b>${item?.['NUMBER_OF_EMPLOYEES']}<br />`;
15893
+ }
15894
+ if (item?.['NAICS_CODE']) {
15895
+ html += `<b>NAICS: </b>${item?.['NAICS_CODE']} ${item?.['NAICS_DESCRIPTION']} <br />`;
15896
+ }
15897
+ if (item?.['SIC_CODE1']) {
15898
+ html += `<b>SIC: </b>${item?.['SIC_CODE1']} ${item?.['SIC1_DESCRIPTION']}`;
15899
+ }
15900
+ return html;
15901
+ }
15902
+ }
15903
+
15614
15904
  class SimpleMapper {
15615
15905
  reset() {
15616
15906
  if (this.map) {
@@ -15992,6 +16282,58 @@ class WatershedSettings {
15992
16282
  autoSelectLayer = false;
15993
16283
  }
15994
16284
 
16285
+ function isGeoloader(obj) {
16286
+ return typeof obj === 'object' &&
16287
+ obj !== null &&
16288
+ typeof obj.LoadBoundary === 'function';
16289
+ }
16290
+ function isMultiPolygon(obj) {
16291
+ return typeof obj === 'object' &&
16292
+ obj !== null &&
16293
+ obj.type === 'MultiPolygon';
16294
+ }
16295
+ function isPolygon(obj) {
16296
+ return typeof obj === 'object' &&
16297
+ obj !== null &&
16298
+ obj.type === 'Polygon';
16299
+ }
16300
+ function isNumber2DArray(input) {
16301
+ return Array.isArray(input) &&
16302
+ input.every(row => Array.isArray(row) &&
16303
+ row.every(cell => typeof cell === 'number'));
16304
+ }
16305
+ function isNumber3DArray(input) {
16306
+ return Array.isArray(input) &&
16307
+ input.every(matrix => Array.isArray(matrix) &&
16308
+ matrix.every(row => Array.isArray(row) &&
16309
+ row.every(cell => typeof cell === 'number')));
16310
+ }
16311
+ function toMultiPolygon(geom) {
16312
+ if (isMultiPolygon(geom)) {
16313
+ return geom;
16314
+ }
16315
+ if (isPolygon(geom)) {
16316
+ return {
16317
+ type: 'MultiPolygon',
16318
+ coordinates: [geom.coordinates]
16319
+ };
16320
+ }
16321
+ if (isNumber2DArray(geom)) {
16322
+ let raw = [geom];
16323
+ const coords = raw;
16324
+ return {
16325
+ type: 'MultiPolygon',
16326
+ coordinates: coords
16327
+ };
16328
+ }
16329
+ let raw = geom;
16330
+ const coords = raw;
16331
+ return {
16332
+ type: 'MultiPolygon',
16333
+ coordinates: coords
16334
+ };
16335
+ }
16336
+
15995
16337
  class HttpBoundaryLoader {
15996
16338
  static NewCustom(http, path) {
15997
16339
  const loader = new HttpBoundaryLoader(http);
@@ -16096,5 +16438,5 @@ class HttpBoundaryLoader {
16096
16438
  * Generated bundle index. Do not edit.
16097
16439
  */
16098
16440
 
16099
- export { AddLayer, AddSource, AreaMapperMapper, BackgroundMaskMapper, BaseMapLight, BasemapSelect, BasemapSelectMenu, CensusTractMapper, CroplandDataLayerMapper, CroplandDataLayerSettings, CroplandLegend, DEFAULT_GLYPHS, DrawingMapper, EsriMapper, EsriSettings, HardinessMapper, HardinessSettings, HttpBoundaryLoader, MapAreaSelectComponent, MapComponent, MapSelectionService, MapService, MapStyles, MapboxMapperGroup, NAASMapper, NAASSettings, NoOpMapper, RemoveLayer, RemoveSource, SaveMap, SelectMode, SimpleMapper, StandardLayersMapper, Styles, VectorTileServerMapper, WatershedMapper, WatershedSettings, discoverLayers, isGeoloader, isMultiPolygon, isNumber2DArray, isNumber3DArray, isPolygon, mapboxLoadImages, mapboxloadImage, pmtilesPixelInfo, sampleTilesForLayers, simpleClone, toMultiPolygon, trySync };
16441
+ export { AddLayer, AddSource, AreaMapperMapper, BackgroundMaskMapper, BackgroundMaskSettings, BaseMapLight, BasemapSelect, BasemapSelectMenu, CensusTractMapper, CroplandDataLayerMapper, CroplandDataLayerSettings, CroplandLegend, DEFAULT_GLYPHS, DrawingMapper, EsriMapper, EsriSettings, HardinessMapper, HardinessSettings, HttpBoundaryLoader, MapAreaSelectComponent, MapComponent, MapSelectionService, MapService, MapStyles, MapboxMapperGroup, NAASMapper, NAASSettings, NaicsMapper, NaicsMapperSettings, NoOpMapper, RemoveLayer, RemoveSource, SaveMap, SelectMode, SimpleMapper, StandardLayersMapper, Styles, VectorTileServerMapper, WatershedMapper, WatershedSettings, discoverLayers, isGeoloader, isMultiPolygon, isNumber2DArray, isNumber3DArray, isPolygon, mapboxLoadImages, mapboxloadImage, pmtilesPixelInfo, sampleTilesForLayers, simpleClone, toMultiPolygon, trySync };
16100
16442
  //# sourceMappingURL=foodmarketmaker-mapag.mjs.map