up emoji on demo map
This commit is contained in:
parent
65956ff6be
commit
dea71fc6b3
7 changed files with 565 additions and 35 deletions
|
@ -614,15 +614,17 @@ def submit_event(event):
|
|||
event: A GeoJSON Feature representing the event.
|
||||
|
||||
Returns:
|
||||
bool: True if the event was successfully submitted, False otherwise.
|
||||
tuple: A tuple containing (success: bool, event_id: str or None).
|
||||
success is True if the event was successfully submitted, False otherwise.
|
||||
event_id is the OEDB event ID if available, None otherwise.
|
||||
"""
|
||||
try:
|
||||
# Extract event properties for logging
|
||||
properties = event['properties']
|
||||
|
||||
|
||||
# API endpoint for OpenEventDatabase
|
||||
api_url = "https://api.openeventdatabase.org/event"
|
||||
|
||||
|
||||
# Make the API request
|
||||
logger.info(f"Submitting event '{properties.get('label')}' to API")
|
||||
response = requests.post(
|
||||
|
@ -630,32 +632,38 @@ def submit_event(event):
|
|||
headers={"Content-Type": "application/json"},
|
||||
data=json.dumps(event)
|
||||
)
|
||||
|
||||
|
||||
# Check if the request was successful
|
||||
if response.status_code == 200 or response.status_code == 201:
|
||||
# Parse the response to get the event ID
|
||||
response_data = response.json()
|
||||
event_id = response_data.get('id')
|
||||
|
||||
|
||||
if event_id:
|
||||
logger.success(f"Event created with ID: {event_id}")
|
||||
logger.info(f" https://api.openeventdatabase.org/event/{event_id}")
|
||||
return True
|
||||
return (True, event_id)
|
||||
else:
|
||||
logger.warning(f"Event created but no ID returned in response")
|
||||
return True
|
||||
return (True, None)
|
||||
elif response.status_code == 409:
|
||||
# 409 Conflict - L'événement existe déjà, considéré comme un succès
|
||||
logger.success(f"Event already exists in database: {properties.get('label')} (HTTP 409)")
|
||||
return True
|
||||
# Essayer d'extraire l'ID de l'événement existant depuis la réponse
|
||||
try:
|
||||
response_data = response.json()
|
||||
existing_event_id = response_data.get('id')
|
||||
return (True, existing_event_id)
|
||||
except:
|
||||
return (True, None)
|
||||
else:
|
||||
logger.warning(f"Failed to create event: {properties.get('label')}. Status code: {response.status_code}")
|
||||
logger.warning(f"Response: {response.text}")
|
||||
return False
|
||||
return (False, None)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error submitting event: {e}")
|
||||
return False
|
||||
return (False, None)
|
||||
|
||||
def main(max_events=1, offset=0):
|
||||
"""
|
||||
|
@ -695,7 +703,8 @@ def main(max_events=1, offset=0):
|
|||
# Vérifier si l'événement existe dans le cache et a le statut 'success'
|
||||
if link in event_cache and event_cache[link].get('status') == 'success':
|
||||
success_events.append(link)
|
||||
logger.info(f"Événement déjà traité avec succès, ignoré : {link}")
|
||||
oedb_id = event_cache[link].get('oedb_event_id', 'ID non disponible')
|
||||
logger.info(f"Événement déjà traité avec succès (ID OEDB: {oedb_id}), ignoré : {link}")
|
||||
else:
|
||||
new_events.append(link)
|
||||
# Initialiser l'événement dans le cache s'il n'existe pas
|
||||
|
@ -709,7 +718,8 @@ def main(max_events=1, offset=0):
|
|||
# Log du statut actuel pour les événements déjà en cache
|
||||
current_status = event_cache[link].get('status', 'unknown')
|
||||
attempts = event_cache[link].get('attempts', 0)
|
||||
logger.info(f"Événement à retraiter (statut: {current_status}, tentatives: {attempts}) : {link}")
|
||||
oedb_id = event_cache[link].get('oedb_event_id', 'non disponible')
|
||||
logger.info(f"Événement à retraiter (statut: {current_status}, tentatives: {attempts}, ID OEDB: {oedb_id}) : {link}")
|
||||
|
||||
logger.info(f"Liens d'événements trouvés : {len(event_links)}")
|
||||
logger.info(f"Événements déjà traités avec succès : {len(success_events)}")
|
||||
|
@ -766,11 +776,17 @@ def main(max_events=1, offset=0):
|
|||
|
||||
if event:
|
||||
# Tenter de soumettre l'événement à l'API
|
||||
if submit_event(event):
|
||||
submit_success, oedb_event_id = submit_event(event)
|
||||
if submit_success:
|
||||
success_count += 1
|
||||
event_cache[event_link]['status'] = 'success'
|
||||
event_cache[event_link]['inserted_at'] = datetime.now().isoformat()
|
||||
logger.success(f"Événement inséré avec succès : {event_link}")
|
||||
# Sauvegarder l'ID de l'événement OEDB dans le cache
|
||||
if oedb_event_id:
|
||||
event_cache[event_link]['oedb_event_id'] = oedb_event_id
|
||||
logger.success(f"Événement inséré avec succès (ID OEDB: {oedb_event_id}) : {event_link}")
|
||||
else:
|
||||
logger.success(f"Événement inséré avec succès (ID OEDB non disponible) : {event_link}")
|
||||
else:
|
||||
event_cache[event_link]['status'] = 'failed'
|
||||
logger.warning(f"Échec de l'insertion de l'événement : {event_link}")
|
||||
|
|
|
@ -66,7 +66,7 @@ class DemoResource:
|
|||
template = self.jinja_env.get_template('edit.html')
|
||||
html = template.render(
|
||||
id=id,
|
||||
event_data=json.dumps(event_data)
|
||||
event_data=event_data
|
||||
)
|
||||
|
||||
# Set the response body and status
|
||||
|
@ -404,5 +404,64 @@ class DemoResource:
|
|||
resp.status = falcon.HTTP_500
|
||||
resp.text = f"Error: {str(e)}"
|
||||
|
||||
def on_get_property_stats(self, req, resp):
|
||||
"""
|
||||
Handle GET requests to the /demo/property-stats endpoint.
|
||||
Returns an HTML page with statistics about property occurrences in the last 200 events.
|
||||
|
||||
Args:
|
||||
req: The request object.
|
||||
resp: The response object.
|
||||
"""
|
||||
logger.info("Processing GET request to /demo/property-stats")
|
||||
|
||||
try:
|
||||
# Set content type to HTML
|
||||
resp.content_type = 'text/html'
|
||||
|
||||
# Fetch the last 200 events from the API
|
||||
try:
|
||||
response = requests.get('https://api.openeventdatabase.org/event?limit=200')
|
||||
if response.status_code == 200 and response.text:
|
||||
events_data = response.json()
|
||||
else:
|
||||
logger.error(f"Error fetching events: Status code {response.status_code}")
|
||||
events_data = {"features": []}
|
||||
except Exception as e:
|
||||
logger.error(f"Error fetching events: {e}")
|
||||
events_data = {"features": []}
|
||||
|
||||
# Count property occurrences
|
||||
property_counts = {}
|
||||
total_events = len(events_data.get('features', []))
|
||||
|
||||
for feature in events_data.get('features', []):
|
||||
properties = feature.get('properties', {})
|
||||
for prop_name in properties.keys():
|
||||
if prop_name in property_counts:
|
||||
property_counts[prop_name] += 1
|
||||
else:
|
||||
property_counts[prop_name] = 1
|
||||
|
||||
# Sort properties by occurrence count (descending order)
|
||||
sorted_properties = sorted(property_counts.items(), key=lambda x: x[1], reverse=True)
|
||||
|
||||
# Render the template
|
||||
template = self.jinja_env.get_template('property_stats.html')
|
||||
html = template.render(
|
||||
property_stats=sorted_properties,
|
||||
total_events=total_events,
|
||||
unique_properties=len(sorted_properties)
|
||||
)
|
||||
|
||||
# Set the response body and status
|
||||
resp.text = html
|
||||
resp.status = falcon.HTTP_200
|
||||
logger.success("Successfully processed GET request to /demo/property-stats")
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing GET request to /demo/property-stats: {e}")
|
||||
resp.status = falcon.HTTP_500
|
||||
resp.text = f"Error: {str(e)}"
|
||||
|
||||
# Create a global instance of DemoResource
|
||||
demo = DemoResource()
|
|
@ -297,6 +297,7 @@ class DemoMainResource:
|
|||
<p><a href="/demo/traffic" class="add-event-btn" style="display: block; text-align: center; margin-top: 15px; padding: 8px; background-color: #0078ff; color: white; border-radius: 4px; font-weight: bold;">+ Traffic event</a></p>
|
||||
<p><a href="/demo/add" class="add-event-btn" style="display: block; text-align: center; margin-top: 15px; padding: 8px; background-color: #0078ff; color: white; border-radius: 4px; font-weight: bold;">+ Any Event</a></p>
|
||||
<p><a href="/demo/live" class="live-event-btn" style="display: block; text-align: center; margin-top: 15px; padding: 8px; background-color: #0078ff; color: white; border-radius: 4px; font-weight: bold;"> Live</a></p>
|
||||
<p><a href="/demo/property-stats" class="stats-event-btn" style="display: block; text-align: center; margin-top: 15px; padding: 8px; background-color: #28a745; color: white; border-radius: 4px; font-weight: bold;">📊 Statistiques propriétés</a></p>
|
||||
|
||||
|
||||
|
||||
|
@ -304,7 +305,7 @@ class DemoMainResource:
|
|||
<br/>
|
||||
<br/>
|
||||
<!-- Filtres pour les événements -->
|
||||
<div class="event-filters" id="filters_panel" style="margin-top: 15px; padding: 10px; background-color: #f5f5f5; border-radius: 4px; display:none;">
|
||||
<div class="event-filters" id="filters_panel" style="margin-top: 15px; padding: 10px; background-color: #f5f5f5; border-radius: 4px;">
|
||||
<h3 id="filters_header" style="margin-top: 0; color: #0078ff; cursor:pointer;">Filtres</h3>
|
||||
<div style="margin-top: 10px;">
|
||||
<label style="display: block; margin-bottom: 5px;">Type d'événement:</label>
|
||||
|
@ -317,14 +318,14 @@ class DemoMainResource:
|
|||
</select>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:12px; display:flex; align-items:center; gap:8px;">
|
||||
<input type="checkbox" id="autoRefreshToggle" checked>
|
||||
<label for="autoRefreshToggle" style="margin:0;">Rafraîchissement automatique (30s)</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="event-filters" style="margin-top: 10px; padding: 10px; background-color: #fff; border: 1px solid #e5e7eb; border-radius: 4px;">
|
||||
<h3 style="margin-top: 0; color: #0078ff;">Histogramme des évènements</h3>
|
||||
<div style="margin-top:12px; display:flex; align-items:center; gap:8px;">
|
||||
<input type="checkbox" id="autoRefreshToggle" checked>
|
||||
<label for="autoRefreshToggle" style="margin:0;">Rafraîchissement automatique (30s)</label>
|
||||
</div>
|
||||
<canvas id="eventsHistogram" style="width:100%; height:220px;"></canvas>
|
||||
</div>
|
||||
|
||||
|
@ -364,6 +365,28 @@ class DemoMainResource:
|
|||
criteria: (name, description, what) => {
|
||||
return (what || '').toLowerCase().includes('bike');
|
||||
}
|
||||
},
|
||||
// Emoji casque de chantier pour les travaux
|
||||
construction: {
|
||||
emoji: '⛑️',
|
||||
criteria: (name, description, what) => {
|
||||
const text = (name + ' ' + description + ' ' + what).toLowerCase();
|
||||
return text.includes('travaux');
|
||||
}
|
||||
},
|
||||
// Emoji soleil pour les types contenant "daylight"
|
||||
daylight: {
|
||||
emoji: '☀️',
|
||||
criteria: (name, description, what) => {
|
||||
return (what || '').toLowerCase().includes('daylight');
|
||||
}
|
||||
},
|
||||
// Emoji carte pour les types contenant "community.osm"
|
||||
osm_community: {
|
||||
emoji: '🗺️',
|
||||
criteria: (name, description, what) => {
|
||||
return (what || '').toLowerCase().includes('community.osm');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<script src="https://unpkg.com/maplibre-gl@3.0.0/dist/maplibre-gl.js"></script>
|
||||
<link href="https://unpkg.com/maplibre-gl@3.0.0/dist/maplibre-gl.css" rel="stylesheet" />
|
||||
<script src="https://unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.js"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.css" type="text/css" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
|
||||
|
@ -83,19 +85,15 @@
|
|||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
// Event data from API - sécuriser le parsing
|
||||
let eventData = null;
|
||||
// Event data from API - corriger le double-encodage
|
||||
window.eventData = null;
|
||||
try {
|
||||
const rawData = '{{ event_data|tojson|safe }}';
|
||||
console.log('🔍 Données brutes reçues:', rawData);
|
||||
eventData = JSON.parse(rawData);
|
||||
console.log('✅ Données événement parsées:', eventData);
|
||||
|
||||
// Rendre les données disponibles globalement
|
||||
window.eventData = eventData;
|
||||
// Utiliser directement les données JSON sans double-encodage
|
||||
window.eventData = {{ event_data|safe }};
|
||||
console.log('✅ Données événement chargées:', window.eventData);
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur de parsing des données événement:', error);
|
||||
console.error('Données brutes:', '{{ event_data|tojson|safe }}');
|
||||
console.error('❌ Erreur de chargement des données événement:', error);
|
||||
console.error('Données brutes JSON:', '{{ event_data|safe }}');
|
||||
|
||||
// Afficher une erreur à l'utilisateur
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
@ -104,7 +102,7 @@
|
|||
resultElement.innerHTML = `
|
||||
<div style="color: #dc3545; padding: 15px; background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 4px;">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<strong>Erreur de chargement:</strong> Impossible de parser les données de l'événement.
|
||||
<strong>Erreur de chargement:</strong> Impossible de charger les données de l'événement.
|
||||
<br><small>Erreur technique: ${error.message}</small>
|
||||
</div>
|
||||
`;
|
||||
|
|
177
oedb/resources/demo/templates/property_stats.html
Normal file
177
oedb/resources/demo/templates/property_stats.html
Normal file
|
@ -0,0 +1,177 @@
|
|||
{% extends "layout.html" %}
|
||||
|
||||
{% block title %}Statistiques des propriétés - OpenEventDatabase{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<style>
|
||||
.stats-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.stats-summary {
|
||||
background-color: #f8f9fa;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 30px;
|
||||
border-left: 4px solid #0078ff;
|
||||
}
|
||||
|
||||
.stats-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background-color: white;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stats-table th,
|
||||
.stats-table td {
|
||||
padding: 12px 15px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.stats-table th {
|
||||
background-color: #0078ff;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.stats-table tbody tr:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.stats-table tbody tr:nth-child(even) {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.percentage {
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.property-name {
|
||||
font-family: monospace;
|
||||
background-color: #f1f3f4;
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.occurrence-count {
|
||||
font-weight: bold;
|
||||
color: #0078ff;
|
||||
}
|
||||
|
||||
.nav-breadcrumb {
|
||||
margin-bottom: 20px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.nav-breadcrumb a {
|
||||
color: #0078ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-breadcrumb a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.refresh-info {
|
||||
font-size: 0.9em;
|
||||
color: #666;
|
||||
margin-bottom: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}Statistiques des propriétés des événements{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="stats-container">
|
||||
<div class="nav-breadcrumb">
|
||||
<a href="/">Accueil</a> ›
|
||||
<a href="/demo">Demo</a> ›
|
||||
Statistiques des propriétés
|
||||
</div>
|
||||
|
||||
<div class="refresh-info">
|
||||
Basé sur les {{ total_events }} derniers événements de la base de données
|
||||
</div>
|
||||
|
||||
<div class="stats-summary">
|
||||
<h2>Résumé</h2>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px;">
|
||||
<div>
|
||||
<strong>Nombre total d'événements analysés:</strong><br>
|
||||
<span style="font-size: 1.5em; color: #0078ff;">{{ total_events }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Propriétés uniques trouvées:</strong><br>
|
||||
<span style="font-size: 1.5em; color: #28a745;">{{ unique_properties }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Propriété la plus commune:</strong><br>
|
||||
{% if property_stats %}
|
||||
<span class="property-name">{{ property_stats[0][0] }}</span><br>
|
||||
<small>({{ property_stats[0][1] }} occurrences)</small>
|
||||
{% else %}
|
||||
<span>Aucune donnée</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if property_stats %}
|
||||
<table class="stats-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 50px;">Rang</th>
|
||||
<th>Nom de la propriété</th>
|
||||
<th style="width: 120px;">Occurrences</th>
|
||||
<th style="width: 100px;">Pourcentage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for property_name, count in property_stats %}
|
||||
<tr>
|
||||
<td>{{ loop.index }}</td>
|
||||
<td><span class="property-name">{{ property_name }}</span></td>
|
||||
<td class="occurrence-count">{{ count }}</td>
|
||||
<td class="percentage">{{ "%.1f"|format((count / total_events * 100)) }}%</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<div style="text-align: center; padding: 40px; color: #666;">
|
||||
<h3>Aucune donnée disponible</h3>
|
||||
<p>Impossible de récupérer les statistiques des propriétés pour le moment.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div style="margin-top: 30px; text-align: center;">
|
||||
<a href="/demo" style="display: inline-block; padding: 10px 20px; background-color: #0078ff; color: white; text-decoration: none; border-radius: 5px;">
|
||||
← Retour à la démo
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
// Auto-refresh every 5 minutes
|
||||
setTimeout(function() {
|
||||
window.location.reload();
|
||||
}, 5 * 60 * 1000);
|
||||
|
||||
// Add click handler to property names for future enhancement
|
||||
document.querySelectorAll('.property-name').forEach(function(element) {
|
||||
element.style.cursor = 'pointer';
|
||||
element.title = 'Propriété: ' + element.textContent;
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
232
static/edit.js
Normal file
232
static/edit.js
Normal file
|
@ -0,0 +1,232 @@
|
|||
// Variables globales
|
||||
let map;
|
||||
let draw;
|
||||
let currentMarker = null;
|
||||
|
||||
// Initialisation quand le DOM est chargé
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initializeForm();
|
||||
initializeMap();
|
||||
setupEventHandlers();
|
||||
});
|
||||
|
||||
function initializeForm() {
|
||||
if (!window.eventData) {
|
||||
console.error('Aucune donnée d\'événement disponible');
|
||||
return;
|
||||
}
|
||||
|
||||
const properties = window.eventData.properties || {};
|
||||
const geometry = window.eventData.geometry || {};
|
||||
|
||||
// Remplir les champs du formulaire
|
||||
document.getElementById('label').value = properties.label || '';
|
||||
document.getElementById('type').value = properties.type || 'scheduled';
|
||||
document.getElementById('what').value = properties.what || '';
|
||||
document.getElementById('what_series').value = properties['what:series'] || '';
|
||||
document.getElementById('where').value = properties.where || '';
|
||||
|
||||
// Convertir les dates pour les inputs datetime-local
|
||||
if (properties.start) {
|
||||
const startDate = new Date(properties.start);
|
||||
document.getElementById('start').value = formatDateTimeLocal(startDate);
|
||||
}
|
||||
if (properties.stop) {
|
||||
const stopDate = new Date(properties.stop);
|
||||
document.getElementById('stop').value = formatDateTimeLocal(stopDate);
|
||||
}
|
||||
|
||||
console.log('✅ Formulaire initialisé avec les données de l\'événement');
|
||||
}
|
||||
|
||||
function formatDateTimeLocal(date) {
|
||||
// Convertir une date en format compatible avec datetime-local
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
|
||||
return `${year}-${month}-${day}T${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
function initializeMap() {
|
||||
if (!window.maplibregl) {
|
||||
console.error('MapLibre GL JS non disponible');
|
||||
return;
|
||||
}
|
||||
|
||||
const geometry = window.eventData?.geometry || {};
|
||||
const coordinates = geometry.coordinates || [2.3522, 48.8566]; // Paris par défaut
|
||||
|
||||
// Initialiser la carte
|
||||
map = new maplibregl.Map({
|
||||
container: 'map',
|
||||
style: 'https://tiles.openfreemap.org/styles/liberty',
|
||||
center: coordinates,
|
||||
zoom: coordinates[0] === 0 && coordinates[1] === 0 ? 2 : 12
|
||||
});
|
||||
|
||||
// Ajouter les contrôles
|
||||
map.addControl(new maplibregl.NavigationControl());
|
||||
map.addControl(new maplibregl.GeolocateControl({
|
||||
positionOptions: {
|
||||
enableHighAccuracy: true
|
||||
}
|
||||
}));
|
||||
|
||||
// Ajouter un marqueur pour la position actuelle
|
||||
if (coordinates[0] !== 0 || coordinates[1] !== 0) {
|
||||
currentMarker = new maplibregl.Marker()
|
||||
.setLngLat(coordinates)
|
||||
.addTo(map);
|
||||
}
|
||||
|
||||
// Gérer les clics sur la carte pour changer la position
|
||||
map.on('click', function(e) {
|
||||
const coords = [e.lngLat.lng, e.lngLat.lat];
|
||||
|
||||
// Supprimer l'ancien marqueur
|
||||
if (currentMarker) {
|
||||
currentMarker.remove();
|
||||
}
|
||||
|
||||
// Ajouter un nouveau marqueur
|
||||
currentMarker = new maplibregl.Marker()
|
||||
.setLngLat(coords)
|
||||
.addTo(map);
|
||||
|
||||
console.log('📍 Position mise à jour:', coords);
|
||||
});
|
||||
|
||||
console.log('✅ Carte initialisée');
|
||||
}
|
||||
|
||||
function setupEventHandlers() {
|
||||
// Gestionnaire de soumission du formulaire
|
||||
const form = document.getElementById('eventForm');
|
||||
if (form) {
|
||||
form.addEventListener('submit', handleFormSubmit);
|
||||
}
|
||||
|
||||
// Gestionnaire du bouton de suppression
|
||||
const deleteButton = document.getElementById('deleteButton');
|
||||
if (deleteButton) {
|
||||
deleteButton.addEventListener('click', handleDelete);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleFormSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const resultElement = document.getElementById('result');
|
||||
resultElement.innerHTML = '<div style="color: #0078ff;">⏳ Mise à jour en cours...</div>';
|
||||
resultElement.style.display = 'block';
|
||||
|
||||
try {
|
||||
// Récupérer les données du formulaire
|
||||
const formData = new FormData(e.target);
|
||||
const eventId = document.getElementById('eventId').value;
|
||||
|
||||
// Construire l'objet événement
|
||||
const updatedEvent = {
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: currentMarker ?
|
||||
[currentMarker.getLngLat().lng, currentMarker.getLngLat().lat] :
|
||||
[0, 0]
|
||||
},
|
||||
properties: {
|
||||
...window.eventData.properties,
|
||||
label: formData.get('label'),
|
||||
type: formData.get('type'),
|
||||
what: formData.get('what'),
|
||||
'what:series': formData.get('what_series') || undefined,
|
||||
where: formData.get('where') || undefined,
|
||||
start: formData.get('start') ? new Date(formData.get('start')).toISOString() : undefined,
|
||||
stop: formData.get('stop') ? new Date(formData.get('stop')).toISOString() : undefined
|
||||
}
|
||||
};
|
||||
|
||||
// Nettoyer les propriétés undefined
|
||||
Object.keys(updatedEvent.properties).forEach(key => {
|
||||
if (updatedEvent.properties[key] === undefined) {
|
||||
delete updatedEvent.properties[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Envoyer la mise à jour
|
||||
const response = await fetch(`https://api.openeventdatabase.org/event/${eventId}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(updatedEvent)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
resultElement.innerHTML = `
|
||||
<div style="color: #28a745; padding: 15px; background: #d4edda; border: 1px solid #c3e6cb; border-radius: 4px;">
|
||||
<i class="fas fa-check"></i>
|
||||
<strong>Succès:</strong> L'événement a été mis à jour avec succès.
|
||||
<br><a href="/demo/by_id/${eventId}">Voir l'événement</a>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
const errorData = await response.text();
|
||||
throw new Error(`HTTP ${response.status}: ${errorData}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la mise à jour:', error);
|
||||
resultElement.innerHTML = `
|
||||
<div style="color: #dc3545; padding: 15px; background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 4px;">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<strong>Erreur:</strong> ${error.message}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDelete() {
|
||||
const eventId = document.getElementById('eventId').value;
|
||||
const eventLabel = window.eventData?.properties?.label || 'cet événement';
|
||||
|
||||
if (!confirm(`Êtes-vous sûr de vouloir supprimer "${eventLabel}" ? Cette action est irréversible.`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resultElement = document.getElementById('result');
|
||||
resultElement.innerHTML = '<div style="color: #dc3545;">⏳ Suppression en cours...</div>';
|
||||
resultElement.style.display = 'block';
|
||||
|
||||
try {
|
||||
const response = await fetch(`https://api.openeventdatabase.org/event/${eventId}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
resultElement.innerHTML = `
|
||||
<div style="color: #28a745; padding: 15px; background: #d4edda; border: 1px solid #c3e6cb; border-radius: 4px;">
|
||||
<i class="fas fa-check"></i>
|
||||
<strong>Succès:</strong> L'événement a été supprimé avec succès.
|
||||
<br><a href="/demo">Retour à la démo</a>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Désactiver le formulaire après suppression
|
||||
document.getElementById('eventForm').style.display = 'none';
|
||||
} else {
|
||||
const errorData = await response.text();
|
||||
throw new Error(`HTTP ${response.status}: ${errorData}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la suppression:', error);
|
||||
resultElement.innerHTML = `
|
||||
<div style="color: #dc3545; padding: 15px; background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 4px;">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<strong>Erreur:</strong> ${error.message}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
|
@ -30,6 +30,28 @@ window.EMOJI_CRITERIA = {
|
|||
criteria: (name, description, what) => {
|
||||
return (what || '').toLowerCase().includes('bike');
|
||||
}
|
||||
},
|
||||
// Emoji casque de chantier pour les travaux
|
||||
construction: {
|
||||
emoji: '🏗️️',
|
||||
criteria: (name, description, what) => {
|
||||
const text = (name + ' ' + description + ' ' + what).toLowerCase();
|
||||
return text.includes('travaux');
|
||||
}
|
||||
},
|
||||
// Emoji soleil pour les types contenant "daylight"
|
||||
daylight: {
|
||||
emoji: '☀️',
|
||||
criteria: (name, description, what) => {
|
||||
return (what || '').toLowerCase().includes('daylight');
|
||||
}
|
||||
},
|
||||
// Emoji carte pour les types contenant "community.osm"
|
||||
osm_community: {
|
||||
emoji: '🗺️',
|
||||
criteria: (name, description, what) => {
|
||||
return (what || '').toLowerCase().includes('community.osm');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -39,6 +61,11 @@ function getEventEmoji(properties) {
|
|||
const description = properties.description || '';
|
||||
const what = properties.what || '';
|
||||
|
||||
|
||||
if (what.includes('accident')) return 'PAF!';
|
||||
if (what.includes('incident')) return '⚠️';
|
||||
if (what.includes('traffic')) return '🚗';
|
||||
|
||||
// Parcourir les critères dans l'ordre de priorité
|
||||
for (const [key, config] of Object.entries(window.EMOJI_CRITERIA)) {
|
||||
if (config.criteria(name, description, what)) {
|
||||
|
@ -47,10 +74,8 @@ function getEventEmoji(properties) {
|
|||
}
|
||||
|
||||
// Emoji par défaut selon le type d'événement
|
||||
if (what.includes('traffic')) return '🚗';
|
||||
if (what.includes('weather')) return '🌤️';
|
||||
if (what.includes('gathering')) return '👥';
|
||||
if (what.includes('incident')) return '⚠️';
|
||||
|
||||
return ' '; // Emoji par défaut
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue