mirror of
https://github.com/hyperknot/openfreemap.git
synced 2026-05-21 14:02:15 +00:00
mix
This commit is contained in:
@@ -48,12 +48,40 @@
|
||||
maxlength="5"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="pt-5">
|
||||
<button
|
||||
id="shareBtn"
|
||||
class="px-4 py-1.5 text-sm font-medium text-white bg-blue-600 rounded hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-gray-900 transition-all"
|
||||
>
|
||||
Share
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="map" class="flex-1"></div>
|
||||
|
||||
<!-- Modal Dialog -->
|
||||
<div id="shareModal" class="hidden fixed inset-0 z-50">
|
||||
<div id="modalOverlay" class="fixed inset-0 bg-black/50"></div>
|
||||
<div class="fixed inset-0 flex items-center justify-center p-4">
|
||||
<div class="relative bg-gray-800 rounded-lg p-8 max-w-md shadow-2xl">
|
||||
<p class="text-gray-300 text-center leading-relaxed mb-6">
|
||||
Your settings have been saved to the URL.<br>
|
||||
Copy the address bar to share this map.
|
||||
</p>
|
||||
<button
|
||||
id="closeModalBtn"
|
||||
class="w-full py-2.5 text-sm text-white bg-gray-700 rounded hover:bg-gray-600 transition-all"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/maplibre-gl/dist/maplibre-gl.js"></script>
|
||||
<script src="mix.js"></script>
|
||||
</body>
|
||||
|
||||
@@ -1,37 +1,157 @@
|
||||
// ============================================
|
||||
// 1. MAIN EXECUTION (Entry Point)
|
||||
// ============================================
|
||||
|
||||
const map = new maplibregl.Map({
|
||||
container: 'map',
|
||||
style: 'https://tiles.openfreemap.org/styles/liberty',
|
||||
center: [0, 0],
|
||||
zoom: 2,
|
||||
hash: true,
|
||||
})
|
||||
|
||||
const line1Input = document.getElementById('line1')
|
||||
const line2Input = document.getElementById('line2')
|
||||
const langInput = document.getElementById('lang')
|
||||
|
||||
// Parse URL search params to get line1 and line2
|
||||
function parseParams() {
|
||||
map.on('load', () => {
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
return {
|
||||
line1: params.get('line1') || 'underscore,colon,name,latin',
|
||||
line2: params.get('line2') || 'nonlatin',
|
||||
lang: params.get('lang') || 'en',
|
||||
}
|
||||
}
|
||||
|
||||
// Update URL search params
|
||||
function updateParams(line1, line2, lang) {
|
||||
// Set defaults if no params present
|
||||
if (!params.has('line1') && !params.has('line2') && !params.has('lang')) {
|
||||
const url = new URL(window.location)
|
||||
url.searchParams.set('line1', line1)
|
||||
url.searchParams.set('line2', line2)
|
||||
url.searchParams.set('lang', lang)
|
||||
url.searchParams.set('line1', 'underscore,colon,name,latin')
|
||||
url.searchParams.set('line2', 'nonlatin')
|
||||
url.searchParams.set('lang', 'en')
|
||||
window.history.replaceState({}, '', url)
|
||||
}
|
||||
|
||||
// Build field accessor from config string
|
||||
syncInputsFromParams()
|
||||
applyConfiguration()
|
||||
initializeInputListeners()
|
||||
initializeModal()
|
||||
})
|
||||
|
||||
// ============================================
|
||||
// 2. UI INITIALIZATION
|
||||
// ============================================
|
||||
|
||||
function initializeInputListeners() {
|
||||
line1Input.addEventListener('input', updateParamsFromInputs)
|
||||
line2Input.addEventListener('input', updateParamsFromInputs)
|
||||
langInput.addEventListener('input', updateParamsFromInputs)
|
||||
}
|
||||
|
||||
function initializeModal() {
|
||||
const modal = document.getElementById('shareModal')
|
||||
|
||||
document.getElementById('shareBtn').addEventListener('click', () => {
|
||||
modal.classList.remove('hidden')
|
||||
})
|
||||
|
||||
document.getElementById('closeModalBtn').addEventListener('click', () => {
|
||||
modal.classList.add('hidden')
|
||||
})
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && !modal.classList.contains('hidden')) {
|
||||
modal.classList.add('hidden')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 3. CONFIGURATION & SYNC
|
||||
// ============================================
|
||||
|
||||
function applyConfiguration() {
|
||||
const { line1, line2, lang } = parseParams()
|
||||
|
||||
if (!map.getStyle()) return
|
||||
|
||||
const style = map.getStyle()
|
||||
modifyStyle({
|
||||
style,
|
||||
line1Config: line1 ?? '',
|
||||
line2Config: line2 ?? '',
|
||||
langCode: lang || 'en',
|
||||
})
|
||||
map.setStyle(style, { diff: false })
|
||||
}
|
||||
|
||||
function syncInputsFromParams() {
|
||||
const { line1, line2, lang } = parseParams()
|
||||
line1Input.value = line1 ?? ''
|
||||
line2Input.value = line2 ?? ''
|
||||
langInput.value = lang ?? ''
|
||||
}
|
||||
|
||||
function updateParamsFromInputs() {
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
params.set('line1', line1Input.value)
|
||||
params.set('line2', line2Input.value)
|
||||
params.set('lang', langInput.value)
|
||||
|
||||
const queryString = params.toString()
|
||||
const hash = window.location.hash
|
||||
const newUrl = `${window.location.pathname}?${queryString}${hash}`
|
||||
|
||||
window.history.replaceState({}, '', newUrl)
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 4. STYLE MODIFICATION
|
||||
// ============================================
|
||||
|
||||
function modifyStyle({ style, line1Config, line2Config, langCode }) {
|
||||
for (const layer of style.layers) {
|
||||
if (layer.source !== 'openmaptiles') continue
|
||||
if (!layer.layout) continue
|
||||
|
||||
const textField = layer.layout['text-field']
|
||||
if (!textField) continue
|
||||
|
||||
if (JSON.stringify(textField) === JSON.stringify(['to-string', ['get', 'ref']])) continue
|
||||
|
||||
const id = layer.id
|
||||
const separator = id.includes('line') || id.includes('highway') ? ' ' : '\n'
|
||||
|
||||
const line1Expr = buildFieldAccessor(line1Config, langCode)
|
||||
const line2Expr = buildFieldAccessor(line2Config, langCode)
|
||||
|
||||
if (line1Expr && line2Expr) {
|
||||
layer.layout['text-field'] = ['concat', line1Expr, separator, line2Expr]
|
||||
} else if (line1Expr) {
|
||||
layer.layout['text-field'] = line1Expr
|
||||
} else if (line2Expr) {
|
||||
layer.layout['text-field'] = line2Expr
|
||||
} else {
|
||||
layer.layout['text-field'] = ['get', 'name']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 5. UTILITY FUNCTIONS
|
||||
// ============================================
|
||||
|
||||
function parseParams() {
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
return {
|
||||
line1: params.get('line1'),
|
||||
line2: params.get('line2'),
|
||||
lang: params.get('lang'),
|
||||
}
|
||||
}
|
||||
|
||||
function buildFieldAccessor(config, langCode) {
|
||||
if (!config) return null
|
||||
|
||||
const parts = []
|
||||
const fields = config.split(',').map(f => f.trim()).filter(f => f)
|
||||
const fields = config
|
||||
.split(',')
|
||||
.map((f) => f.trim())
|
||||
.filter((f) => f)
|
||||
|
||||
for (const field of fields) {
|
||||
if (field === 'underscore') {
|
||||
@@ -45,96 +165,9 @@ function buildFieldAccessor(config, langCode) {
|
||||
} else if (field === 'nonlatin') {
|
||||
parts.push(['get', 'name:nonlatin'])
|
||||
} else {
|
||||
// Custom field name
|
||||
parts.push(['get', field])
|
||||
}
|
||||
}
|
||||
|
||||
return parts.length > 0 ? ['coalesce', ...parts] : ['get', 'name']
|
||||
return parts.length > 0 ? ['coalesce', ...parts] : null
|
||||
}
|
||||
|
||||
function modifyStyle({ style, line1Config, line2Config, langCode }) {
|
||||
for (const layer of style.layers) {
|
||||
if (layer.source !== 'openmaptiles') continue
|
||||
if (!layer.layout) continue
|
||||
|
||||
const textField = layer.layout['text-field']
|
||||
if (!textField) continue
|
||||
|
||||
// Skip ref-only fields
|
||||
if (JSON.stringify(textField) === JSON.stringify(['to-string', ['get', 'ref']])) continue
|
||||
|
||||
const id = layer.id
|
||||
let separator = id.includes('line') || id.includes('highway') ? ' ' : '\n'
|
||||
|
||||
const line1Expr = buildFieldAccessor(line1Config, langCode)
|
||||
const line2Expr = buildFieldAccessor(line2Config, langCode)
|
||||
|
||||
// Combine both lines
|
||||
layer.layout['text-field'] = ['concat', line1Expr, separator, line2Expr]
|
||||
}
|
||||
}
|
||||
|
||||
function applyConfiguration() {
|
||||
const { line1, line2, lang } = parseParams()
|
||||
|
||||
if (!map.getStyle()) return
|
||||
|
||||
const style = map.getStyle()
|
||||
modifyStyle({ style, line1Config: line1, line2Config: line2, langCode: lang })
|
||||
map.setStyle(style, { diff: false })
|
||||
}
|
||||
|
||||
function syncInputsFromParams() {
|
||||
const { line1, line2, lang } = parseParams()
|
||||
line1Input.value = line1
|
||||
line2Input.value = line2
|
||||
langInput.value = lang
|
||||
}
|
||||
|
||||
// Update params when inputs change
|
||||
line1Input.addEventListener('input', () => {
|
||||
updateParams(line1Input.value, line2Input.value, langInput.value)
|
||||
})
|
||||
|
||||
line2Input.addEventListener('input', () => {
|
||||
updateParams(line1Input.value, line2Input.value, langInput.value)
|
||||
})
|
||||
|
||||
langInput.addEventListener('input', () => {
|
||||
updateParams(line1Input.value, line2Input.value, langInput.value)
|
||||
})
|
||||
|
||||
// Apply configuration when Enter is pressed
|
||||
line1Input.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') applyConfiguration()
|
||||
})
|
||||
|
||||
line2Input.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') applyConfiguration()
|
||||
})
|
||||
|
||||
langInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') applyConfiguration()
|
||||
})
|
||||
|
||||
map.on('load', () => {
|
||||
// Initialize params if not present
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
if (!params.has('line1') || !params.has('line2') || !params.has('lang')) {
|
||||
const url = new URL(window.location)
|
||||
if (!params.has('line1')) url.searchParams.set('line1', 'underscore,colon,name,latin')
|
||||
if (!params.has('line2')) url.searchParams.set('line2', 'nonlatin')
|
||||
if (!params.has('lang')) url.searchParams.set('lang', 'en')
|
||||
window.history.replaceState({}, '', url)
|
||||
}
|
||||
|
||||
syncInputsFromParams()
|
||||
applyConfiguration()
|
||||
})
|
||||
|
||||
// Listen for URL changes (e.g., browser back/forward)
|
||||
window.addEventListener('popstate', () => {
|
||||
syncInputsFromParams()
|
||||
applyConfiguration()
|
||||
})
|
||||
Reference in New Issue
Block a user