fix(CatalogMap): use proper icons with colors for related points
- Add loadRelatedPointIcons to load icons for all entity types - Change related points layer from circle to symbol with icons - Each type (hub, supplier, offer) gets its standard color: - hub: green (#22c55e) - supplier: blue (#3b82f6) - offer: orange (#f97316)
This commit is contained in:
@@ -128,6 +128,53 @@ const loadEntityIcon = async (map: MapboxMapType, type: 'offer' | 'hub' | 'suppl
|
||||
})
|
||||
}
|
||||
|
||||
// Standard colors for entity types
|
||||
const ENTITY_COLORS = {
|
||||
hub: '#22c55e', // green
|
||||
supplier: '#3b82f6', // blue
|
||||
offer: '#f97316' // orange
|
||||
} as const
|
||||
|
||||
// Load all icons for related points (each type with its standard color)
|
||||
const loadRelatedPointIcons = async (map: MapboxMapType) => {
|
||||
const types: Array<'hub' | 'supplier' | 'offer'> = ['hub', 'supplier', 'offer']
|
||||
for (const type of types) {
|
||||
const iconName = `related-icon-${type}`
|
||||
if (map.hasImage(iconName)) {
|
||||
map.removeImage(iconName)
|
||||
}
|
||||
|
||||
const svg = createEntityIcon(type, ENTITY_COLORS[type])
|
||||
const img = new Image(32, 32)
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = 32
|
||||
canvas.height = 32
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (ctx) {
|
||||
ctx.beginPath()
|
||||
ctx.arc(16, 16, 15, 0, 2 * Math.PI)
|
||||
ctx.fillStyle = ENTITY_COLORS[type]
|
||||
ctx.fill()
|
||||
ctx.strokeStyle = 'white'
|
||||
ctx.lineWidth = 2
|
||||
ctx.stroke()
|
||||
ctx.drawImage(img, 4, 4, 24, 24)
|
||||
}
|
||||
|
||||
const imageData = ctx?.getImageData(0, 0, 32, 32)
|
||||
if (imageData) {
|
||||
map.addImage(iconName, { width: 32, height: 32, data: imageData.data })
|
||||
}
|
||||
resolve()
|
||||
}
|
||||
img.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const mapOptions = computed(() => ({
|
||||
style: 'mapbox://styles/mapbox/satellite-streets-v12',
|
||||
center: props.initialCenter,
|
||||
@@ -372,27 +419,28 @@ const initClientClusteringLayers = async (map: MapboxMapType) => {
|
||||
}
|
||||
})
|
||||
|
||||
// Related points layer (for Info mode - colored by type)
|
||||
// Related points layer (for Info mode - icons by type)
|
||||
await loadRelatedPointIcons(map)
|
||||
|
||||
map.addSource(relatedSourceId.value, {
|
||||
type: 'geojson',
|
||||
data: relatedPointsGeoJson.value
|
||||
})
|
||||
map.addLayer({
|
||||
id: `${props.mapId}-related-circles`,
|
||||
type: 'circle',
|
||||
id: `${props.mapId}-related-points`,
|
||||
type: 'symbol',
|
||||
source: relatedSourceId.value,
|
||||
paint: {
|
||||
'circle-radius': 8,
|
||||
'circle-color': [
|
||||
layout: {
|
||||
'icon-image': [
|
||||
'match',
|
||||
['get', 'type'],
|
||||
'hub', '#22c55e', // green
|
||||
'supplier', '#3b82f6', // blue
|
||||
'offer', '#f97316', // orange
|
||||
'#06b6d4' // default cyan
|
||||
'hub', 'related-icon-hub',
|
||||
'supplier', 'related-icon-supplier',
|
||||
'offer', 'related-icon-offer',
|
||||
'related-icon-offer' // default
|
||||
],
|
||||
'circle-stroke-width': 2,
|
||||
'circle-stroke-color': '#ffffff'
|
||||
'icon-size': 1,
|
||||
'icon-allow-overlap': true
|
||||
}
|
||||
})
|
||||
map.addLayer({
|
||||
@@ -403,7 +451,7 @@ const initClientClusteringLayers = async (map: MapboxMapType) => {
|
||||
'text-field': ['get', 'name'],
|
||||
'text-size': 11,
|
||||
'text-anchor': 'top',
|
||||
'text-offset': [0, 1.2]
|
||||
'text-offset': [0, 1.5]
|
||||
},
|
||||
paint: {
|
||||
'text-color': '#ffffff',
|
||||
@@ -413,19 +461,19 @@ const initClientClusteringLayers = async (map: MapboxMapType) => {
|
||||
})
|
||||
|
||||
// Click handlers for related points
|
||||
map.on('click', `${props.mapId}-related-circles`, (e) => {
|
||||
const features = map.queryRenderedFeatures(e.point, { layers: [`${props.mapId}-related-circles`] })
|
||||
map.on('click', `${props.mapId}-related-points`, (e) => {
|
||||
const features = map.queryRenderedFeatures(e.point, { layers: [`${props.mapId}-related-points`] })
|
||||
const feature = features[0]
|
||||
if (!feature) return
|
||||
const props_data = feature.properties as Record<string, any> | undefined
|
||||
emit('select-item', props_data?.uuid, props_data)
|
||||
})
|
||||
|
||||
map.on('mouseenter', `${props.mapId}-related-circles`, () => {
|
||||
map.on('mouseenter', `${props.mapId}-related-points`, () => {
|
||||
map.getCanvas().style.cursor = 'pointer'
|
||||
})
|
||||
|
||||
map.on('mouseleave', `${props.mapId}-related-circles`, () => {
|
||||
map.on('mouseleave', `${props.mapId}-related-points`, () => {
|
||||
map.getCanvas().style.cursor = ''
|
||||
})
|
||||
|
||||
@@ -564,27 +612,28 @@ const initServerClusteringLayers = async (map: MapboxMapType) => {
|
||||
}
|
||||
})
|
||||
|
||||
// Related points layer (for Info mode - colored by type)
|
||||
// Related points layer (for Info mode - icons by type)
|
||||
await loadRelatedPointIcons(map)
|
||||
|
||||
map.addSource(relatedSourceId.value, {
|
||||
type: 'geojson',
|
||||
data: relatedPointsGeoJson.value
|
||||
})
|
||||
map.addLayer({
|
||||
id: `${props.mapId}-related-circles`,
|
||||
type: 'circle',
|
||||
id: `${props.mapId}-related-points`,
|
||||
type: 'symbol',
|
||||
source: relatedSourceId.value,
|
||||
paint: {
|
||||
'circle-radius': 8,
|
||||
'circle-color': [
|
||||
layout: {
|
||||
'icon-image': [
|
||||
'match',
|
||||
['get', 'type'],
|
||||
'hub', '#22c55e', // green
|
||||
'supplier', '#3b82f6', // blue
|
||||
'offer', '#f97316', // orange
|
||||
'#06b6d4' // default cyan
|
||||
'hub', 'related-icon-hub',
|
||||
'supplier', 'related-icon-supplier',
|
||||
'offer', 'related-icon-offer',
|
||||
'related-icon-offer' // default
|
||||
],
|
||||
'circle-stroke-width': 2,
|
||||
'circle-stroke-color': '#ffffff'
|
||||
'icon-size': 1,
|
||||
'icon-allow-overlap': true
|
||||
}
|
||||
})
|
||||
map.addLayer({
|
||||
@@ -595,7 +644,7 @@ const initServerClusteringLayers = async (map: MapboxMapType) => {
|
||||
'text-field': ['get', 'name'],
|
||||
'text-size': 11,
|
||||
'text-anchor': 'top',
|
||||
'text-offset': [0, 1.2]
|
||||
'text-offset': [0, 1.5]
|
||||
},
|
||||
paint: {
|
||||
'text-color': '#ffffff',
|
||||
@@ -605,19 +654,19 @@ const initServerClusteringLayers = async (map: MapboxMapType) => {
|
||||
})
|
||||
|
||||
// Click handlers for related points
|
||||
map.on('click', `${props.mapId}-related-circles`, (e) => {
|
||||
const features = map.queryRenderedFeatures(e.point, { layers: [`${props.mapId}-related-circles`] })
|
||||
map.on('click', `${props.mapId}-related-points`, (e) => {
|
||||
const features = map.queryRenderedFeatures(e.point, { layers: [`${props.mapId}-related-points`] })
|
||||
const feature = features[0]
|
||||
if (!feature) return
|
||||
const props_data = feature.properties as Record<string, any> | undefined
|
||||
emit('select-item', props_data?.uuid, props_data)
|
||||
})
|
||||
|
||||
map.on('mouseenter', `${props.mapId}-related-circles`, () => {
|
||||
map.on('mouseenter', `${props.mapId}-related-points`, () => {
|
||||
map.getCanvas().style.cursor = 'pointer'
|
||||
})
|
||||
|
||||
map.on('mouseleave', `${props.mapId}-related-circles`, () => {
|
||||
map.on('mouseleave', `${props.mapId}-related-points`, () => {
|
||||
map.getCanvas().style.cursor = ''
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user