PageSourceSearch

https://columbiaslough.github.io/shared-map-functions.js

js columbiaslough.github.io collected 2026-10-03 08:49:11 UTC 10,134 bytes, 338 lines download raw bytes

1export const MAP_CONFIG = {
2    accessToken: 'pk.eyJ1IjoiY29sdW1iaWFzbG91Z2giLCJhIjoiY201MWFrbTBmMHN1aTJwcHd1dHloMGs4YyJ9.kQj7ux3XSeQiOBwxzM5B9g',
3    style: 'mapbox://styles/mapbox/satellite-streets-v11',
4    bounds: [[-122.85, 45.53], [-122.35, 45.6]],
5    maxBounds: [[-123, 45.3], [-122.1, 45.8]],
6    bearing: 16
7};
8
9export const LAYER_STYLES = {
10    polygons: {
11        paint: {
12            'fill-color': '#33ff99',
13            'fill-opacity': 0.3,
14            'fill-outline-color': '#d7f531'
15        },
16        minzoom: 13
17    },
18    lines: {
19        paint: {
20            'line-color': '#ff5773',
21            'line-width': 2
22        },
23        minzoom: 13
24    },
25    points: {
26        paint: {
27            'circle-radius': 8,
28            'circle-color': '#ff0000',
29            'circle-opacity': 0.7
30        },
31        minzoom: 13
32    },
33    poi: {
34        circles: {
35            paint: {
36                'circle-radius': 9,
37                'circle-color': '#dc9c3c',
38                'circle-opacity': 1
39            }
40        },
41        labels: {
42            layout: {
43                'text-field': ['get', 'labelId'],
44                'text-size': 10,
45                'text-font': ['Roboto Bold', 'Open Sans Bold', 'Arial Unicode MS Bold'],
46                'text-anchor': 'center',
47                'text-allow-overlap': true,
48                'text-ignore-placement': true,
49                'symbol-placement': 'point'
50            },
51            paint: {
52                'text-color': 'white',
53                'text-halo-color': 'black',
54                'text-halo-width': 0.6
55            }
56        },
57        pointLabels: {
58            layout: {
59                'text-size': 14,
60                'text-font': ['Roboto Bold', 'Open Sans Bold', 'Arial Unicode MS Bold'],
61                'text-anchor': 'bottom-left',
62                'text-offset': [0.5, -0.5],
63                'text-allow-overlap': false,
64                'text-ignore-placement': false,
65                'symbol-placement': 'point',
66                'visibility': 'none'
67            },
68            paint: {
69                'text-color': 'white',
70                'text-halo-color': 'black',
71                'text-halo-width': 2
72            }
73        }
74    }
75};
76
77export function initializeMapBase(mapboxgl) {
78    mapboxgl.accessToken = MAP_CONFIG.accessToken;
79    const map = new mapboxgl.Map({
80        container: 'map',
81        style: MAP_CONFIG.style,
82        bounds: MAP_CONFIG.bounds,
83        maxBounds: MAP_CONFIG.maxBounds,
84        minZoom: 2
85    });
86    map.setBearing(MAP_CONFIG.bearing);
87    return map;
88}
89
90export function addPolygonsLayer(map, data) {
91    if (!data || !data.features || data.features.length === 0) {
92        console.warn('No polygon features to add');
93        return;
94    }
95
96    map.addSource('polygons', {
97        type: 'geojson',
98        data: data
99    });
100
101    map.addLayer({
102        id: 'polygons-layer',
103        type: 'fill',
104        source: 'polygons',
105        paint: LAYER_STYLES.polygons.paint,
106        layout: { visibility: 'none' },
107        minzoom: LAYER_STYLES.polygons.minzoom
108    });
109}
110
111export function addLinesLayer(map, data) {
112    if (!data || !data.features || data.features.length === 0) {
113        console.warn('No line features to add');
114        return;
115    }
116
117    map.addSource('lines', {
118        type: 'geojson',
119        data: data
120    });
121
122    map.addLayer({
123        id: 'lines-layer',
124        type: 'line',
125        source: 'lines',
126        paint: LAYER_STYLES.lines.paint,
127        layout: { visibility: 'none' },
128        minzoom: LAYER_STYLES.lines.minzoom
129    });
130}
131
132export function addPointsLayer(map, data, iconNames = ['restrooms', 'parking', 'boats']) {
133    if (!data || !data.features || data.features.length === 0) {
134        console.warn('No point features to add');
135        return Promise.resolve();
136    }
137
138    console.log('Points data:', data);
139
140    const loadIcon = (iconName) => {
141        return new Promise((resolve, reject) => {
142            map.loadImage(`resources/icons/${iconName}.png`, (error, image) => {
143                if (error) {
144                    console.error(`Error loading icon ${iconName}:`, error);
145                    reject(error);
146                    return;
147                }
148                if (!map.hasImage(iconName)) {
149                    map.addImage(iconName, image);
150                }
151                resolve();
152            });
153        });
154    };
155
156    return Promise.all(iconNames.map(icon => loadIcon(icon)))
157        .then(() => {
158            map.addSource('points', {
159                type: 'geojson',
160                data: data
161            });
162
163            map.addLayer({
164                id: 'points-layer',
165                type: 'symbol',
166                source: 'points',
167                layout: {
168                    'icon-image': ['get', 'icon_type'],
169                    'icon-size': 0.09,
170                    'visibility': 'none'
171                },
172                minzoom: LAYER_STYLES.points.minzoom
173            });
174        })
175        .catch(error => console.error('Error loading point icons:', error));
176}
177
178export function addPOILayer(map, data, currentLanguage = 'en') {
179    if (!data || !data.features || !Array.isArray(data.features)) {
180        console.error('Invalid POI data structure:', data);
181        return;
182    }
183
184    map.addSource('poi', {
185        type: 'geojson',
186        data: data
187    });
188
189    map.addLayer({
190        id: 'poi-circles',
191        type: 'circle',
192        source: 'poi',
193        paint: LAYER_STYLES.poi.circles.paint
194    });
195
196    map.addLayer({
197        id: 'poi-labels',
198        type: 'symbol',
199        source: 'poi',
200        layout: LAYER_STYLES.poi.labels.layout,
201        paint: LAYER_STYLES.poi.labels.paint
202    });
203
204    map.addLayer({
205        id: 'poi-point-labels',
206        type: 'symbol',
207        source: 'poi',
208        layout: {
209            ...LAYER_STYLES.poi.pointLabels.layout,
210            'text-field': ['get', `name_${currentLanguage}`]
211        },
212        paint: LAYER_STYLES.poi.pointLabels.paint
213    });
214}
215
216export function addMaskLayer(map, data) {
217    if (!data || !data.features || data.features.length === 0) {
218        console.warn('No mask features to add');
219        return;
220    }
221
222    map.addSource('mask', {
223        type: 'geojson',
224        data: data
225    });
226
227    map.addLayer({
228        id: 'mask-layer',
229        type: 'fill',
230        source: 'mask',
231        paint: {
232            'fill-color': '#cfcfcf',
233            'fill-opacity': 0.5,
234            'fill-outline-color': '#cccccc'
235        },
236        layout: { visibility: 'visible' }
237    });
238}
239
240export function parseImages(imagesString) {
241    if (!imagesString) return [];
242    return imagesString.split(',').map(img =>
242 img.trim()).filter(Boolean);
243}
244
245export function hasRealImages(images) {
246    return images.length > 0 && images[0] !== 'image0' && images[0].startsWith('http');
247}
248
249export function getImageUrl(imageUrl) {
250    if (imageUrl && imageUrl.startsWith('http')) {
251        return imageUrl;
252    }
253}
254
255export function filterAndShowLayer(map, sourceId, layerId, data, property, filterValue) {
256    if (!data || !data.features) {
257        console.log(`No data available for ${sourceId}`);
258        return;
259    }
260
261    const source = map.getSource(sourceId);
262    if (!source) {
263        console.log(`Source ${sourceId} not found`);
264        return;
265    }
266
267    try {
268        const matchingFeatures = data.features.filter(
269            feature => feature.properties[property] === filterValue
270        );
271
272        if (matchingFeatures.length > 0) {
273            const filteredData = {
274                type: 'FeatureCollection',
275                features: matchingFeatures
276            };
277            source.setData(filteredData);
278            map.setLayoutProperty(layerId, 'visibility', 'visible');
279            console.log(`Showing ${layerId} with ${matchingFeatures.length} features`);
280        } else {
281            map.setLayoutProperty(layerId, 'visibility', 'none');
282        }
283    } catch (error) {
284        console.error(`Error filtering ${sourceId}:`, error);
285    }
286}
287
288export function updatePOILanguage(map, language) {
289    if (map.getLayer('poi-point-labels')) {
290        map.setLayoutProperty('poi-point-labels', 'text-field', ['get', `name_${language}`]);
291    }
292}
293
294export function getPopupImageHtml(images, pointId, isEditable = false) {
295    const realImages = hasRealImages(images);
296    
297    if (realImages) {
298        return `<img id="current-image" src="${images[0]}" alt="picture" class="popup-image" />`;
299    } else {
300        return `<p class="no-images-message">No images available</p>`;
301    }
302}
303
304export function getIconsHtml(tagsInput) {
305    if (!tagsInput) return '';
306    
307    const tags = Array.isArray(tagsInput) 
308        ? tagsInput 
309        : tagsInput.split(',').map(tag => tag.trim()).filter(Boolean);
310    
311    return tags.map(tag => `
312        <div class="icon-container" title="${tag}">
313            <img src="resources/icons/${tag}.svg" alt="${tag}" class="icon-image">
314        </div>
315    `).join('');
316}
317
318export function getMapLinksHtml(latitude, longitude) {
319    return `
320        <div class="map-icons">
321            <div class="map-icon-container" title="Open in Google Maps">
322                <a href="https://maps.google.com/?q=${latitude},${longitude}" target="_blank">
323                    <img src="https://www.vectorlogo.zone/logos/google_maps/google_maps-icon.svg" class="icon-image">
324                </a>
325            </div>
326            <div class="map-icon-container" title="Open in Apple Maps">
327                <a href="http://maps.apple.com/?q=${latitude},${longitude}" target="_blank">
328                    <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQk4kU3uCXeJ2uIbJL0bZQbm1KNRjnI7vW3Ww&s" class="icon-image">
329                </a>
330            </div>
331            <div class="map-icon-container" title="Open in Bing Maps">
332                <a href="https://bing.com/maps/default.aspx?cp=${latitude}~${longitude}&lvl=15" target="_blank">
333                    <img src="https://download.logo.wine/logo/Bing_Maps_Platform/Bing_Maps_Platform-Logo.wine.png" class="icon-image">
334                </a>
335            </div>
336        </div>
337    `;
338}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.