From 40ba6aabd56a76e1d6b5c518cd130f68e792f43e Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Wed, 25 Oct 2023 14:51:29 -0500 Subject: [PATCH 01/22] svg + tile selection --- src/components/Map.jsx | 163 +++++++++++++------- src/components/Maps.css | 5 +- src/data/maps.json | 27 ++-- src/modules/leaflet-control-groupedlayer.js | 18 ++- 4 files changed, 139 insertions(+), 74 deletions(-) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index 449c64304..c6095b6a1 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -303,7 +303,7 @@ function Map() { position: 'topleft', collapsed: true, groupCheckboxes: true, - exclusiveGroups: [tMaps('Levels')], + exclusiveOptionalGroups: [tMaps('Levels')], }).addTo(map); map.layerControl = layerControl; map.addControl(new L.Control.Fullscreen({ @@ -327,82 +327,125 @@ function Map() { //L.control.scale({position: 'bottomright'}).addTo(map); - let baseLayer; const bounds = getBounds(mapData); + const layerOptions = { + heightRange: mapData.heightRange || [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER], + type: 'map-layer', + }; + let tileLayer = false; + const baseLayers = []; + if (mapData.tilePath) { + tileLayer = L.tileLayer(mapData.tilePath || `https://assets.tarkov.dev/maps/${mapData.normalizedName}/{z}/{x}/{y}.png`, { + tileSize: mapData.tileSize, + bounds, + ...layerOptions, + }); + layerControl.addBaseLayer(tileLayer, tMaps('Satellite')); + baseLayers.push(tileLayer); + } + + let svgLayer = false; if (mapData.svgPath) { // if (process.env.NODE_ENV === "development") { // mapData.svgPath = mapData.svgPath.replace("assets.tarkov.dev/maps/svg", "raw.githubusercontent.com/the-hideout/tarkov-dev-src-maps/main/interactive"); // } - baseLayer = L.imageOverlay(mapData.svgPath, bounds, { - heightRange: mapData.heightRange || [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER], - type: 'layer-svg' - }); + //baseLayer = L.imageOverlay(mapData.svgPath, bounds, layerOptions); + svgLayer = L.imageOverlay(mapData.svgPath, bounds, layerOptions); + layerControl.addBaseLayer(svgLayer, tMaps('Vector')); + baseLayers.push(svgLayer); } - else { - baseLayer = L.tileLayer(mapData.mapPath || `https://assets.tarkov.dev/maps/${mapData.normalizedName}/{z}/{x}/{y}.png`, { - tileSize: mapData.tileSize, - bounds: bounds, - heightRange: mapData.heightRange || [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER], - type: 'layer-tile', - }); - } - baseLayer.addTo(map); - //layerControl.addBaseLayer(baseLayer, tMaps('Base')); - // Add map layers - if (mapData.layers) { - for (const layer of mapData.layers) { - let tileLayer; - - if (layer.svgPath) { - // if (process.env.NODE_ENV === "development") { - // layer.svgPath = layer.svgPath.replace("assets.tarkov.dev/maps/svg", "raw.githubusercontent.com/the-hideout/tarkov-dev-src-maps/main/interactive"); - // } - tileLayer = L.imageOverlay(layer.svgPath, bounds, { - heightRange: layer.heightRange || baseLayer.options?.heightRange, - type: 'layer-svg', - overlay: Boolean(layer.heightRange), - }); + for (const baseLayer of baseLayers) { + if (mapData.layers?.length === 0) { + break; + } + const svgParent = baseLayer._url.endsWith('.svg'); + let selectedLayer = ''; + baseLayer.on('add', () => { + const existingLayers = Object.values(layerControl._layers).filter(l => l.layer.options.type === 'map-layer' && !baseLayers.includes(l.layer)).map(l => l.layer); + for (const existingLayer of existingLayers) { + const svgOverlay = Boolean(existingLayer._url.endsWith('.svg')); + if (svgParent === svgOverlay) { + continue; + } + layerControl.removeLayer(existingLayer); + if (map.hasLayer(existingLayer)) { + map.removeLayer(existingLayer); + selectedLayer = existingLayer.options.name; + } } - else { - tileLayer = L.tileLayer(layer.path, { - tileSize: mapData.tileSize, - bounds: bounds, + for (const layer of mapData.layers) { + let heightLayer; + let svgOverlay = false; + + const layerOptions = { + name: layer.name, heightRange: layer.heightRange || baseLayer.options?.heightRange, - type: 'layer-tile', + type: 'map-layer', overlay: Boolean(layer.heightRange), - }); - } - layerControl.addOverlay(tileLayer, tMaps(layer.name), tMaps('Levels')); - if (layer.show) { - tileLayer.addTo(map); - } - - tileLayer.on('add', () => { - if (layer.heightRange) { - for (const marker of Object.values(map._layers)) { - checkMarkerForActiveLayers(marker); - } + }; + + if (baseLayer._url.endsWith('.svg') && layer.svgPath) { + // if (process.env.NODE_ENV === "development") { + // layer.svgPath = layer.svgPath.replace("assets.tarkov.dev/maps/svg", "raw.githubusercontent.com/the-hideout/tarkov-dev-src-maps/main/interactive"); + // } + heightLayer = L.imageOverlay(layer.svgPath, bounds, layerOptions); + svgOverlay = true; } - if (baseLayer.options?.type === 'layer-svg' && !layer.show) { - baseLayer._image.classList.add('off-level'); + else if (!baseLayer._url.endsWith('.svg') && layer.tilePath) { + heightLayer = L.tileLayer(layer.tilePath, { + tileSize: mapData.tileSize, + bounds, + ...layerOptions, + }); } - }); - tileLayer.on('remove', () => { - const heightLayer = Object.values(map._layers).findLast(l => l.options?.heightRange); - if (heightLayer) { - for (const marker of Object.values(map._layers)) { - checkMarkerForActiveLayers(marker); + + layerControl.addOverlay(heightLayer, tMaps(layer.name), tMaps('Levels')); + + heightLayer.on('add', () => { + if (layer.heightRange) { + for (const marker of Object.values(map._layers)) { + checkMarkerForActiveLayers(marker); + } + } + // mapLayer._image means svg + if (baseLayer._image && !layer.show) { + baseLayer._image.classList.add('off-level'); + } else if (baseLayer._container && !layer.show) { + baseLayer._container.classList.add('off-level'); } - const layers = Object.values(map._layers).filter(l => l.options.type === 'layer-svg'); - if (layers.length === 1 && baseLayer.options.type === 'layer-svg') { - baseLayer._image.classList.remove('off-level'); + }); + heightLayer.on('remove', () => { + const heightLayer = Object.values(map._layers).findLast(l => l.options?.heightRange); + if (heightLayer) { + for (const marker of Object.values(map._layers)) { + checkMarkerForActiveLayers(marker); + } + const layers = Object.values(map._layers).filter(l => l.options.type === 'map-layer'); + if (layers.length !== 1) { + return; + } + if (baseLayer._image) { + baseLayer._image.classList.remove('off-level'); + } else if (baseLayer._container) { + baseLayer._container.classList.remove('off-level'); + } } + }); + + if (selectedLayer === layer.name) { + heightLayer.addTo(map); + } else if (!selectedLayer && layer.show) { + heightLayer.addTo(map); } - }); - } + } + }); } + const baseLayer = tileLayer ? tileLayer : svgLayer; + + baseLayer.addTo(map); + const categories = { 'extract_pmc': t('PMC'), 'extract_shared': t('Shared'), diff --git a/src/components/Maps.css b/src/components/Maps.css index ca0347248..03fbe89b7 100644 --- a/src/components/Maps.css +++ b/src/components/Maps.css @@ -86,12 +86,13 @@ /* elevation visability */ +.leaflet-layer.off-level > .leaflet-tile-container, div.leaflet-pane.leaflet-overlay-pane > img.off-level { - opacity: 50%; + opacity: 20%; } div.awesome-marker.off-level { - opacity: 50%; + opacity: 20%; z-index: -9999 !important; } diff --git a/src/data/maps.json b/src/data/maps.json index 8552a449b..3a78116ca 100644 --- a/src/data/maps.json +++ b/src/data/maps.json @@ -69,31 +69,36 @@ { "key": "customs", "projection": "interactive", + "tileSize": 256, "minZoom": 2, - "maxZoom": 7, - "transform": [0.182815, 0, 0.182815, 0], + "maxZoom": 6, + "transform": [0.239, 168.6, 0.239, 73.1], "coordinateRotation": 180, "bounds": [[698, -307], [-371, 237]], - "heightRange": [-13, 2.7], + "heightRange": [0, 2.7], "author": "Shebuka", "authorLink": "https://github.com/TarkovTracker/tarkovdata/", "svgPath": "https://assets.tarkov.dev/maps/svg/Customs-Ground_Level.svg", + "tilePath": "https://assets.tarkov.dev/maps/customs/main/{z}/{x}/{y}.png", "layers": [ { "name": "Underground", "svgPath": "https://assets.tarkov.dev/maps/svg/Customs-Underground_Level.svg", + "tilePath": "https://assets.tarkov.dev/maps/customs/underground/{z}/{x}/{y}.png", "show": false, - "heightRange": [-10000, -13] + "heightRange": [-10000, 0] }, { "name": "2nd Floor", "svgPath": "https://assets.tarkov.dev/maps/svg/Customs-Second_Floor.svg", + "tilePath": "https://assets.tarkov.dev/maps/customs/2nd/{z}/{x}/{y}.png", "show": false, "heightRange": [2.7, 6.5] }, { "name": "3rd Floor", "svgPath": "https://assets.tarkov.dev/maps/svg/Customs-Third_Floor.svg", + "tilePath": "https://assets.tarkov.dev/maps/customs/3rd/{z}/{x}/{y}.png", "show": false, "heightRange": [6.5, 10000] } @@ -130,23 +135,23 @@ "heightRange": [-1, 3], "author": "Tarkov.dev", "authorLink": "https://tarkov.dev", - "mapPath": "https://assets.tarkov.dev/maps/factory_v2/main/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/factory_v2/main/{z}/{x}/{y}.png", "layers": [ { "name": "2nd Floor", - "path": "https://assets.tarkov.dev/maps/factory_v2/2nd/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/factory_v2/2nd_notint/{z}/{x}/{y}.png", "show": false, "heightRange": [3, 6] }, { "name": "3rd Floor", - "path": "https://assets.tarkov.dev/maps/factory_v2/3rd/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/factory_v2/3rd_notint/{z}/{x}/{y}.png", "show": false, "heightRange": [6, 10000] }, { "name": "Tunnels", - "path": "https://assets.tarkov.dev/maps/factory_v2/tunnels/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/factory_v2/tunnels_notint/{z}/{x}/{y}.png", "show": false, "heightRange": [-10000, -1] } @@ -214,18 +219,18 @@ "bounds": [[-91, -477], [-287, -193]], "author": "Tarkov.dev", "authorLink": "https://tarkov.dev", - "mapPath": "https://assets.tarkov.dev/maps/labs_v2/1st/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/labs_v2/1st/{z}/{x}/{y}.png", "heightRange": [-0.9, 3], "layers": [ { "name": "Second Level", - "path": "https://assets.tarkov.dev/maps/labs_v2/2nd/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/labs_v2/2nd/{z}/{x}/{y}.png", "show": false, "heightRange": [3, 10000] }, { "name": "Technical", - "path": "https://assets.tarkov.dev/maps/labs_v2/technical/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/labs_v2/technical/{z}/{x}/{y}.png", "show": false, "heightRange": [-10000, -0.9] } diff --git a/src/modules/leaflet-control-groupedlayer.js b/src/modules/leaflet-control-groupedlayer.js index 5550c8e88..a4c5a0082 100644 --- a/src/modules/leaflet-control-groupedlayer.js +++ b/src/modules/leaflet-control-groupedlayer.js @@ -36,6 +36,7 @@ L.Control.GroupedLayers = L.Control.extend({ position: 'topright', autoZIndex: true, exclusiveGroups: [], + exclusiveOptionalGroups: [], groupCheckboxes: false, groupsCollapsable: false, groupsExpandedClass: "leaflet-control-layers-group-collapse-default", @@ -187,11 +188,13 @@ L.Control.GroupedLayers = L.Control.extend({ } var exclusive = (this._indexOf(this.options.exclusiveGroups, group) !== -1); + var exclusiveOptional = (this._indexOf(this.options.exclusiveOptionalGroups, group) !== -1); _layer.group = { name: group, id: groupId, - exclusive: exclusive + exclusive: exclusive, + exclusiveOptional: exclusiveOptional, }; if (this.options.autoZIndex && layer.setZIndex) { @@ -273,6 +276,16 @@ L.Control.GroupedLayers = L.Control.extend({ }, _createRadioElement: function (name, checked) { + var radio = document.createElement('input'); + radio.type = 'radio'; + radio.name = name; + radio.className = 'leaflet-control-layers-selector'; + radio.checked = checked; + + return radio; + }, + + _createExclusiveCheckElement: function (name, checked) { var radio = document.createElement('input'); radio.type = 'checkbox'; radio.name = name; @@ -306,6 +319,9 @@ L.Control.GroupedLayers = L.Control.extend({ if (obj.group.exclusive) { groupRadioName = 'leaflet-exclusive-group-layer-' + obj.group.id; input = this._createRadioElement(groupRadioName, checked); + } else if (obj.group.exclusiveOptional) { + groupRadioName = 'leaflet-exclusive-group-layer-' + obj.group.id; + input = this._createExclusiveCheckElement(groupRadioName, checked); } else { input = document.createElement('input'); input.type = 'checkbox'; From d5ae5a86c9635aeb6a8298421d6881969af7a11a Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Wed, 25 Oct 2023 14:55:36 -0500 Subject: [PATCH 02/22] linting --- src/components/Map.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index c6095b6a1..0d714a752 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -359,9 +359,10 @@ function Map() { if (mapData.layers?.length === 0) { break; } - const svgParent = baseLayer._url.endsWith('.svg'); + let selectedLayer = ''; baseLayer.on('add', () => { + const svgParent = baseLayer._url.endsWith('.svg'); const existingLayers = Object.values(layerControl._layers).filter(l => l.layer.options.type === 'map-layer' && !baseLayers.includes(l.layer)).map(l => l.layer); for (const existingLayer of existingLayers) { const svgOverlay = Boolean(existingLayer._url.endsWith('.svg')); @@ -376,7 +377,6 @@ function Map() { } for (const layer of mapData.layers) { let heightLayer; - let svgOverlay = false; const layerOptions = { name: layer.name, @@ -390,7 +390,6 @@ function Map() { // layer.svgPath = layer.svgPath.replace("assets.tarkov.dev/maps/svg", "raw.githubusercontent.com/the-hideout/tarkov-dev-src-maps/main/interactive"); // } heightLayer = L.imageOverlay(layer.svgPath, bounds, layerOptions); - svgOverlay = true; } else if (!baseLayer._url.endsWith('.svg') && layer.tilePath) { heightLayer = L.tileLayer(layer.tilePath, { From 572f101faf576089800f7577ef991fdf40b14f35 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Wed, 25 Oct 2023 14:57:16 -0500 Subject: [PATCH 03/22] Vector -> Abstract --- src/components/Map.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index 0d714a752..9442740d7 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -351,7 +351,7 @@ function Map() { // } //baseLayer = L.imageOverlay(mapData.svgPath, bounds, layerOptions); svgLayer = L.imageOverlay(mapData.svgPath, bounds, layerOptions); - layerControl.addBaseLayer(svgLayer, tMaps('Vector')); + layerControl.addBaseLayer(svgLayer, tMaps('Abstract')); baseLayers.push(svgLayer); } From 610f43b0d7731136f36bcdef84b0cf064c9b43b1 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Wed, 25 Oct 2023 15:05:38 -0500 Subject: [PATCH 04/22] no group checkbox for levels --- src/modules/leaflet-control-groupedlayer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/leaflet-control-groupedlayer.js b/src/modules/leaflet-control-groupedlayer.js index a4c5a0082..1ab87d298 100644 --- a/src/modules/leaflet-control-groupedlayer.js +++ b/src/modules/leaflet-control-groupedlayer.js @@ -356,7 +356,7 @@ L.Control.GroupedLayers = L.Control.extend({ var groupLabel = document.createElement('label'); groupLabel.className = 'leaflet-control-layers-group-label'; - if (obj.group.name !== '' && !obj.group.exclusive) { + if (obj.group.name !== '' && !obj.group.exclusive && !obj.group.exclusiveOptional) { // ------ add a group checkbox with an _onInputClickGroup function if (this.options.groupCheckboxes) { var groupInput = document.createElement('input'); From 4d6acd21556f8bf7233fa9e6277f7829dd8bf6c9 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Wed, 25 Oct 2023 15:19:41 -0500 Subject: [PATCH 05/22] levels on top --- src/components/Map.jsx | 1 + src/modules/leaflet-control-groupedlayer.js | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index 9442740d7..233ba0cff 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -303,6 +303,7 @@ function Map() { position: 'topleft', collapsed: true, groupCheckboxes: true, + groupsCollapsable: true, exclusiveOptionalGroups: [tMaps('Levels')], }).addTo(map); map.layerControl = layerControl; diff --git a/src/modules/leaflet-control-groupedlayer.js b/src/modules/leaflet-control-groupedlayer.js index 1ab87d298..68330e0a5 100644 --- a/src/modules/leaflet-control-groupedlayer.js +++ b/src/modules/leaflet-control-groupedlayer.js @@ -42,13 +42,7 @@ L.Control.GroupedLayers = L.Control.extend({ groupsExpandedClass: "leaflet-control-layers-group-collapse-default", groupsCollapsedClass: "leaflet-control-layers-group-expand-default", sortFunction: function (nameA, nameB) { - if (nameA < nameB) { - return -1; - } else if (nameB < nameA) { - return 1; - } else { - return 0; - } + return nameA.localeCompare(nameB); } }, @@ -220,6 +214,12 @@ L.Control.GroupedLayers = L.Control.extend({ if (this.options.sortGroups) { this._layers.sort(L.bind(function (a, b) { + if (a.group.exclusiveOptional && !b.group.exclusiveOptional) { + return -1; + } + if (!a.group.exclusiveOptional && b.group.exclusiveOptional) { + return 1; + } return this.options.sortFunction(a.group.name, b.group.name); }, this)); } @@ -371,7 +371,7 @@ L.Control.GroupedLayers = L.Control.extend({ if (this.options.groupsCollapsable){ groupContainer.classList.add("group-collapsable"); - groupContainer.classList.add("collapsed"); + //groupContainer.classList.add("collapsed"); var groupMin = document.createElement('span'); groupMin.className = 'leaflet-control-layers-group-collapse '+this.options.groupsExpandedClass; From 43697b120cc7e040da53b281140703ae7abcf518 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Wed, 25 Oct 2023 15:31:40 -0500 Subject: [PATCH 06/22] fix click to activate layer --- src/components/Map.jsx | 2 +- src/data/maps.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index 233ba0cff..2944bb6e8 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -207,7 +207,7 @@ function activateMarkerLayer(event) { for (const layer of activeLayers) { layer.removeFrom(marker._map); } - const heightLayers = marker._map.layerControl._layers.filter(l => l.group.exclusive && l.layer.options.heightRange).map(l => l.layer); + const heightLayers = marker._map.layerControl._layers.filter(l => l.layer.options.heightRange && l.layer.options.overlay).map(l => l.layer); for (const layer of heightLayers) { const heightRange = layer.options.heightRange; if (top >= heightRange[0] && bottom < heightRange[1]) { diff --git a/src/data/maps.json b/src/data/maps.json index 3a78116ca..9876e9daa 100644 --- a/src/data/maps.json +++ b/src/data/maps.json @@ -75,7 +75,7 @@ "transform": [0.239, 168.6, 0.239, 73.1], "coordinateRotation": 180, "bounds": [[698, -307], [-371, 237]], - "heightRange": [0, 2.7], + "heightRange": [-0.1, 2.7], "author": "Shebuka", "authorLink": "https://github.com/TarkovTracker/tarkovdata/", "svgPath": "https://assets.tarkov.dev/maps/svg/Customs-Ground_Level.svg", @@ -86,7 +86,7 @@ "svgPath": "https://assets.tarkov.dev/maps/svg/Customs-Underground_Level.svg", "tilePath": "https://assets.tarkov.dev/maps/customs/underground/{z}/{x}/{y}.png", "show": false, - "heightRange": [-10000, 0] + "heightRange": [-10000, -0.1] }, { "name": "2nd Floor", From c0483c805acb29b57b697c9d33948c8382bdac36 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Wed, 25 Oct 2023 15:37:08 -0500 Subject: [PATCH 07/22] preserve layers checked between toggles --- src/components/Map.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index 2944bb6e8..f52b99b97 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -399,8 +399,6 @@ function Map() { ...layerOptions, }); } - - layerControl.addOverlay(heightLayer, tMaps(layer.name), tMaps('Levels')); heightLayer.on('add', () => { if (layer.heightRange) { @@ -438,6 +436,8 @@ function Map() { } else if (!selectedLayer && layer.show) { heightLayer.addTo(map); } + + layerControl.addOverlay(heightLayer, tMaps(layer.name), tMaps('Levels')); } }); } From 1ee6390d4b91a17c013d2bc17fad31023f9c00b2 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Wed, 25 Oct 2023 18:01:22 -0500 Subject: [PATCH 08/22] restrict layers to bounds --- src/components/Map.jsx | 68 +++++++++++++++++++++++++++++++----------- src/data/maps.json | 10 +++++-- 2 files changed, 58 insertions(+), 20 deletions(-) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index f52b99b97..71d9e3141 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -116,32 +116,66 @@ function checkMarkerBounds(position, markerBounds) { if (position.z < markerBounds.BR.z) markerBounds.BR.z = position.z; } -function getBounds(mapData) { - if (!mapData.bounds) { +function getBounds(bounds) { + if (!bounds) { return undefined; } - return [[mapData.bounds[0][1], mapData.bounds[0][0]], [mapData.bounds[1][1], mapData.bounds[1][0]]]; + return L.latLngBounds([bounds[0][1], bounds[0][0]], [bounds[1][1], bounds[1][0]]); + //return [[bounds[0][1], bounds[0][0]], [bounds[1][1], bounds[1][0]]]; +} + +function markerIsOnLayer(marker, layer) { + var top = marker.options.top || marker.options.position.y; + var bottom = marker.options.bottom || marker.options.position.y; + const heightRange = layer.options.heightRange; + if (top >= heightRange[0] && bottom < heightRange[1]) { + if (layer.options.layerBounds) { + let inBounds = false; + for (const boundsArray of layer.options.layerBounds) { + const bounds = getBounds(boundsArray); + if (bounds.contains(pos(marker.options.position))) { + inBounds = true; + break; + } + } + return inBounds; + } + return true; + } + return false; } function markerIsOnActiveLayer(marker) { if (!marker.options.position) { return true; } - var top = marker.options.top || marker.options.position.y; - var bottom = marker.options.bottom || marker.options.position.y; - let activeLayers = Object.values(marker._map._layers).filter(l => l.options?.heightRange); + + const map = marker._map; + + const overlays = map.layerControl._layers.map(l => l.layer).filter(l => Boolean(l.options.heightRange)); + let onAbsentLayer = false; + for (const layer of overlays) { + const onOverlay = markerIsOnLayer(marker, layer); + if (onOverlay && !map.hasLayer(layer) && layer.options.layerBounds) { + onAbsentLayer = true; + break; + } + } + if (onAbsentLayer) { + return false; + } + + let activeLayers = Object.values(map._layers).filter(l => l.options?.heightRange); if (activeLayers.some(l => l.options.overlay)) { activeLayers = activeLayers.filter(l => l.options.overlay); } - let onLevel = false; + for (const layer of activeLayers) { - const heightRange = layer.options.heightRange; - if (top >= heightRange[0] && bottom < heightRange[1]) { - onLevel = true; - break; + if (markerIsOnLayer(marker, layer)) { + return true; } } - return onLevel; + return false; } function checkMarkerForActiveLayers(event) { @@ -198,8 +232,6 @@ function toggleForceOutline(event) { function activateMarkerLayer(event) { const marker = event.target || event; - var top = marker.options.top || marker.options.position.y; - var bottom = marker.options.bottom || marker.options.position.y; if (markerIsOnActiveLayer(marker)) { return; } @@ -209,8 +241,7 @@ function activateMarkerLayer(event) { } const heightLayers = marker._map.layerControl._layers.filter(l => l.layer.options.heightRange && l.layer.options.overlay).map(l => l.layer); for (const layer of heightLayers) { - const heightRange = layer.options.heightRange; - if (top >= heightRange[0] && bottom < heightRange[1]) { + if (markerIsOnLayer(marker, layer)) { layer.addTo(marker._map); break; } @@ -328,7 +359,7 @@ function Map() { //L.control.scale({position: 'bottomright'}).addTo(map); - const bounds = getBounds(mapData); + const bounds = getBounds(mapData.bounds); const layerOptions = { heightRange: mapData.heightRange || [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER], type: 'map-layer', @@ -382,6 +413,7 @@ function Map() { const layerOptions = { name: layer.name, heightRange: layer.heightRange || baseLayer.options?.heightRange, + layerBounds: layer.layerBounds, type: 'map-layer', overlay: Boolean(layer.heightRange), }; @@ -1076,7 +1108,7 @@ function Map() { console.log(`Markers "bounds": [[${markerBounds.BR.x}, ${markerBounds.BR.z}], [${markerBounds.TL.x}, ${markerBounds.TL.z}]] (already rotated, copy/paste to maps.json)`); L.rectangle([pos(markerBounds.TL), pos(markerBounds.BR)], {color: '#ff000055', weight: 1}).addTo(map); - L.rectangle(getBounds(mapData), {color: '#00ff0055', weight: 1}).addTo(map); + L.rectangle(getBounds(mapData.bounds), {color: '#00ff0055', weight: 1}).addTo(map); const positionLayer = L.layerGroup(); const playerIcon = L.AwesomeMarkers.icon({ diff --git a/src/data/maps.json b/src/data/maps.json index 9876e9daa..9a1afa895 100644 --- a/src/data/maps.json +++ b/src/data/maps.json @@ -75,7 +75,7 @@ "transform": [0.239, 168.6, 0.239, 73.1], "coordinateRotation": 180, "bounds": [[698, -307], [-371, 237]], - "heightRange": [-0.1, 2.7], + "heightRange": [-1000, 2.7], "author": "Shebuka", "authorLink": "https://github.com/TarkovTracker/tarkovdata/", "svgPath": "https://assets.tarkov.dev/maps/svg/Customs-Ground_Level.svg", @@ -86,7 +86,13 @@ "svgPath": "https://assets.tarkov.dev/maps/svg/Customs-Underground_Level.svg", "tilePath": "https://assets.tarkov.dev/maps/customs/underground/{z}/{x}/{y}.png", "show": false, - "heightRange": [-10000, -0.1] + "heightRange": [-10000, 0], + "layerBounds": [ + [[635, -137], [620, -125]], + [[473, -122], [458, -110]], + [[349, -88], [323, -32]], + [[219, -158], [193, -137]] + ] }, { "name": "2nd Floor", From cb71e50266640e8f3e4477a3c5f383b0c309523d Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Wed, 25 Oct 2023 20:30:25 -0500 Subject: [PATCH 09/22] only add selctor if multiple maps available --- src/components/Map.jsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index 71d9e3141..788066150 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -372,7 +372,6 @@ function Map() { bounds, ...layerOptions, }); - layerControl.addBaseLayer(tileLayer, tMaps('Satellite')); baseLayers.push(tileLayer); } @@ -383,10 +382,15 @@ function Map() { // } //baseLayer = L.imageOverlay(mapData.svgPath, bounds, layerOptions); svgLayer = L.imageOverlay(mapData.svgPath, bounds, layerOptions); - layerControl.addBaseLayer(svgLayer, tMaps('Abstract')); baseLayers.push(svgLayer); } + // only add selector if there are multiple + if (tileLayer && svgLayer) { + layerControl.addBaseLayer(tileLayer, tMaps('Satellite')); + layerControl.addBaseLayer(svgLayer, tMaps('Abstract')); + } + for (const baseLayer of baseLayers) { if (mapData.layers?.length === 0) { break; From 1263f99ac7c25b18ea8d46e30071578a36291984 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Thu, 26 Oct 2023 09:41:14 -0500 Subject: [PATCH 10/22] no thumbnails for interactive maps --- scripts/generate-thumbnails.js | 6 +++--- src/data/maps.json | 10 +++++----- src/pages/maps/index.js | 7 ++++++- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/scripts/generate-thumbnails.js b/scripts/generate-thumbnails.js index e1c62c5b8..896b32663 100644 --- a/scripts/generate-thumbnails.js +++ b/scripts/generate-thumbnails.js @@ -20,12 +20,12 @@ const sharp = require('sharp'); const image = sharp(mapsPath+fileName).resize(null, maxHeight).jpeg({mozjpeg: true, quality: 90}); await image.toFile(mapsPath+thumbName); } - const mapGroups = JSON.parse(await fs.readFile('./src/data/maps.json')); + /*const mapGroups = JSON.parse(await fs.readFile('./src/data/maps.json')); for (const group of mapGroups) { for (const map of group.maps) { if (map.projection !== 'interactive') continue; - let path = map.mapPath || map.svgPath || `https://assets.tarkov.dev/maps/${group.normalizedName}/{z}/{x}/{y}.png`; + let path = map.tilePath || map.svgPath || `https://assets.tarkov.dev/maps/${group.normalizedName}/{z}/{x}/{y}.png`; path = path.replace(/{[xyz]}/g, '0'); const thumbName = `${group.normalizedName}_thumb.jpg`; try { @@ -43,6 +43,6 @@ const sharp = require('sharp'); console.log(`Asset for ${thumbName} unavailable`) } } - } + }*/ console.timeEnd('Generating thumbnails'); })(); diff --git a/src/data/maps.json b/src/data/maps.json index 9a1afa895..8f680c158 100644 --- a/src/data/maps.json +++ b/src/data/maps.json @@ -72,19 +72,19 @@ "tileSize": 256, "minZoom": 2, "maxZoom": 6, - "transform": [0.239, 168.6, 0.239, 73.1], + "transform": [0.239, 168.65, 0.239, 136.35], "coordinateRotation": 180, "bounds": [[698, -307], [-371, 237]], "heightRange": [-1000, 2.7], "author": "Shebuka", "authorLink": "https://github.com/TarkovTracker/tarkovdata/", "svgPath": "https://assets.tarkov.dev/maps/svg/Customs-Ground_Level.svg", - "tilePath": "https://assets.tarkov.dev/maps/customs/main/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/customs_v2/main/{z}/{x}/{y}.png", "layers": [ { "name": "Underground", "svgPath": "https://assets.tarkov.dev/maps/svg/Customs-Underground_Level.svg", - "tilePath": "https://assets.tarkov.dev/maps/customs/underground/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/customs_v2/underground/{z}/{x}/{y}.png", "show": false, "heightRange": [-10000, 0], "layerBounds": [ @@ -97,14 +97,14 @@ { "name": "2nd Floor", "svgPath": "https://assets.tarkov.dev/maps/svg/Customs-Second_Floor.svg", - "tilePath": "https://assets.tarkov.dev/maps/customs/2nd/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/customs_v2/2nd/{z}/{x}/{y}.png", "show": false, "heightRange": [2.7, 6.5] }, { "name": "3rd Floor", "svgPath": "https://assets.tarkov.dev/maps/svg/Customs-Third_Floor.svg", - "tilePath": "https://assets.tarkov.dev/maps/customs/3rd/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/customs_v2/3rd/{z}/{x}/{y}.png", "show": false, "heightRange": [6.5, 10000] } diff --git a/src/pages/maps/index.js b/src/pages/maps/index.js index ad16909d9..42de26f96 100644 --- a/src/pages/maps/index.js +++ b/src/pages/maps/index.js @@ -73,6 +73,11 @@ function Maps() { .filter(map => map.normalizedName === mapsGroup.normalizedName) .map((map) => { const { displayText, key, imageThumb } = map; + let mapImageLink = `${process.env.PUBLIC_URL}${imageThumb}`; + if (map.projection === 'interactive') { + let path = map.svgPath || map.tilePath || `https://assets.tarkov.dev/maps/${map.normalizedName}/{z}/{x}/{y}.png`; + mapImageLink = path.replace(/{[xyz]}/g, '0'); + } return (

{displayText}

@@ -82,7 +87,7 @@ function Maps() { className="map-image" loading="lazy" title={`Map of ${displayText}`} - src={`${process.env.PUBLIC_URL}${imageThumb}`} + src={mapImageLink} />
From 9e635d17e92acedc2034fc9f1ad6be75d36f7531 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Thu, 26 Oct 2023 09:43:18 -0500 Subject: [PATCH 11/22] fix error --- src/components/Map.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index 788066150..a9f5d6d44 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -411,6 +411,9 @@ function Map() { selectedLayer = existingLayer.options.name; } } + if (!mapData.layers) { + return; + } for (const layer of mapData.layers) { let heightLayer; From 76870e1e80088a6c523ce15af9331be20ebf1ed2 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Thu, 26 Oct 2023 12:51:45 -0500 Subject: [PATCH 12/22] add Factory svg --- src/data/maps.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/data/maps.json b/src/data/maps.json index 8f680c158..48e9d1ca6 100644 --- a/src/data/maps.json +++ b/src/data/maps.json @@ -142,22 +142,26 @@ "author": "Tarkov.dev", "authorLink": "https://tarkov.dev", "tilePath": "https://assets.tarkov.dev/maps/factory_v2/main/{z}/{x}/{y}.png", + "svgPath": "https://assets.tarkov.dev/maps/svg/Factory-Ground_Floor.svg", "layers": [ { "name": "2nd Floor", "tilePath": "https://assets.tarkov.dev/maps/factory_v2/2nd_notint/{z}/{x}/{y}.png", + "svgPath": "https://assets.tarkov.dev/maps/svg/Factory-First_Floor.svg", "show": false, "heightRange": [3, 6] }, { "name": "3rd Floor", "tilePath": "https://assets.tarkov.dev/maps/factory_v2/3rd_notint/{z}/{x}/{y}.png", + "svgPath": "https://assets.tarkov.dev/maps/svg/Factory-Second_Floor.svg", "show": false, "heightRange": [6, 10000] }, { "name": "Tunnels", "tilePath": "https://assets.tarkov.dev/maps/factory_v2/tunnels_notint/{z}/{x}/{y}.png", + "svgPath": "https://assets.tarkov.dev/maps/svg/Factory-Basement.svg", "show": false, "heightRange": [-10000, -1] } From f3a78d5b1e4aaf6328522ba35f4759a2f883c8a3 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Thu, 26 Oct 2023 16:13:37 -0500 Subject: [PATCH 13/22] streamline maps menu --- src/components/menu/index.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/components/menu/index.js b/src/components/menu/index.js index aef8cd158..3f85fd496 100644 --- a/src/components/menu/index.js +++ b/src/components/menu/index.js @@ -158,9 +158,19 @@ const Menu = () => {
  • {t('Maps')}
      - {uniqueMaps.map((map) => ( + {Object.values(uniqueMaps.reduce((unique, map) => { + const sameMap = Object.values(unique).find(m => m.id === map.id); + if (!sameMap) { + unique[map.id] = map; + return unique; + } + if (map.projection === 'interactive') { + unique[map.id] = map; + } + return unique; + }, {})).map((map) => ( { //onClick={setIsOpen.bind(this, false)} /> ))} +
  • From 285aa1ef185b298fa77025fb1d00b14fdd671683 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Fri, 27 Oct 2023 09:52:30 -0500 Subject: [PATCH 14/22] remember map settings --- .../container_bank-cash-register.png | Bin 1018 -> 0 bytes .../maps/interactive/container_bank-safe.png | Bin 1327 -> 0 bytes .../container_cash-register-tar2-2.png | Bin 1018 -> 0 bytes .../interactive/container_ground-cache.png | Bin 4107 -> 0 bytes .../container_medical-supply-crate.png | Bin 4985 -> 0 bytes .../container_ration-supply-crate.png | Bin 4985 -> 0 bytes .../interactive/container_shturmans-stash.png | Bin 631 -> 0 bytes .../interactive/{locked_door.png => lock.png} | Bin .../{compass.png => quest_objective.png} | Bin .../interactive/{lever.png => switch.png} | Bin src/components/Map.jsx | 152 ++++++++++++++---- src/modules/leaflet-control-groupedlayer.js | 58 ++++++- 12 files changed, 174 insertions(+), 36 deletions(-) delete mode 100644 public/maps/interactive/container_bank-cash-register.png delete mode 100644 public/maps/interactive/container_bank-safe.png delete mode 100644 public/maps/interactive/container_cash-register-tar2-2.png delete mode 100644 public/maps/interactive/container_ground-cache.png delete mode 100644 public/maps/interactive/container_medical-supply-crate.png delete mode 100644 public/maps/interactive/container_ration-supply-crate.png delete mode 100644 public/maps/interactive/container_shturmans-stash.png rename public/maps/interactive/{locked_door.png => lock.png} (100%) rename public/maps/interactive/{compass.png => quest_objective.png} (100%) rename public/maps/interactive/{lever.png => switch.png} (100%) diff --git a/public/maps/interactive/container_bank-cash-register.png b/public/maps/interactive/container_bank-cash-register.png deleted file mode 100644 index d57edefdbee9b98b38980c31bef4faaac3fc8f3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1018 zcmV?)e<8 zBaigw?#Aose7ECSudjctmd(x0?Ixml_FC+%a6aPvC7R7AI7vP*7H5K~NF=y@OtVOC zW@d&aCMLWV&W9l$;)((|h@?J#{74B22_#99*Gf!Gq)(qdamYbY0PiEAr%#`f-EP+{ z#DO8Muz<;h36$HAsaY6rD!|$k!rE0`#F-fEHjMN~auU@^9ldn5jf$I&gvJb~s zGQ^#aKgx81h_-6|#y7AN#>p_#0jOdCC8Bj&X0MQ+G(0>^XV0EBOufCmipCR9*|q6- zPZI~e@p#S1xr!|5QW8Hy z+N`RT0QGjvih*L|OEfw)(qkggDUqsxnN_JYaIZs?%KM9yfX0`oQ*w9&L@OV--=PAS zrn#&Er35s*L=il#G+9+>CTcSn5g$gG)oB2QRNu#C#;j6_>X-g&aIjeHE*g^-GV z-S>D6PB1-Usv@F)m9ut^lxARw>Xd31RT`K$+#cnV<_i}tK-AaQr~Jto*Md+|{;Z8$ zdEQ{)1~!yu786ZPO;J!#5Kb_)LPJAoc6OFCW?=@4@ecnFMUo`b1G-!;84zsC0ivU! z&P^rcYytWju=aSlr)M`9V@^QaIP+bAnk1J*eRtZ;2dMS=DYj3hz6P<)QIXYTi~z)A zcbs4*A!il9HNMaX%zvP!rX~z9wOU$Qh#S~&FS?GsdCi@mH?z2b32b~vaggZ^tq}tp o(}Bf1`SnbJOvNTw0G~P7F9Zz5ksNj%o&W#<07*qoM6N<$g3P|yoB#j- diff --git a/public/maps/interactive/container_bank-safe.png b/public/maps/interactive/container_bank-safe.png deleted file mode 100644 index 17de5b98b3cfd78fe65924b174f3932db2081fe9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1327 zcmV+~1r6 zQ~kN=?^o&|=Bxbz02LvdFpaQOB#b5`5@hd_8bkP%PzU0>UK741)Ci?!ynOi*_U_#a zZnqoMYPG1y%*=#4ckZCz3j+VYl!fqc#flYxzls&9R0=n5-bA@i2~rdr@XtU0fL^bs zZ!ib5AhX{L!pxA9(RVsH@_Df5)r5}RYET+XwC%;k#ZXsQhr`l}V!MC;{dZUrfZ>(x53^xjkG-y~#Ws`RFiAXjVKOb3aCw#!-ArEOx` zjt=Ox>3-DAF_}{JhFH5;)_^&nFbv)fPH0JqvP`hCSC{^Fcz@V6bGAfQmD-zvJ9uJryaj z?rW~myjV7MdyB=&r|n`X+?~rquZ{QE9K&-|$E3z|<1X(LSurdT;NNgq^=@HKOsF10 z?^SAz&|}@r2HBn913BoCSe3ax>@hYO^MC*8ZUtMOjCXyET%S6spsmM#_veJ0Wd zasJf$=Ud&P5n~xM&X>x{3?@$!s5lEG4VXq7q<6*Fj|hiPJYpGrrqg?+as|}PKgQ%z zhRnsr;DSN-Nm?08EfkMf=Vj+Wrcl!RH6|NbfOfFO12dt@Xpa1#B!Ee2ux6Eh!gB%60ACj;-HCAEK$%Rj*ffB^Hi7%Swg|uV8o6s2D=Yj zSye4Lk6~zLJ%ur>X2CJ&L6S_t=v9!&4+BeD5e>b#fF$&p1WF>#$>t1?A63_Y#{%!8o>Ym}^0h7KKyQjZ846jKtus;#XBqmdr`L<#oRx8Ht?V&|HE zNGyx+%TGW31Y5Rjf%)_2i;5#hj=;l*4^iqff=T!ZI16D1;d27M>$ynylTb@oOpt%< l1DPDbE)p~yV&H1O{sVq>@q!?$U{C-6002ovPDHLkV1jA_frtPA diff --git a/public/maps/interactive/container_cash-register-tar2-2.png b/public/maps/interactive/container_cash-register-tar2-2.png deleted file mode 100644 index d57edefdbee9b98b38980c31bef4faaac3fc8f3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1018 zcmV?)e<8 zBaigw?#Aose7ECSudjctmd(x0?Ixml_FC+%a6aPvC7R7AI7vP*7H5K~NF=y@OtVOC zW@d&aCMLWV&W9l$;)((|h@?J#{74B22_#99*Gf!Gq)(qdamYbY0PiEAr%#`f-EP+{ z#DO8Muz<;h36$HAsaY6rD!|$k!rE0`#F-fEHjMN~auU@^9ldn5jf$I&gvJb~s zGQ^#aKgx81h_-6|#y7AN#>p_#0jOdCC8Bj&X0MQ+G(0>^XV0EBOufCmipCR9*|q6- zPZI~e@p#S1xr!|5QW8Hy z+N`RT0QGjvih*L|OEfw)(qkggDUqsxnN_JYaIZs?%KM9yfX0`oQ*w9&L@OV--=PAS zrn#&Er35s*L=il#G+9+>CTcSn5g$gG)oB2QRNu#C#;j6_>X-g&aIjeHE*g^-GV z-S>D6PB1-Usv@F)m9ut^lxARw>Xd31RT`K$+#cnV<_i}tK-AaQr~Jto*Md+|{;Z8$ zdEQ{)1~!yu786ZPO;J!#5Kb_)LPJAoc6OFCW?=@4@ecnFMUo`b1G-!;84zsC0ivU! z&P^rcYytWju=aSlr)M`9V@^QaIP+bAnk1J*eRtZ;2dMS=DYj3hz6P<)QIXYTi~z)A zcbs4*A!il9HNMaX%zvP!rX~z9wOU$Qh#S~&FS?GsdCi@mH?z2b32b~vaggZ^tq}tp o(}Bf1`SnbJOvNTw0G~P7F9Zz5ksNj%o&W#<07*qoM6N<$g3P|yoB#j- diff --git a/public/maps/interactive/container_ground-cache.png b/public/maps/interactive/container_ground-cache.png deleted file mode 100644 index cf54061e79c30aa5d3f07444f9e919dbf9cc0919..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4107 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 DFG+43 diff --git a/public/maps/interactive/container_ration-supply-crate.png b/public/maps/interactive/container_ration-supply-crate.png deleted file mode 100644 index 595796cac56316d433fb98faae0ed066411c9f85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4985 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 DFG+43 diff --git a/public/maps/interactive/container_shturmans-stash.png b/public/maps/interactive/container_shturmans-stash.png deleted file mode 100644 index 62e553e9b400c9ebd623ce1dbb6b8035495d22f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 631 zcmV--0*L*IP)Sr`9P>8q{`U)mB^t<=t{RbH>`3FL|Pk-t~_KJR^(; z08Xd`oj|YA4+DFQx={{Vr%8Y-x(;egBoe{s=%`F1BO`L*<4R5JA3BQGsv6OZ{`~s& z3oc!{2)#Ys(B9UnR9R^$q^GCLRWn)Q{l9 zhY!%v-UbH`>{rRwEt?@JDM>C@S67+b+}t2BF#%Fil2y{w)C8YCeFU5Y?Ufbbi=Im) z61aKu1{^+gQ00gNC@(KhqYx4l6r^$p$_o76^Jb{6tyNoLVIgE?Whvz8>8X&vzdwY81gmZP_H8h+ z017#eprD`t^7Hfc!pFx)FX)M!*j@_xW&t4s6e8si4&e|h3dzXGp#MdB;o;$-7gbeN z^FtgQ9Khb*UN6?KU#}OLnVB47`XQmAp%4-hq8GNdwt7)iRK#Du9Ksxn}_( z9Td`Ot|4`Gb(0|(6q16Vudh$5khr)wiWw6X6*VO?GLp`j5uJw6<+|bFVZe=`H59T4 z-9#W3i**KXAke#swJn$&#mu-><0%>i)7@`$p8`;_5}cQd_Zb RnRx&J002ovPDHLkV1mOd6c7La diff --git a/public/maps/interactive/locked_door.png b/public/maps/interactive/lock.png similarity index 100% rename from public/maps/interactive/locked_door.png rename to public/maps/interactive/lock.png diff --git a/public/maps/interactive/compass.png b/public/maps/interactive/quest_objective.png similarity index 100% rename from public/maps/interactive/compass.png rename to public/maps/interactive/quest_objective.png diff --git a/public/maps/interactive/lever.png b/public/maps/interactive/switch.png similarity index 100% rename from public/maps/interactive/lever.png rename to public/maps/interactive/switch.png diff --git a/src/components/Map.jsx b/src/components/Map.jsx index a9f5d6d44..30ab5746b 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -32,6 +32,8 @@ import SEO from './SEO'; import ErrorPage from './error-page'; +import useStateWithLocalStorage from '../hooks/useStateWithLocalStorage'; + import './Maps.css'; const showOtherMarkers = true; @@ -263,6 +265,18 @@ function Map() { const { t } = useTranslation(); const dispatch = useDispatch(); + const [savedMapSettings, setSavedMapSettings] = useStateWithLocalStorage( + 'savedMapSettings', + { + style: 'svg', + hiddenGroups: [], + hiddenLayers: [], + collapsedGroups: [], + }, + ); + + const mapSettingsRef = useRef(savedMapSettings); + useEffect(() => { let viewableHeight = window.innerHeight - document.querySelector('.navigation')?.offsetHeight || 0; if (viewableHeight < 100) { @@ -337,6 +351,27 @@ function Map() { groupsCollapsable: true, exclusiveOptionalGroups: [tMaps('Levels')], }).addTo(map); + layerControl.on('groupToggle', (e) => { + const groupState = e.detail; + if (!groupState.checked) { + mapSettingsRef.current.hiddenGroups.push(groupState.key); + + } else { + mapSettingsRef.current.hiddenGroups = mapSettingsRef.current.hiddenGroups.filter(key => key !== groupState.key); + } + setSavedMapSettings(mapSettingsRef.current); + }); + layerControl.on('groupCollapseToggle', (e) => { + const groupState = e.detail; + if (groupState.collapsed) { + mapSettingsRef.current.collapsedGroups.push(groupState.key); + + } else { + mapSettingsRef.current.collapsedGroups = mapSettingsRef.current.collapsedGroups.filter(key => key !== groupState.key); + } + setSavedMapSettings(mapSettingsRef.current); + }); + map.layerControl = layerControl; map.addControl(new L.Control.Fullscreen({ title: { @@ -399,6 +434,11 @@ function Map() { let selectedLayer = ''; baseLayer.on('add', () => { const svgParent = baseLayer._url.endsWith('.svg'); + if (tileLayer && svgLayer) { + const selectedStyle = svgParent ? 'svg' : 'tile'; + mapSettingsRef.current.style = selectedStyle; + setSavedMapSettings(mapSettingsRef.current); + } const existingLayers = Object.values(layerControl._layers).filter(l => l.layer.options.type === 'map-layer' && !baseLayers.includes(l.layer)).map(l => l.layer); for (const existingLayer of existingLayers) { const svgOverlay = Boolean(existingLayer._url.endsWith('.svg')); @@ -476,12 +516,15 @@ function Map() { heightLayer.addTo(map); } - layerControl.addOverlay(heightLayer, tMaps(layer.name), tMaps('Levels')); + layerControl.addOverlay(heightLayer, tMaps(layer.name), {groupName: tMaps('Levels')}); } }); } - const baseLayer = tileLayer ? tileLayer : svgLayer; + let baseLayer = tileLayer ? tileLayer : svgLayer; + if (baseLayer === tileLayer && svgLayer && mapSettingsRef.current.style === 'svg') { + baseLayer = svgLayer; + } baseLayer.addTo(map); @@ -516,6 +559,49 @@ function Map() { 'stationarygun': t('Stationary Gun'), 'switch': t('Switch'), }; + const images = { + 'container_bank-cash-register': 'container_cash-register', + 'container_bank-safe': 'container_safe', + 'container_buried-barrel-cache': 'container_buried-barrel-cache', + 'container_cash-register': 'container_cash-register', + 'container_cash-register-tar2-2': 'container_cash-register', + 'container_dead-scav': 'container_dead-scav', + 'container_drawer': 'container_drawer', + 'container_duffle-bag': 'container_duffle-bag', + 'container_grenade-box': 'container_grenade-box', + 'container_ground-cache': 'container_buried-barrel-cache', + 'container_jacket': 'container_jacket', + 'container_medbag-smu06': 'container_medbag-smu06', + 'container_medcase': 'container_medcase', + 'container_medical-supply-crate': 'container_technical-supply-crate', + 'container_pc-block': 'container_pc-block', + 'container_plastic-suitcase': 'container_plastic-suitcase', + 'container_ration-supply-crate': 'container_technical-supply-crate', + 'container_safe': 'container_safe', + 'container_shturmans-stash': 'container_weapon-box', + 'container_technical-supply-crate': 'container_technical-supply-crate', + 'container_toolbox': 'container_toolbox', + 'container_weapon-box': 'container_weapon-box', + 'container_wooden-ammo-box': 'container_wooden-ammo-box', + 'container_wooden-crate': 'container_wooden-crate', + 'extract_pmc': 'extract_pmc', + 'extract_scav': 'extract_scav', + 'extract_shared': 'extract_shared', + 'hazard': 'hazard', + 'key': 'key', + 'lock': 'lock', + 'quest_item': 'quest_item', + 'quest_objective': 'quest_objective', + 'sniper_scav': 'sniper_scav', + 'spawn_bloodhound': 'spawn_bloodhound', + 'spawn_boss': 'spawn_boss', + 'spawn_cultist-priest': 'spawn_cultist-priest', + 'spawn_pmc': 'spawn_pmc', + 'spawn_rogue': 'spawn_rogue', + 'spawn_scav': 'spawn_scav', + 'stationarygun': 'stationarygun', + 'switch': 'switch', + }; const focusOnPoi = (id) => { for (const marker of Object.values(map._layers)) { @@ -553,6 +639,27 @@ function Map() { return a; }; + const getLayerOptions = (layerKey, groupKey, layerName) => { + return { + groupKey, + layerKey, + groupName: tMaps(groupKey), + layerName: layerName || categories[layerKey] || layerKey, + groupHidden: Boolean(mapSettingsRef.current.hiddenGroups?.includes(groupKey)), + layerHidden: Boolean(mapSettingsRef.current.hiddenLayers?.includes(layerKey)), + image: images[layerKey] ? `${process.env.PUBLIC_URL}/maps/interactive/${images[layerKey]}.png` : undefined, + groupCollapsed: Boolean(mapSettingsRef.current.collapsedGroups?.includes(groupKey)), + }; + }; + + const addLayer = (layer, layerKey, groupKey, layerName) => { + const layerOptions = getLayerOptions(layerKey, groupKey, layerName); + if (!layerOptions.groupHidden && !layerOptions.layerHidden) { + layer.addTo(map); + } + layerControl.addOverlay(layer, layerOptions.layerName, layerOptions); + }; + let markerBounds = { 'TL': {x:Number.MAX_SAFE_INTEGER, z:Number.MIN_SAFE_INTEGER}, 'BR': {x:Number.MIN_SAFE_INTEGER, z:Number.MAX_SAFE_INTEGER} @@ -711,8 +818,7 @@ function Map() { } for (const key in spawnLayers) { if (Object.keys(spawnLayers[key]._layers).length > 0) { - spawnLayers[key].addTo(map); - layerControl.addOverlay(spawnLayers[key], ` ${categories[`spawn_${key}`]}`, tMaps('Spawns')); + addLayer(spawnLayers[key], `spawn_${key}`, 'Spawns'); } } } @@ -775,8 +881,7 @@ function Map() { } for (const key in extractLayers) { if (Object.keys(extractLayers[key]._layers).length > 0) { - extractLayers[key].addTo(map); - layerControl.addOverlay(extractLayers[key], ` ${categories[`extract_${key}`]}`, t('Extracts')); + addLayer(extractLayers[key], `extract_${key}`, 'Extracts'); } } } @@ -791,7 +896,7 @@ function Map() { checkMarkerBounds(lock.position, markerBounds); const lockIcon = L.icon({ - iconUrl: `${process.env.PUBLIC_URL}/maps/interactive/locked_door.png`, + iconUrl: `${process.env.PUBLIC_URL}/maps/interactive/lock.png`, iconSize: [24, 24], popupAnchor: [0, -12], }); @@ -840,8 +945,7 @@ function Map() { } if (Object.keys(locks._layers).length > 0) { - locks.addTo(map); - layerControl.addOverlay(locks, ` ${categories['lock']}`, t('Locks')); + addLayer(locks, 'lock', 'Interactive'); } } @@ -894,7 +998,7 @@ function Map() { } const rect = L.polygon(outlineToPoly(zone.outline), {color: '#e5e200', weight: 1, className: 'not-shown'}); const zoneIcon = L.icon({ - iconUrl: `${process.env.PUBLIC_URL}/maps/interactive/compass.png`, + iconUrl: `${process.env.PUBLIC_URL}/maps/interactive/quest_objective.png`, iconSize: [24, 24], popupAnchor: [0, -12], }); @@ -927,24 +1031,18 @@ function Map() { } } if (Object.keys(questItems._layers).length > 0) { - questItems.addTo(map); - layerControl.addOverlay(questItems, ` ${categories['quest_item']}`, t('Tasks')); + addLayer(questItems, 'quest_item', 'Tasks'); } if (Object.keys(questObjectives._layers).length > 0) { - questObjectives.addTo(map); - layerControl.addOverlay(questObjectives, ` ${categories['quest_objective']}`, t('Tasks')); + addLayer(questObjectives, 'quest_objective', 'Tasks'); } - /*if (Object.keys(questZones._layers).length > 0) { - questZones.addTo(map); - layerControl.addOverlay(questZones, categories['quest_zone'], t('Tasks')); - }*/ //add switches if (mapData.switches.length > 0) { const switches = L.layerGroup(); for (const sw of mapData.switches) { const switchIcon = L.icon({ - iconUrl: `${process.env.PUBLIC_URL}/maps/interactive/lever.png`, + iconUrl: `${process.env.PUBLIC_URL}/maps/interactive/switch.png`, iconSize: [24, 24], popupAnchor: [0, -12], }); @@ -1008,8 +1106,7 @@ function Map() { checkMarkerBounds(sw.position, markerBounds); } if (Object.keys(switches._layers).length > 0) { - switches.addTo(map); - layerControl.addOverlay(switches, ` ${categories['switch']}`, t('Switches')); + addLayer(switches, 'switch', 'Interactive'); } } @@ -1019,7 +1116,7 @@ function Map() { const containerNames = {}; for (const containerPosition of mapData.lootContainers) { const containerIcon = L.icon({ - iconUrl: `${process.env.PUBLIC_URL}/maps/interactive/container_${containerPosition.lootContainer.normalizedName}.png`, + iconUrl: `${process.env.PUBLIC_URL}/maps/interactive/${images[`container_${containerPosition.lootContainer.normalizedName}`]}.png`, iconSize: [24, 24], popupAnchor: [0, -12], }); @@ -1039,8 +1136,7 @@ function Map() { } for (const key in containerLayers) { if (Object.keys(containerLayers[key]._layers).length > 0) { - containerLayers[key].addTo(map); - layerControl.addOverlay(containerLayers[key], ` ${containerNames[key]}`, t('Items')); + addLayer(containerLayers[key], `container_${key}`, 'Lootable Items', containerNames[key]); } } } @@ -1082,8 +1178,7 @@ function Map() { } for (const key in hazardLayers) { if (Object.keys(hazardLayers[key]._layers).length > 0) { - hazardLayers[key].addTo(map); - layerControl.addOverlay(hazardLayers[key], key, t('Hazards')); + addLayer(hazardLayers[key], key, 'Hazards'); } } } @@ -1107,8 +1202,7 @@ function Map() { weaponMarker.on('click', activateMarkerLayer); weaponMarker.addTo(stationaryWeapons); } - stationaryWeapons.addTo(map); - layerControl.addOverlay(stationaryWeapons, ` ${categories.stationarygun}`, t('Stationary Guns')); + addLayer(stationaryWeapons, 'stationarygun', 'Interactive'); } if (showTestMarkers) { @@ -1172,7 +1266,7 @@ function Map() { } } } - }, [mapData, items, quests, mapRef, playerPosition, t, dispatch, navigate]); + }, [mapData, items, quests, mapRef, playerPosition, t, dispatch, navigate, mapSettingsRef, setSavedMapSettings]); if (!mapData) { return ; diff --git a/src/modules/leaflet-control-groupedlayer.js b/src/modules/leaflet-control-groupedlayer.js index 68330e0a5..e2d655d92 100644 --- a/src/modules/leaflet-control-groupedlayer.js +++ b/src/modules/leaflet-control-groupedlayer.js @@ -25,6 +25,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import L from 'leaflet'; +let controlContainer; + // A layer control which provides for layer groupings. // Author: Ishmael Smyrnow L.Control.GroupedLayers = L.Control.extend({ @@ -96,8 +98,8 @@ L.Control.GroupedLayers = L.Control.extend({ return this; }, - addOverlay: function (layer, name, group) { - this._addLayer(layer, name, group, true); + addOverlay: function (layer, name, options) { + this._addLayer(layer, name, {...options, overlay: true}); this._update(); return this; }, @@ -164,17 +166,25 @@ L.Control.GroupedLayers = L.Control.extend({ this._overlaysList = L.DomUtil.create('div', className + '-overlays', form); container.appendChild(form); + + controlContainer = container; }, - _addLayer: function (layer, name, group, overlay) { + _addLayer: function (layer, name, options) { + options = {...options}; + if (options.image) { + name = ` ${name}`; + } + var _layer = { layer: layer, name: name, - overlay: overlay + overlay: options.overlay, + key: options.layerKey, }; this._layers.push(_layer); - group = group || ''; + const group = options.groupName || ''; var groupId = this._indexOf(this._groupList, group); if (groupId === -1) { @@ -189,6 +199,8 @@ L.Control.GroupedLayers = L.Control.extend({ id: groupId, exclusive: exclusive, exclusiveOptional: exclusiveOptional, + key: options.groupKey, + collapsed: options.groupCollapsed, }; if (this.options.autoZIndex && layer.setZIndex) { @@ -352,6 +364,7 @@ L.Control.GroupedLayers = L.Control.extend({ groupContainer = document.createElement('div'); groupContainer.className = 'leaflet-control-layers-group'; groupContainer.id = 'leaflet-control-layers-group-' + obj.group.id; + groupContainer.dataset.key = obj.group.key; var groupLabel = document.createElement('label'); groupLabel.className = 'leaflet-control-layers-group-label'; @@ -364,6 +377,7 @@ L.Control.GroupedLayers = L.Control.extend({ groupInput.className = 'leaflet-control-layers-group-selector'; groupInput.groupID = obj.group.id; groupInput.legend = this; + groupInput.dataset.key = obj.group.key; L.DomEvent.on(groupInput, 'click', this._onGroupInputClick, groupInput); groupLabel.appendChild(groupInput); } @@ -371,7 +385,9 @@ L.Control.GroupedLayers = L.Control.extend({ if (this.options.groupsCollapsable){ groupContainer.classList.add("group-collapsable"); - //groupContainer.classList.add("collapsed"); + if (obj.group.collapsed) { + groupContainer.classList.add("collapsed"); + } var groupMin = document.createElement('span'); groupMin.className = 'leaflet-control-layers-group-collapse '+this.options.groupsExpandedClass; @@ -413,6 +429,15 @@ L.Control.GroupedLayers = L.Control.extend({ } else if (this.classList.contains("group-collapsable") && !this.classList.contains("collapsed")) { this.classList.add("collapsed"); } + if (this.dataset.key) { + controlContainer.dispatchEvent(new CustomEvent('groupCollapseToggle', { + bubbles: false, + detail: { + key: this.dataset.key, + collapsed: this.classList.contains("collapsed"), + }, + })); + } }, _onGroupInputClick: function (event) { @@ -435,6 +460,16 @@ L.Control.GroupedLayers = L.Control.extend({ } } } + + if (this.dataset.key) { + controlContainer.dispatchEvent(new CustomEvent('groupToggle', { + bubbles: false, + detail: { + key: this.dataset.key, + checked: this.checked, + }, + })); + } this_legend._handlingClick = false; }, @@ -524,7 +559,16 @@ L.Control.GroupedLayers = L.Control.extend({ } } return -1; - } + }, + on: (eventName, handler, options) => { + controlContainer.addEventListener(eventName, handler, options); + }, + once: (eventName, handler, options) => { + controlContainer.addEventListener(eventName, handler, {...options, once: true}); + }, + off: (eventName, handler, options) => { + controlContainer.removeEventListener(eventName, handler, options); + }, }); L.control.groupedLayers = function (baseLayers, groupedOverlays, options) { From e4cd0f8153d3969df353601597b1f3184e6050b3 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Fri, 27 Oct 2023 10:52:09 -0500 Subject: [PATCH 15/22] save individual layer state --- src/components/Map.jsx | 11 +++++++++++ src/modules/leaflet-control-groupedlayer.js | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index 30ab5746b..9b05dae11 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -351,6 +351,16 @@ function Map() { groupsCollapsable: true, exclusiveOptionalGroups: [tMaps('Levels')], }).addTo(map); + layerControl.on('layerToggle', (e) => { + const layerState = e.detail; + if (!layerState.checked) { + mapSettingsRef.current.hiddenLayers.push(layerState.key); + + } else { + mapSettingsRef.current.hiddenLayers = mapSettingsRef.current.hiddenLayers.filter(key => key !== layerState.key); + } + setSavedMapSettings(mapSettingsRef.current); + }); layerControl.on('groupToggle', (e) => { const groupState = e.detail; if (!groupState.checked) { @@ -653,6 +663,7 @@ function Map() { }; const addLayer = (layer, layerKey, groupKey, layerName) => { + layer.key = layerKey; const layerOptions = getLayerOptions(layerKey, groupKey, layerName); if (!layerOptions.groupHidden && !layerOptions.layerHidden) { layer.addTo(map); diff --git a/src/modules/leaflet-control-groupedlayer.js b/src/modules/leaflet-control-groupedlayer.js index e2d655d92..674f678db 100644 --- a/src/modules/leaflet-control-groupedlayer.js +++ b/src/modules/leaflet-control-groupedlayer.js @@ -347,6 +347,17 @@ L.Control.GroupedLayers = L.Control.extend({ input.layerId = L.Util.stamp(obj.layer); input.groupID = obj.group.id; L.DomEvent.on(input, 'click', this._onInputClick, this); + L.DomEvent.on(input, 'click', () => { + if (obj.layer.key) { + controlContainer.dispatchEvent(new CustomEvent('layerToggle', { + bubbles: false, + detail: { + key: obj.layer.key, + checked: input.checked, + }, + })); + } + }, this); var name = document.createElement('span'); name.innerHTML = ' ' + obj.name; From 3bb50623897083cf9749d13312d6bdbd4d8a0e81 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Tue, 7 Nov 2023 07:29:13 -0600 Subject: [PATCH 16/22] improved labs map --- src/components/Map.jsx | 6 +++--- src/data/maps.json | 15 +++++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index 9b05dae11..7bbd352f9 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -877,7 +877,7 @@ function Map() { textElement.textContent = t('Activated by:'); popup.appendChild(textElement); for (const sw of extract.switches) { - const linkElement = getPoiLinkElement(sw.id, 'lever'); + const linkElement = getPoiLinkElement(sw.id, 'switch'); const nameElement = L.DomUtil.create('span'); nameElement.innerHTML = `${sw.name}`; linkElement.append(nameElement); @@ -1078,7 +1078,7 @@ function Map() { const textElement = L.DomUtil.create('div'); textElement.textContent = `${t('Activated by')}:`; popup.appendChild(textElement); - const linkElement = getPoiLinkElement(sw.activatedBy.id, 'lever'); + const linkElement = getPoiLinkElement(sw.activatedBy.id, 'switch'); const nameElement = L.DomUtil.create('span'); nameElement.innerHTML = `${sw.activatedBy.name}`; linkElement.append(nameElement); @@ -1091,7 +1091,7 @@ function Map() { } for (const switchOperation of sw.activates) { if (switchOperation.target.__typename === 'MapSwitch') { - const linkElement = getPoiLinkElement(switchOperation.target.id, 'lever'); + const linkElement = getPoiLinkElement(switchOperation.target.id, 'switch'); const nameElement = L.DomUtil.create('span'); nameElement.innerHTML = `${switchOperation.target.name}`; linkElement.append(nameElement); diff --git a/src/data/maps.json b/src/data/maps.json index 48e9d1ca6..677b3a63a 100644 --- a/src/data/maps.json +++ b/src/data/maps.json @@ -223,24 +223,27 @@ "projection": "interactive", "tileSize": 175, "minZoom": 2, - "maxZoom": 5, - "transform": [0.467, 244.2, 0.467, 137.6], + "maxZoom": 6, + "transform": [0.575, 281.2, 0.575, 196.7], "coordinateRotation": 270, "bounds": [[-91, -477], [-287, -193]], "author": "Tarkov.dev", "authorLink": "https://tarkov.dev", - "tilePath": "https://assets.tarkov.dev/maps/labs_v2/1st/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/labs_v3/1st/{z}/{x}/{y}.png", "heightRange": [-0.9, 3], "layers": [ { "name": "Second Level", - "tilePath": "https://assets.tarkov.dev/maps/labs_v2/2nd/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/labs_v3/2nd/{z}/{x}/{y}.png", "show": false, - "heightRange": [3, 10000] + "heightRange": [3, 10000], + "layerBounds": [ + [[-101, -422], [-271, -270]] + ] }, { "name": "Technical", - "tilePath": "https://assets.tarkov.dev/maps/labs_v2/technical/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/labs_v3/technical/{z}/{x}/{y}.png", "show": false, "heightRange": [-10000, -0.9] } From 776ecf457ac907f98c7580e311631fe4f667c3e7 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Tue, 7 Nov 2023 16:09:23 -0600 Subject: [PATCH 17/22] make reserve cardinal --- src/components/Map.jsx | 84 ++++++++++----------- src/data/maps.json | 167 ++++++++++++++++++++++++++++++++--------- 2 files changed, 168 insertions(+), 83 deletions(-) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index 7bbd352f9..cf465aadc 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -127,22 +127,25 @@ function getBounds(bounds) { } function markerIsOnLayer(marker, layer) { + if (!layer.options.extents) { + return true; + } var top = marker.options.top || marker.options.position.y; var bottom = marker.options.bottom || marker.options.position.y; - const heightRange = layer.options.heightRange; - if (top >= heightRange[0] && bottom < heightRange[1]) { - if (layer.options.layerBounds) { - let inBounds = false; - for (const boundsArray of layer.options.layerBounds) { - const bounds = getBounds(boundsArray); - if (bounds.contains(pos(marker.options.position))) { - inBounds = true; - break; + for (const extent of layer.options.extents) { + if (top >= extent.height[0] && bottom < extent.height[1]) { + if (extent.bounds) { + for (const boundsArray of extent.bounds) { + const bounds = getBounds(boundsArray); + if (bounds.contains(pos(marker.options.position))) { + if (layer.overlay) console.log('marker on layer', marker.options, layer.options) + return true; + } } + } else { + return true; } - return inBounds; } - return true; } return false; } @@ -154,20 +157,22 @@ function markerIsOnActiveLayer(marker) { const map = marker._map; - const overlays = map.layerControl._layers.map(l => l.layer).filter(l => Boolean(l.options.heightRange)); + const overlays = map.layerControl._layers.map(l => l.layer).filter(l => Boolean(l.options.extents) && l.options.overlay); let onAbsentLayer = false; - for (const layer of overlays) { - const onOverlay = markerIsOnLayer(marker, layer); - if (onOverlay && !map.hasLayer(layer) && layer.options.layerBounds) { - onAbsentLayer = true; - break; + layerLoop: for (const layer of overlays) { + for (const extent of layer.options.extents) { + const onOverlay = markerIsOnLayer(marker, layer); + if (onOverlay && !map.hasLayer(layer) && extent.bounds) { + onAbsentLayer = true; + break layerLoop; + } } } if (onAbsentLayer) { return false; } - let activeLayers = Object.values(map._layers).filter(l => l.options?.heightRange); + let activeLayers = Object.values(map._layers).filter(l => l.options?.extents); if (activeLayers.some(l => l.options.overlay)) { activeLayers = activeLayers.filter(l => l.options.overlay); } @@ -183,23 +188,6 @@ function markerIsOnActiveLayer(marker) { function checkMarkerForActiveLayers(event) { const marker = event.target || event; const outline = marker.options.outline; - /*if (!marker.options.position) { - return; - } - var top = marker.options.top || marker.options.position.y; - var bottom = marker.options.bottom || marker.options.position.y; - let activeLayers = Object.values(marker._map._layers).filter(l => l.options?.heightRange); - if (activeLayers.some(l => l.options.overlay)) { - activeLayers = activeLayers.filter(l => l.options.overlay); - } - let onLevel = false; - for (const layer of activeLayers) { - const heightRange = layer.options.heightRange; - if (top >= heightRange[0] && bottom < heightRange[1]) { - onLevel = true; - break; - } - }*/ const onLevel = markerIsOnActiveLayer(marker); if (onLevel) { marker._icon?.classList.remove('off-level'); @@ -237,11 +225,11 @@ function activateMarkerLayer(event) { if (markerIsOnActiveLayer(marker)) { return; } - const activeLayers = Object.values(marker._map._layers).filter(l => l.options?.heightRange && l.options?.overlay); + const activeLayers = Object.values(marker._map._layers).filter(l => l.options?.extents && l.options?.overlay); for (const layer of activeLayers) { layer.removeFrom(marker._map); } - const heightLayers = marker._map.layerControl._layers.filter(l => l.layer.options.heightRange && l.layer.options.overlay).map(l => l.layer); + const heightLayers = marker._map.layerControl._layers.filter(l => l.layer.options.extents && l.layer.options.overlay).map(l => l.layer); for (const layer of heightLayers) { if (markerIsOnLayer(marker, layer)) { layer.addTo(marker._map); @@ -406,7 +394,12 @@ function Map() { const bounds = getBounds(mapData.bounds); const layerOptions = { - heightRange: mapData.heightRange || [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER], + extents: [ + { + height: mapData.heightRange || [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER], + bounds: [mapData.bounds], + } + ], type: 'map-layer', }; let tileLayer = false; @@ -469,10 +462,9 @@ function Map() { const layerOptions = { name: layer.name, - heightRange: layer.heightRange || baseLayer.options?.heightRange, - layerBounds: layer.layerBounds, + extents: layer.extents || baseLayer.options?.extents, type: 'map-layer', - overlay: Boolean(layer.heightRange), + overlay: Boolean(layer.extents), }; if (baseLayer._url.endsWith('.svg') && layer.svgPath) { @@ -490,7 +482,7 @@ function Map() { } heightLayer.on('add', () => { - if (layer.heightRange) { + if (layer.extents) { for (const marker of Object.values(map._layers)) { checkMarkerForActiveLayers(marker); } @@ -503,7 +495,7 @@ function Map() { } }); heightLayer.on('remove', () => { - const heightLayer = Object.values(map._layers).findLast(l => l.options?.heightRange); + const heightLayer = Object.values(map._layers).findLast(l => l.options?.extents); if (heightLayer) { for (const marker of Object.values(map._layers)) { checkMarkerForActiveLayers(marker); @@ -531,9 +523,9 @@ function Map() { }); } - let baseLayer = tileLayer ? tileLayer : svgLayer; - if (baseLayer === tileLayer && svgLayer && mapSettingsRef.current.style === 'svg') { - baseLayer = svgLayer; + let baseLayer = svgLayer ? svgLayer : tileLayer; + if (baseLayer === svgLayer && tileLayer && mapSettingsRef.current.style === 'tile') { + baseLayer = tileLayer; } baseLayer.addTo(map); diff --git a/src/data/maps.json b/src/data/maps.json index 677b3a63a..d9c0e6d08 100644 --- a/src/data/maps.json +++ b/src/data/maps.json @@ -20,31 +20,51 @@ "name": "Underground", "svgPath": "https://assets.tarkov.dev/maps/svg/StreetsOfTarkov-Underground_Level.svg", "show": false, - "heightRange": [-10000, -6] + "extents": [ + { + "height": [-10000, -6] + } + ] }, { "name": "2nd Floor", "svgPath": "https://assets.tarkov.dev/maps/svg/StreetsOfTarkov-Second_Floor.svg", "show": false, - "heightRange": [10, 15] + "extents": [ + { + "height": [10, 15] + } + ] }, { "name": "3rd Floor", "svgPath": "https://assets.tarkov.dev/maps/svg/StreetsOfTarkov-Third_Floor.svg", "show": false, - "heightRange": [15, 20] + "extents": [ + { + "height": [15, 20] + } + ] }, { "name": "4th Floor", "svgPath": "https://assets.tarkov.dev/maps/svg/StreetsOfTarkov-Forth_Floor.svg", "show": false, - "heightRange": [20, 25] + "extents": [ + { + "height": [20, 25] + } + ] }, { "name": "5th Floor", "svgPath": "https://assets.tarkov.dev/maps/svg/StreetsOfTarkov-Fifth_Floor.svg", "show": false, - "heightRange": [25, 10000] + "extents": [ + { + "height": [25, 10000] + } + ] } ] }, @@ -75,38 +95,67 @@ "transform": [0.239, 168.65, 0.239, 136.35], "coordinateRotation": 180, "bounds": [[698, -307], [-371, 237]], - "heightRange": [-1000, 2.7], - "author": "Shebuka", - "authorLink": "https://github.com/TarkovTracker/tarkovdata/", + "heightRange": [-1000, 1000], + "author": "Tarkov.dev", + "authorLink": "https://tarkov.dev/", "svgPath": "https://assets.tarkov.dev/maps/svg/Customs-Ground_Level.svg", - "tilePath": "https://assets.tarkov.dev/maps/customs_v2/main/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/customs_v3/main/{z}/{x}/{y}.png", "layers": [ { "name": "Underground", "svgPath": "https://assets.tarkov.dev/maps/svg/Customs-Underground_Level.svg", - "tilePath": "https://assets.tarkov.dev/maps/customs_v2/underground/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/customs_v3/underground/{z}/{x}/{y}.png", "show": false, - "heightRange": [-10000, 0], - "layerBounds": [ - [[635, -137], [620, -125]], - [[473, -122], [458, -110]], - [[349, -88], [323, -32]], - [[219, -158], [193, -137]] + "extents": [ + { + "height": [-10000, 0], + "bounds": [ + [[635, -137], [620, -125]], + [[473, -122], [458, -110]], + [[349, -88], [323, -32]], + [[219, -158], [193, -137]] + ] + } ] }, { "name": "2nd Floor", "svgPath": "https://assets.tarkov.dev/maps/svg/Customs-Second_Floor.svg", - "tilePath": "https://assets.tarkov.dev/maps/customs_v2/2nd/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/customs_v3/2nd/{z}/{x}/{y}.png", "show": false, - "heightRange": [2.7, 6.5] + "extents": [ + { + "height": [2.7, 6.5], + "bounds": [ + [[243, 190], [165, 125]], + [[116, -83], [72, -170]], + [[356, -30], [341, -84]], + [[334, -52], [321, -59]], + [[589, 10], [577, -1]], + [[580, -104], [532, -134]], + [[625, -120], [599, -139]] + ] + } + ] }, { "name": "3rd Floor", "svgPath": "https://assets.tarkov.dev/maps/svg/Customs-Third_Floor.svg", - "tilePath": "https://assets.tarkov.dev/maps/customs_v2/3rd/{z}/{x}/{y}.png", + "tilePath": "https://assets.tarkov.dev/maps/customs_v3/3rd/{z}/{x}/{y}.png", "show": false, - "heightRange": [6.5, 10000] + "extents": [ + { + "height": [6.5, 10000], + "bounds": [ + [[243, 190], [165, 125]], + [[-199, -90], [-210, -114]], + [[239, 3], [169, -160]], + [[336, -56], [316, -95]], + [[584, -46], [556, -92]], + [[93, 0], [65, -22]] + ] + } + ] } ] }, @@ -149,21 +198,33 @@ "tilePath": "https://assets.tarkov.dev/maps/factory_v2/2nd_notint/{z}/{x}/{y}.png", "svgPath": "https://assets.tarkov.dev/maps/svg/Factory-First_Floor.svg", "show": false, - "heightRange": [3, 6] + "extents": [ + { + "height": [3, 6] + } + ] }, { "name": "3rd Floor", "tilePath": "https://assets.tarkov.dev/maps/factory_v2/3rd_notint/{z}/{x}/{y}.png", "svgPath": "https://assets.tarkov.dev/maps/svg/Factory-Second_Floor.svg", "show": false, - "heightRange": [6, 10000] + "extents": [ + { + "height": [6, 10000] + } + ] }, { "name": "Tunnels", "tilePath": "https://assets.tarkov.dev/maps/factory_v2/tunnels_notint/{z}/{x}/{y}.png", "svgPath": "https://assets.tarkov.dev/maps/svg/Factory-Basement.svg", "show": false, - "heightRange": [-10000, -1] + "extents": [ + { + "height": [-10000, -1] + } + ] } ] }, @@ -196,13 +257,21 @@ "name": "2nd Floor", "svgPath": "https://assets.tarkov.dev/maps/svg/Interchange-First_Floor.svg", "show": false, - "heightRange": [25, 34] + "extents": [ + { + "height": [25, 34] + } + ] }, { "name": "3rd Floor", "svgPath": "https://assets.tarkov.dev/maps/svg/Interchange-Second_Floor.svg", "show": false, - "heightRange": [34, 10000] + "extents": [ + { + "height": [34, 10000] + } + ] } ] }, @@ -236,16 +305,24 @@ "name": "Second Level", "tilePath": "https://assets.tarkov.dev/maps/labs_v3/2nd/{z}/{x}/{y}.png", "show": false, - "heightRange": [3, 10000], - "layerBounds": [ - [[-101, -422], [-271, -270]] + "extents": [ + { + "height": [3, 10000], + "bounds": [ + [[-101, -422], [-271, -270]] + ] + } ] }, { "name": "Technical", "tilePath": "https://assets.tarkov.dev/maps/labs_v3/technical/{z}/{x}/{y}.png", "show": false, - "heightRange": [-10000, -0.9] + "extents": [ + { + "height": [-10000, -0.9] + } + ] } ] }, @@ -303,18 +380,22 @@ "minZoom": 2, "maxZoom": 6, "transform": [0.268, 0, 0.268, 0], - "coordinateRotation": 195.2090, - "bounds": [[343, -318], [-346, 326]], + "coordinateRotation": 180, + "bounds": [[289, -338], [-303, 336]], "author": "Shebuka", "authorLink": "https://github.com/TarkovTracker/tarkovdata/", - "svgPath": "https://assets.tarkov.dev/maps/svg/Reserve-Ground_Level.svg", + "svgPath": "https://assets.tarkov.dev/maps/svg/Reserve_cardinal-Ground_Level.svg", "heightRange": [-8, 10000], "layers": [ { "name": "Bunkers", - "svgPath": "https://assets.tarkov.dev/maps/svg/Reserve-Bunkers.svg", + "svgPath": "https://assets.tarkov.dev/maps/svg/Reserve_cardinal-Bunkers.svg", "show": false, - "heightRange": [-10000, -8] + "extents": [ + { + "height": [-10000, -8] + } + ] } ] }, @@ -353,19 +434,31 @@ "name": "Underground", "svgPath": "https://assets.tarkov.dev/maps/svg/Shoreline-Underground_Level.svg", "show": false, - "heightRange": [-10000, -66] + "extents": [ + { + "height": [-10000, -66] + } + ] }, { "name": "2nd Floor", "svgPath": "https://assets.tarkov.dev/maps/svg/Shoreline-Second_Floor.svg", "show": false, - "heightRange": [-2, 2] + "extents": [ + { + "height": [-2, 2] + } + ] }, { "name": "3rd Floor", "svgPath": "https://assets.tarkov.dev/maps/svg/Shoreline-Third_Floor.svg", "show": false, - "heightRange": [2, 10000] + "extents": [ + { + "height": [2, 10000] + } + ] } ] }, From bc63f819c4a0fbdebcf5d5e1b239df20b696ef00 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Wed, 8 Nov 2023 07:34:18 -0600 Subject: [PATCH 18/22] fix old gas level --- src/data/maps.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/data/maps.json b/src/data/maps.json index d9c0e6d08..a30130b84 100644 --- a/src/data/maps.json +++ b/src/data/maps.json @@ -113,7 +113,8 @@ [[635, -137], [620, -125]], [[473, -122], [458, -110]], [[349, -88], [323, -32]], - [[219, -158], [193, -137]] + [[219, -158], [193, -137]], + [[314, -173], [308, -184]] ] } ] From 723c0576fa0ed2d1e972e3ce834064167b73f38e Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Wed, 8 Nov 2023 18:52:19 -0600 Subject: [PATCH 19/22] improve layer assignments --- src/components/Map.jsx | 93 +++++++++++++++++++++++++++--------------- src/data/maps.json | 12 +++--- 2 files changed, 65 insertions(+), 40 deletions(-) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index cf465aadc..c223e69fe 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -38,6 +38,7 @@ import './Maps.css'; const showOtherMarkers = true; const showTestMarkers = false; +const markerDebug = false; function getCRS(mapData) { let scaleX = 1; @@ -134,16 +135,20 @@ function markerIsOnLayer(marker, layer) { var bottom = marker.options.bottom || marker.options.position.y; for (const extent of layer.options.extents) { if (top >= extent.height[0] && bottom < extent.height[1]) { + let containedType = 'partial'; + if (bottom >= extent.height[0] && top <= extent.height[1]) { + containedType = 'full'; + } if (extent.bounds) { for (const boundsArray of extent.bounds) { const bounds = getBounds(boundsArray); if (bounds.contains(pos(marker.options.position))) { if (layer.overlay) console.log('marker on layer', marker.options, layer.options) - return true; + return containedType; } } } else { - return true; + return containedType; } } } @@ -157,31 +162,29 @@ function markerIsOnActiveLayer(marker) { const map = marker._map; + // check if marker is completely contained by inactive layer const overlays = map.layerControl._layers.map(l => l.layer).filter(l => Boolean(l.options.extents) && l.options.overlay); - let onAbsentLayer = false; - layerLoop: for (const layer of overlays) { + for (const layer of overlays) { for (const extent of layer.options.extents) { - const onOverlay = markerIsOnLayer(marker, layer); - if (onOverlay && !map.hasLayer(layer) && extent.bounds) { - onAbsentLayer = true; - break layerLoop; + if (markerIsOnLayer(marker, layer) === 'full' && !map.hasLayer(layer) && extent.bounds) { + console.log(marker.options); + return false; } } } - if (onAbsentLayer) { - return false; - } - let activeLayers = Object.values(map._layers).filter(l => l.options?.extents); - if (activeLayers.some(l => l.options.overlay)) { - activeLayers = activeLayers.filter(l => l.options.overlay); + // check if marker is on active overlay + const activeOverlay = Object.values(map._layers).find(l => l.options?.extents && l.options?.overlay); + if (activeOverlay && markerIsOnLayer(marker, activeOverlay)) { + return true; } - for (const layer of activeLayers) { - if (markerIsOnLayer(marker, layer)) { - return true; - } + // check if marker is on base layer + const baseLayer = Object.values(map._layers).find(l => l.options?.extents && !L.options?.overlay); + if (!activeOverlay && markerIsOnLayer(marker, baseLayer)) { + return true; } + return false; } @@ -243,6 +246,18 @@ function outlineToPoly(outline) { return outline.map(vector => [vector.z, vector.x]); } +function addElevation(item, popup) { + if (!markerDebug) { + return; + } + const elevationContent = L.DomUtil.create('div', undefined, popup); + elevationContent.textContent = `Elevation: ${item.position.y.toFixed(2)}`; + if (item.top && item.bottom && item.top !== item.position.y && item.bottom !== item.position.y) { + const heightContent = L.DomUtil.create('div', undefined, popup); + heightContent.textContent = `Top ${item.top.toFixed(2)}, bottom: ${item.bottom.toFixed(2)}`; + } +} + function Map() { let { currentMap } = useParams(); const [searchParams] = useSearchParams(); @@ -800,10 +815,7 @@ function Map() { const spawnDiv = L.DomUtil.create('div', undefined, popupContent); spawnDiv.textContent = categories[`spawn_${spawnType}`]; } - if (showTestMarkers) { - const elevationDiv = L.DomUtil.create('div', undefined, popupContent); - elevationDiv.textContent = `Elevation: ${spawn.position.y.toFixed(2)}`; - } + addElevation(spawn, popupContent); const marker = L.marker(pos(spawn.position), { icon: spawnIcon, @@ -875,6 +887,11 @@ function Map() { linkElement.append(nameElement); popup.appendChild(linkElement); } + addElevation(extract, popup); + extractMarker.bindPopup(L.popup().setContent(popup)); + } else if (markerDebug) { + const popup = L.DomUtil.create('div'); + addElevation(extract, popup); extractMarker.bindPopup(L.popup().setContent(popup)); } extractMarker.on('add', checkMarkerForActiveLayers); @@ -936,10 +953,7 @@ function Map() { const lockLink = getReactLink(`/item/${key.normalizedName}`, lockImage); lockLink.append(`${key.name}`); popupContent.append(lockLink); - if (showTestMarkers) { - const elevationDiv = L.DomUtil.create('div', undefined, popupContent); - elevationDiv.textContent = `Elevation: ${lock.position.y.toFixed(2)}`; - } + addElevation(lock, popupContent); lockMarker.bindPopup(L.popup().setContent(popupContent)); lockMarker.on('add', checkMarkerForActiveLayers); @@ -981,10 +995,7 @@ function Map() { const questItemImage = L.DomUtil.create('img', 'popup-item', questItem); questItemImage.setAttribute('src', `${obj.questItem.baseImageLink}`); questItem.append(`${obj.questItem.name}`); - if (showTestMarkers) { - const elevationDiv = L.DomUtil.create('div', undefined, popupContent); - elevationDiv.textContent = `Elevation: ${position.y.toFixed(2)}`; - } + addElevation({position}, popupContent); questItemMarker.bindPopup(L.popup().setContent(popupContent)); questItemMarker.on('add', checkMarkerForActiveLayers); questItemMarker.on('click', activateMarkerLayer); @@ -1026,6 +1037,7 @@ function Map() { questLink.append(getReactLink(`/task/${quest.normalizedName}`, quest.name)); const objectiveText = L.DomUtil.create('div', undefined, popupContent); objectiveText.textContent = `- ${obj.description}`; + addElevation(zone, popupContent); zoneMarker.bindPopup(L.popup().setContent(popupContent)); zoneMarker.on('add', checkMarkerForActiveLayers); L.layerGroup([rect, zoneMarker]).addTo(questObjectives); @@ -1099,6 +1111,7 @@ function Map() { popup.appendChild(extractElement); } } + addElevation(sw, popup); if (popup.childNodes.length > 0) { switchMarker.bindPopup(L.popup().setContent(popup)); } @@ -1132,6 +1145,11 @@ function Map() { if (!containerLayers[containerPosition.lootContainer.normalizedName]) { containerLayers[containerPosition.lootContainer.normalizedName] = L.layerGroup(); } + if (markerDebug) { + const popup = L.DomUtil.create('div'); + addElevation(containerPosition, popup); + containerMarker.bindPopup(L.popup().setContent(popup)); + } containerMarker.on('add', checkMarkerForActiveLayers); containerMarker.on('click', activateMarkerLayer); containerMarker.addTo(containerLayers[containerPosition.lootContainer.normalizedName]); @@ -1164,10 +1182,12 @@ function Map() { bottom: hazard.bottom, outline: rect, }); - hazardMarker.bindPopup(L.popup().setContent(hazard.name)); - /*hazardMarker.on('click', (e) => { - rect._path.classList.toggle('not-shown'); - });*/ + const popup = L.DomUtil.create('div'); + const hazardText = L.DomUtil.create('div', undefined, popup); + hazardText.textContent = hazard.name; + addElevation(hazard, popup); + hazardMarker.bindPopup(L.popup().setContent(popup)); + hazardMarker.on('mouseover', mouseHoverOutline); hazardMarker.on('mouseout', mouseHoverOutline); hazardMarker.on('click', toggleForceOutline); @@ -1201,6 +1221,11 @@ function Map() { title: weaponPosition.stationaryWeapon.name, position: weaponPosition.position, }); + if (markerDebug) { + const popup = L.DomUtil.create('div'); + addElevation(weaponPosition, popup); + weaponMarker.bindPopup(L.popup().setContent(popup)); + } weaponMarker.on('add', checkMarkerForActiveLayers); weaponMarker.on('click', activateMarkerLayer); weaponMarker.addTo(stationaryWeapons); diff --git a/src/data/maps.json b/src/data/maps.json index a30130b84..9ac3f7bd2 100644 --- a/src/data/maps.json +++ b/src/data/maps.json @@ -108,13 +108,13 @@ "show": false, "extents": [ { - "height": [-10000, 0], + "height": [-1000, 0.5], "bounds": [ [[635, -137], [620, -125]], [[473, -122], [458, -110]], [[349, -88], [323, -32]], - [[219, -158], [193, -137]], - [[314, -173], [308, -184]] + [[314, -173], [308, -184]], + [[219, -158], [193, -137]] ] } ] @@ -146,7 +146,7 @@ "show": false, "extents": [ { - "height": [6.5, 10000], + "height": [5.7, 1000], "bounds": [ [[243, 190], [165, 125]], [[-199, -90], [-210, -114]], @@ -386,7 +386,7 @@ "author": "Shebuka", "authorLink": "https://github.com/TarkovTracker/tarkovdata/", "svgPath": "https://assets.tarkov.dev/maps/svg/Reserve_cardinal-Ground_Level.svg", - "heightRange": [-8, 10000], + "heightRange": [-7, 10000], "layers": [ { "name": "Bunkers", @@ -394,7 +394,7 @@ "show": false, "extents": [ { - "height": [-10000, -8] + "height": [-10000, -7] } ] } From bf229e6ea8569d6ec5bae4f4f28178f1ccefa1ad Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Thu, 9 Nov 2023 07:41:51 -0600 Subject: [PATCH 20/22] remove debugging output --- src/components/Map.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index c223e69fe..ca9f73256 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -143,7 +143,6 @@ function markerIsOnLayer(marker, layer) { for (const boundsArray of extent.bounds) { const bounds = getBounds(boundsArray); if (bounds.contains(pos(marker.options.position))) { - if (layer.overlay) console.log('marker on layer', marker.options, layer.options) return containedType; } } @@ -167,7 +166,6 @@ function markerIsOnActiveLayer(marker) { for (const layer of overlays) { for (const extent of layer.options.extents) { if (markerIsOnLayer(marker, layer) === 'full' && !map.hasLayer(layer) && extent.bounds) { - console.log(marker.options); return false; } } From 0a7d770e2e1c463cd78eaee1d8c13b647d551a56 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Thu, 9 Nov 2023 08:33:26 -0600 Subject: [PATCH 21/22] maintain map state --- src/components/Map.jsx | 129 +++-- src/data/maps_test.json | 600 -------------------- src/modules/leaflet-control-groupedlayer.js | 10 +- 3 files changed, 88 insertions(+), 651 deletions(-) delete mode 100644 src/data/maps_test.json diff --git a/src/components/Map.jsx b/src/components/Map.jsx index ca9f73256..ca139c994 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -24,7 +24,6 @@ import { useMapImages } from '../features/maps'; import useItemsData from '../features/items'; import useQuestsData from '../features/quests'; -import testMapData from '../data/maps_test.json'; import staticMapData from '../data/maps_static.json' import Time from './Time'; @@ -36,7 +35,7 @@ import useStateWithLocalStorage from '../hooks/useStateWithLocalStorage'; import './Maps.css'; -const showOtherMarkers = true; +const showStaticMarkers = false; const showTestMarkers = false; const markerDebug = false; @@ -278,6 +277,8 @@ function Map() { const mapSettingsRef = useRef(savedMapSettings); + const mapViewRef = useRef({}); + useEffect(() => { let viewableHeight = window.innerHeight - document.querySelector('.navigation')?.offsetHeight || 0; if (viewableHeight < 100) { @@ -332,18 +333,42 @@ function Map() { const tMaps = (string) => { return t(string, { ns: 'maps' }) } + let mapCenter = [0, 0]; + let mapZoom = mapData.minZoom+1; + let mapViewRestored = false; if (mapRef.current?._leaflet_id) { + if (mapRef.current.options.id === mapData.id) { + if (mapViewRef.current.center) { + mapCenter = [mapViewRef.current.center.lat, mapViewRef.current.center.lng]; + mapViewRestored = true; + } + if (typeof mapViewRef.current.zoom !== 'undefined') { + mapZoom = mapViewRef.current.zoom; + mapViewRestored = true; + } + } else { + mapViewRef.current.center = undefined; + mapViewRef.current.zoom = undefined; + mapViewRef.current.layer = undefined; + } mapRef.current.remove(); } const map = L.map('leaflet-map', { maxBounds: getScaledBounds(mapData.bounds, 1.5), - center: [0, 0], - zoom: mapData.minZoom+1, + center: mapCenter, + zoom: mapZoom, minZoom: mapData.minZoom, maxZoom: mapData.maxZoom, scrollWheelZoom: true, crs: getCRS(mapData), attributionControl: false, + id: mapData.id, + }); + map.on('zoom', (e) => { + mapViewRef.current.zoom = map.getZoom(); + }); + map.on('move', (e) => { + mapViewRef.current.center = map.getCenter(); }); const layerControl = L.control.groupedLayers(null, null, { position: 'topleft', @@ -352,12 +377,20 @@ function Map() { groupsCollapsable: true, exclusiveOptionalGroups: [tMaps('Levels')], }).addTo(map); + layerControl.on('overlayToggle', (e) => { + const layerState = e.detail; + if (layerState.checked) { + mapViewRef.current.layer = layerState.key; + } else { + mapViewRef.current.layer = undefined; + } + }); layerControl.on('layerToggle', (e) => { const layerState = e.detail; if (!layerState.checked) { mapSettingsRef.current.hiddenLayers.push(layerState.key); - } else { + mapViewRef.current.layer = layerState.key; mapSettingsRef.current.hiddenLayers = mapSettingsRef.current.hiddenLayers.filter(key => key !== layerState.key); } setSavedMapSettings(mapSettingsRef.current); @@ -525,7 +558,7 @@ function Map() { } }); - if (selectedLayer === layer.name) { + if (selectedLayer === layer.name || mapViewRef.current.layer === layer.name) { heightLayer.addTo(map); } else if (!selectedLayer && layer.show) { heightLayer.addTo(map); @@ -681,48 +714,6 @@ function Map() { 'BR': {x:Number.MIN_SAFE_INTEGER, z:Number.MAX_SAFE_INTEGER} } - // Add static items (from test data or static json) - let otherMarkers; - if (showTestMarkers) { - otherMarkers = testMapData; - } - else { - otherMarkers = staticMapData; - } - - if (false && showOtherMarkers) { - for (const category in otherMarkers[mapData.normalizedName]) { - const markerLayer = L.layerGroup(); - - const items = otherMarkers[mapData.normalizedName][category]; - for (const item of items) { - const itemIcon = L.icon({ - iconUrl: `${process.env.PUBLIC_URL}/maps/interactive/${category}.png`, - iconSize: [24, 24], - popupAnchor: [0, -12], - //className: layerIncludesMarker(heightLayer, item) ? '' : 'off-level', - }); - L.marker(pos(item.position), {icon: itemIcon, position: item.position}) - .bindPopup(L.popup().setContent(`${item.name}
    Elevation: ${item.position.y}`)) - .addTo(markerLayer); - - checkMarkerBounds(item.position, markerBounds); - } - - if (items.length > 0) { - var section; - if (category.startsWith('extract')) { - section = t('Extract'); - } - else { - section = t('Items'); - } - markerLayer.addTo(map); - layerControl.addOverlay(markerLayer, ` ${categories[category] || category}`, section); - } - } - } - // Add spawns if (mapData.spawns.length > 0) { const spawnLayers = { @@ -1231,6 +1222,42 @@ function Map() { addLayer(stationaryWeapons, 'stationarygun', 'Interactive'); } + + + // Add static items + if (showStaticMarkers) { + for (const category in staticMapData[mapData.normalizedName]) { + const markerLayer = L.layerGroup(); + + const items = staticMapData[mapData.normalizedName][category]; + for (const item of items) { + const itemIcon = L.icon({ + iconUrl: `${process.env.PUBLIC_URL}/maps/interactive/${category}.png`, + iconSize: [24, 24], + popupAnchor: [0, -12], + //className: layerIncludesMarker(heightLayer, item) ? '' : 'off-level', + }); + L.marker(pos(item.position), {icon: itemIcon, position: item.position}) + .bindPopup(L.popup().setContent(`${item.name}
    Elevation: ${item.position.y}`)) + .addTo(markerLayer); + + checkMarkerBounds(item.position, markerBounds); + } + + if (items.length > 0) { + var section; + if (category.startsWith('extract')) { + section = t('Extract'); + } + else { + section = t('Items'); + } + markerLayer.addTo(map); + layerControl.addOverlay(markerLayer, ` ${categories[category] || category}`, section); + } + } + } + if (showTestMarkers) { console.log(`Markers "bounds": [[${markerBounds.BR.x}, ${markerBounds.BR.z}], [${markerBounds.TL.x}, ${markerBounds.TL.z}]] (already rotated, copy/paste to maps.json)`); @@ -1281,7 +1308,9 @@ function Map() { // maxBounds are bigger than the map and the map center is not in 0,0 so we need to move the view to real center // console.log("Center:", L.latLngBounds(bounds).getCenter(true)); - map.setView(L.latLngBounds(bounds).getCenter(true), undefined, {animate: false}); + if (!mapViewRestored) { + map.setView(L.latLngBounds(bounds).getCenter(true), undefined, {animate: false}); + } mapRef.current = map; if (focusItem.current) { @@ -1292,7 +1321,7 @@ function Map() { } } } - }, [mapData, items, quests, mapRef, playerPosition, t, dispatch, navigate, mapSettingsRef, setSavedMapSettings]); + }, [mapData, items, quests, mapRef, playerPosition, t, dispatch, navigate, mapSettingsRef, setSavedMapSettings, mapViewRef]); if (!mapData) { return ; diff --git a/src/data/maps_test.json b/src/data/maps_test.json deleted file mode 100644 index ed4b66491..000000000 --- a/src/data/maps_test.json +++ /dev/null @@ -1,600 +0,0 @@ -{ - "customs": { - "quest_item": [ - { - "name": "Bronze pocket watch on a chain", - "position": { - "x": 102.2964, - "y": 2.9634, - "z": -5.9064 - } - }, - { - "name": "Secure Folder 0022", - "position": { - "x": -202.8806, - "y": 7.6911, - "z": -102.4012 - } - }, - { - "name": "Secure Folder 0031", - "position": { - "x": 204.073, - "y": 4.6952, - "z": 12.3975 - } - }, - { - "name": "Secure Folder 0048", - "position": { - "x": 367.6828, - "y": 1.3008, - "z": -50.5712 - } - }, - { - "name": "Golden Zibbo lighter", - "position": { - "x": 180.8072, - "y": 6.7378, - "z": 150.0781 - } - }, - { - "name": "Secure Folder 0013", - "position": { - "x": 498.5496, - "y": 5.63900328, - "z": -141.5012 - } - }, - { - "name": "Carbon case", - "position": { - "x": 239.183, - "y": 0.713, - "z": 160.595 - } - }, - { - "name": "Sliderkey Secure Flash drive", - "position": { - "x": 194.2243, - "y": 3.7256, - "z": 171.0656 - } - }, - { - "name": "Sealed letter with a TG logo", - "position": { - "x": 194.7797, - "y": 4.4267, - "z": 173.1325 - } - }, - { - "name": "Package of graphics cards", - "position": { - "x": -204.387573, - "y": 7.32987, - "z": -98.63279 - } - }, - { - "name": "Bank case", - "position": { - "x": 178.07, - "y": 2.877, - "z": 148.702 - } - }, - { - "name": "Flash drive with fake info", - "position": { - "x": -91.521, - "y": 1.027, - "z": 4.346 - } - } - ], - "supply_crate": [ - { - "name": "Technical Supply Crate", - "position": { - "x": 230.6, - "y": 0, - "z": -62.5 - } - }, - { - "name": "Technical Supply Crate", - "position": { - "x": 94.6, - "y": 0, - "z": -125.5 - } - } - ] - }, - "factory": { - "quest_item": [ - { - "name": "Sealed letter", - "position": { - "x": -18.8457, - "y": 0.6842, - "z": 21.6351 - } - }, - { - "name": "Syringe with a chemical", - "position": { - "x": 26.3819, - "y": 9.1867, - "z": 38.9887 - } - } - ] - }, - "interchange": { - "quest_item": [ - { - "name": "OLI cargo route documents", - "position": { - "x": 86.60339, - "y": 28.06081, - "z": 120.860718 - } - }, - { - "name": "Goshan cargo manifests", - "position": { - "x": -76.48059, - "y": 27.9598122, - "z": -157.719238 - } - }, - { - "name": "OLI cargo manifests", - "position": { - "x": 38.0084229, - "y": 27.9528084, - "z": 147.143677 - } - }, - { - "name": "IDEA cargo manifests", - "position": { - "x": -1.99902344, - "y": 27.9606133, - "z": -292.8999 - } - }, - { - "name": "Chemical container #2", - "position": { - "x": 19.3435, - "y": 28.1595, - "z": -106.6355 - } - }, - { - "name": "Chemical container #3", - "position": { - "x": 23.5142841, - "y": 28.033, - "z": -80.812 - } - }, - { - "name": "Clothes design handbook - Part 1", - "position": { - "x": 20.2713623, - "y": 36.60581, - "z": -4.425293 - } - }, - { - "name": "Clothes design handbook - Part 2", - "position": { - "x": -31.44458, - "y": 27.5668068, - "z": -136.191284 - } - } - ] - }, - "the-lab": { - "quest_item": [ - { - "name": "Flash drive marked with blue tape", - "position": { - "x": -133.0114, - "y": 5.0157, - "z": -335.2083 - } - }, - { - "name": "Secured tape", - "position": { - "x": -167.891556, - "y": 4.13383, - "z": -343.641876 - } - } - ] - }, - "lighthouse": { - "quest_item": [ - { - "name": "Water pump operation data", - "position": { - "x": -111.0176, - "y": 9.24679, - "z": -750.147034 - } - }, - { - "name": "Pumping station operation report", - "position": { - "x": 53.2094955, - "y": 8.74386, - "z": -638.8754 - } - }, - { - "name": "Laptop with information", - "position": { - "x": 139.196289, - "y": 3.39897728, - "z": -129.943909 - } - }, - { - "name": "Stolen military documents", - "position": { - "x": -115.694664, - "y": 40.7532349, - "z": 88.8879852 - } - }, - { - "name": "Sealed letter", - "position": { - "x": -248.391052, - "y": 0.134607315, - "z": -328.2831 - } - }, - { - "name": "Forged Lightkeeper intelligence", - "position": { - "x": 21.2779083, - "y": 5.31199932, - "z": -445.487946 - } - }, - { - "name": "Working hard drive", - "position": { - "x": 352.6155, - "y": 16.2855, - "z": 545.5325 - } - } - ] - }, - "reserve": { - "quest_item": [ - { - "name": "Military documents #1", - "position": { - "x": -114.36499, - "y": -13.7081, - "z": 27.3590088 - } - }, - { - "name": "Military documents #2", - "position": { - "x": -114.18103, - "y": -13.6893, - "z": 39.7880249 - } - }, - { - "name": "Military documents #3", - "position": { - "x": -121.867004, - "y": -14.314, - "z": 38.6560059 - } - }, - { - "name": "MBT Integrated Navigation System", - "position": { - "x": 99.78601, - "y": -6.725, - "z": 59.1600342 - } - }, - { - "name": "T-90M Commander Control Panel", - "position": { - "x": -88.065, - "y": -19.129, - "z": 151.055 - } - }, - { - "name": "Medical record #1", - "position": { - "x": -59.0209961, - "y": -3.637, - "z": -36.1919861 - } - }, - { - "name": "Medical record #2", - "position": { - "x": -80.47601, - "y": -3.647, - "z": -30.7659912 - } - }, - { - "name": "Lightkeeper intelligence", - "position": { - "x": 102.242981, - "y": -6.813, - "z": 65.56604 - } - } - ] - }, - "shoreline": { - "quest_item": [ - { - "name": "Secure Folder 0060", - "position": { - "x": -218.02002, - "y": 2.815, - "z": -86.09 - } - }, - { - "name": "UAV SAS disk #1", - "position": { - "x": -715.9717, - "y": -42.1482, - "z": 224.9588 - } - }, - { - "name": "UAV SAS disk #2", - "position": { - "x": -58.2937, - "y": -19.4951, - "z": -311.1382 - } - }, - { - "name": "Key to the closed premises of the Health Resort", - "position": { - "x": -152.349976, - "y": -14.546, - "z": -301.219 - } - }, - { - "name": "Toughbook reinforced laptop", - "position": { - "x": -285.103027, - "y": 2.512, - "z": -89.3500061 - } - }, - { - "name": "Secure Folder 0013", - "position": { - "x": -340.984, - "y": -2.725, - "z": -88.627 - } - }, - { - "name": "Sliderkey Flash drive", - "position": { - "x": -267.220947, - "y": -0.017, - "z": -145.576 - } - }, - { - "name": "Working hard drive", - "position": { - "x": -358.932983, - "y": 2.54, - "z": -76.9750061 - } - }, - { - "name": "Surgery kit marked with a blue symbol", - "position": { - "x": -314.594971, - "y": -55.7136, - "z": 480.1718 - } - }, - { - "name": "Marked Ophthalmoscope", - "position": { - "x": 99.45056, - "y": -44.3777, - "z": 107.002594 - } - }, - { - "name": "Chemical container #1", - "position": { - "x": -186.06189, - "y": -2.7302, - "z": -86.1152039 - } - }, - { - "name": "Motor Controller #2", - "position": { - "x": -753.850952, - "y": -44.348, - "z": 180.932 - } - }, - { - "name": "Motor Controller #3", - "position": { - "x": -289.378052, - "y": 2.225, - "z": -84.373 - } - }, - { - "name": "Single-axis Fiber Optic Gyroscope #2", - "position": { - "x": -181.3706, - "y": 0.2235, - "z": -80.57291 - } - }, - { - "name": "Photo album", - "position": { - "x": -226.552, - "y": 2.089, - "z": -87.571 - } - } - ] - }, - "streets-of-tarkov": { - "quest_item": [ - { - "name": "Registered letter", - "position": { - "x": 39.2349968, - "y": 1.51599979, - "z": 117.6931 - } - }, - { - "name": "Housing office journal", - "position": { - "x": 81.737, - "y": 1.888, - "z": 144.2245 - } - }, - { - "name": "Chemical container with samples", - "position": { - "x": 95.28351, - "y": 2.671, - "z": 318.9527 - } - }, - { - "name": "Accountant's notes", - "position": { - "x": -177.12001, - "y": 6.1466, - "z": 227.096 - } - }, - { - "name": "Flash drive with recordings", - "position": { - "x": -70.079, - "y": 5.75205374, - "z": 69.437 - } - }, - { - "name": "Working hard drive", - "position": { - "x": 213.235886, - "y": -0.88670063, - "z": 410.321228 - } - }, - { - "name": "AG guitar pick", - "position": { - "x": 34.95802, - "y": 12.5019989, - "z": 152.327286 - } - }, - { - "name": "Secure Flash drive", - "position": { - "x": 66.8009949, - "y": 7.72719955, - "z": 303.143 - } - }, - { - "name": "Informant's journal", - "position": { - "x": -127.914, - "y": 2.38099957, - "z": 397.042969 - } - } - ] - }, - "woods": { - "quest_item": [ - { - "name": "Encrypted message", - "position": { - "x": -257.1476, - "y": 8.518, - "z": 7.9998 - } - }, - { - "name": "Blood sample", - "position": { - "x": -94.25195, - "y": -15.702, - "z": 218.992981 - } - }, - { - "name": "Secure Folder 0052", - "position": { - "x": -3.322, - "y": -1.4843, - "z": -81.1259 - } - }, - { - "name": "Motor Controller #1", - "position": { - "x": 233.843018, - "y": -1.235, - "z": -71.10199 - } - }, - { - "name": "Single-axis Fiber Optic Gyroscope #1", - "position": { - "x": 56.7209473, - "y": -1.791, - "z": -50.2489929 - } - } - ] - } -} \ No newline at end of file diff --git a/src/modules/leaflet-control-groupedlayer.js b/src/modules/leaflet-control-groupedlayer.js index 674f678db..ba8faa928 100644 --- a/src/modules/leaflet-control-groupedlayer.js +++ b/src/modules/leaflet-control-groupedlayer.js @@ -348,7 +348,7 @@ L.Control.GroupedLayers = L.Control.extend({ input.groupID = obj.group.id; L.DomEvent.on(input, 'click', this._onInputClick, this); L.DomEvent.on(input, 'click', () => { - if (obj.layer.key) { + if (obj.layer.key && !obj.layer.options.overlay) { controlContainer.dispatchEvent(new CustomEvent('layerToggle', { bubbles: false, detail: { @@ -356,6 +356,14 @@ L.Control.GroupedLayers = L.Control.extend({ checked: input.checked, }, })); + } else if (obj.layer.options.overlay) { + controlContainer.dispatchEvent(new CustomEvent('overlayToggle', { + bubbles: false, + detail: { + key: obj.layer.options.name, + checked: input.checked, + }, + })); } }, this); From a6e7559ecf0f9f2231c29fdebb4e6e9846c2ba55 Mon Sep 17 00:00:00 2001 From: Razzmatazz Date: Thu, 9 Nov 2023 08:51:47 -0600 Subject: [PATCH 22/22] filter out items outside of map bounds --- src/components/Map.jsx | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index ca139c994..2f411e08e 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -709,6 +709,10 @@ function Map() { layerControl.addOverlay(layer, layerOptions.layerName, layerOptions); }; + const positionIsInBounds = (position) => { + return bounds.contains(pos(position)); + }; + let markerBounds = { 'TL': {x:Number.MAX_SAFE_INTEGER, z:Number.MIN_SAFE_INTEGER}, 'BR': {x:Number.MIN_SAFE_INTEGER, z:Number.MAX_SAFE_INTEGER} @@ -725,6 +729,9 @@ function Map() { bloodhound: L.layerGroup(), } for (const spawn of mapData.spawns) { + if (!positionIsInBounds(spawn.position)) { + continue; + } let spawnType = ''; let bosses = []; @@ -840,6 +847,9 @@ function Map() { scav: 100, }; for (const extract of mapData.extracts) { + if (!positionIsInBounds(extract.position)) { + continue; + } const colorMap = { scav: '#ff7800', pmc: '#00e599', @@ -902,6 +912,10 @@ function Map() { if (!key) { continue; } + + if (!positionIsInBounds(lock.position)) { + continue; + } checkMarkerBounds(lock.position, markerBounds); const lockIcon = L.icon({ @@ -966,6 +980,9 @@ function Map() { continue; } for (const position of loc.positions) { + if (!positionIsInBounds(position)) { + continue; + } const questItemIcon = L.icon({ iconUrl: `${process.env.PUBLIC_URL}/maps/interactive/quest_item.png`, iconSize: [24, 24], @@ -999,6 +1016,9 @@ function Map() { if (zone.map.id !== mapData.id) { continue; } + if (!positionIsInBounds(zone.position)) { + continue; + } const rect = L.polygon(outlineToPoly(zone.outline), {color: '#e5e200', weight: 1, className: 'not-shown'}); const zoneIcon = L.icon({ iconUrl: `${process.env.PUBLIC_URL}/maps/interactive/quest_objective.png`, @@ -1045,6 +1065,9 @@ function Map() { if (mapData.switches.length > 0) { const switches = L.layerGroup(); for (const sw of mapData.switches) { + if (!positionIsInBounds(sw.position)) { + continue; + } const switchIcon = L.icon({ iconUrl: `${process.env.PUBLIC_URL}/maps/interactive/switch.png`, iconSize: [24, 24], @@ -1120,6 +1143,9 @@ function Map() { const containerLayers = {}; const containerNames = {}; for (const containerPosition of mapData.lootContainers) { + if (!positionIsInBounds(containerPosition.position)) { + continue; + } const containerIcon = L.icon({ iconUrl: `${process.env.PUBLIC_URL}/maps/interactive/${images[`container_${containerPosition.lootContainer.normalizedName}`]}.png`, iconSize: [24, 24], @@ -1155,6 +1181,9 @@ function Map() { if (mapData.hazards.length > 0) { const hazardLayers = {}; for (const hazard of mapData.hazards) { + if (!positionIsInBounds(hazard.position)) { + continue; + } const rect = L.polygon(outlineToPoly(hazard.outline), {color: '#ff0000', weight: 1, className: 'not-shown'}); const hazardIcon = L.icon({ iconUrl: `${process.env.PUBLIC_URL}/maps/interactive/hazard.png`, @@ -1199,6 +1228,9 @@ function Map() { if (mapData.stationaryWeapons.length > 0) { const stationaryWeapons = L.layerGroup(); for (const weaponPosition of mapData.stationaryWeapons) { + if (!positionIsInBounds(weaponPosition.position)) { + continue; + } const weaponIcon = L.icon({ iconUrl: `${process.env.PUBLIC_URL}/maps/interactive/stationarygun.png`, iconSize: [24, 24],