template html & css
This commit is contained in:
221
svg.html
Normal file
221
svg.html
Normal file
@@ -0,0 +1,221 @@
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<title>SVG from LINESTRINGs</title>
|
||||
<style>
|
||||
body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial; padding: 12px; }
|
||||
#mapWrap { width: 100%; height: 80vh; border: 1px solid #ddd; background: #f8f9fb; }
|
||||
svg { width: 100%; height: 100%; display: block; }
|
||||
.line-sens-1 { stroke: #1f77b4; stroke-width: 3; fill: none; stroke-linecap: round; stroke-linejoin: round; }
|
||||
.line-sens-2 { stroke: #ff7f0e; stroke-width: 3; fill: none; stroke-linecap: round; stroke-linejoin: round; }
|
||||
.hovered { stroke-width: 5; filter: drop-shadow(0 1px 2px rgba(0,0,0,0.2)); }
|
||||
.vertex { r: 2.2; fill: rgba(0,0,0,0.5); pointer-events: none; }
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
background: rgba(0,0,0,0.75);
|
||||
color: white;
|
||||
padding: 4px 6px;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
transform: translate(8px, 8px);
|
||||
display: none;
|
||||
z-index: 10;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Visualisation SVG des LINESTRING</h1>
|
||||
<div id="mapWrap"></div>
|
||||
<div id="tooltip" class="tooltip"></div>
|
||||
|
||||
<script>
|
||||
/* --- données fournies par l'utilisateur --- */
|
||||
const data = [
|
||||
{
|
||||
"fk_route_uuid": "8d004027-3279-4eb8-8de4-7f40ef542111",
|
||||
"sens": 1,
|
||||
"st_astext": "LINESTRING(240786.25000013656 79036.06584277935,240789.6701001395 79034.92654277757,240795.80500013664 79032.88284277637,240816.54300013464 79021.48284277692,240848.48400013373 79003.72684277594,240862.6130001347 78996.93084277771,240869.41630013572 78995.22494277824,240875.52820013772 78995.85994277615,240878.9446763978 78999.20556702279,240883.3350001378 79003.50484277774,240885.7940001368 79005.9478427777,240890.99600013572 79019.1748427758,240893.19700013375 79022.76474277768,240895.1890001397 79026.01384277735)"
|
||||
},
|
||||
{
|
||||
"fk_route_uuid": "8d004027-3279-4eb8-8de4-7f40ef542111",
|
||||
"sens": 2,
|
||||
"st_astext": "LINESTRING(240876.93050013762 79027.10734277777,240880.75500013976 79023.49284277484,240883.52900013572 79017.29684277717,240884.25950013375 79012.2112427773,240884.10070013476 79007.92494277749,240883.3350001378 79003.50484277774,240878.94468326174 78999.20557374507,240875.52820013772 78995.85994277615,240869.41630013572 78995.22494277824,240862.6130001347 78996.93084277771,240848.48400013373 79003.72684277594,240816.54300013464 79021.48284277692,240795.80500013664 79032.88284277637,240789.6701001395 79034.92654277757)"
|
||||
},
|
||||
{
|
||||
"fk_route_uuid": "e8af74b2-62bb-4ed9-841a-d72f01a471bf",
|
||||
"sens": 1,
|
||||
"st_astext": "LINESTRING(240788.70500013442 79077.95804277528,240726.47800013627 79107.39084277768,240719.76930013526 79110.84164277464,240718.30300013922 79111.59584277496,240713.81800014016 79116.28184277564,240711.32100013818 79123.2998427758,240711.94600013812 79131.3228427777,240710.94900013716 79134.09984277561,240718.46600013407 79143.87984277774,240728.20900013513 79148.93084277678,240737.10500013322 79148.99684277643,240749.14700013614 79143.51984277926,240772.7741277222 79129.7665901715,240784.77000013928 79122.7838427769,240811.7210001374 79105.72584277857)"
|
||||
},
|
||||
{
|
||||
"fk_route_uuid": "e8af74b2-62bb-4ed9-841a-d72f01a471bf",
|
||||
"sens": 2,
|
||||
"st_astext": "LINESTRING(240811.7210001374 79105.72584277857,240784.77000013928 79122.7838427769,240772.77412333922 79129.76659272239,240749.14700013614 79143.51984277926,240737.10500013322 79148.99684277643,240728.20900013513 79148.93084277678,240718.46600013407 79143.87984277774,240710.94900013716 79134.09984277561,240708.14400013717 79132.68284277525,240702.59600013716 79122.08384277672,240697.10920013717 79117.31394277606)"
|
||||
}
|
||||
];
|
||||
|
||||
/* --- utilitaires --- */
|
||||
|
||||
/** Parse un WKT LINESTRING et renvoie un array de [x,y] */
|
||||
function parseLinestring(wkt) {
|
||||
// wkt attendu: "LINESTRING(x1 y1,x2 y2,...)"
|
||||
const inner = wkt.replace(/^\s*LINESTRING\s*\(|\)\s*$/gi, '');
|
||||
const parts = inner.split(',');
|
||||
return parts.map(p => {
|
||||
const [xs, ys] = p.trim().split(/\s+/);
|
||||
return [parseFloat(xs), parseFloat(ys)];
|
||||
});
|
||||
}
|
||||
|
||||
/** calcule bbox pour toutes les géométries */
|
||||
function computeBBox(allCoords) {
|
||||
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
||||
allCoords.forEach(coords => {
|
||||
coords.forEach(([x,y]) => {
|
||||
if (x < minX) minX = x;
|
||||
if (y < minY) minY = y;
|
||||
if (x > maxX) maxX = x;
|
||||
if (y > maxY) maxY = y;
|
||||
});
|
||||
});
|
||||
return { minX, minY, maxX, maxY };
|
||||
}
|
||||
|
||||
/* --- construction du SVG --- */
|
||||
(function render() {
|
||||
const parsed = data.map(item => {
|
||||
return {
|
||||
fk_route_uuid: item.fk_route_uuid,
|
||||
sens: item.sens,
|
||||
coords: parseLinestring(item.st_astext)
|
||||
};
|
||||
});
|
||||
|
||||
const allCoords = parsed.map(p => p.coords);
|
||||
const bbox = computeBBox(allCoords);
|
||||
|
||||
// padding en unités réelles (10% de la plus grande dimension)
|
||||
const padFactor = 0.08;
|
||||
const widthReal = bbox.maxX - bbox.minX || 1;
|
||||
const heightReal = bbox.maxY - bbox.minY || 1;
|
||||
const padX = widthReal * padFactor;
|
||||
const padY = heightReal * padFactor;
|
||||
const viewMinX = bbox.minX - padX;
|
||||
const viewMinY = bbox.minY - padY;
|
||||
const viewMaxX = bbox.maxX + padX;
|
||||
const viewMaxY = bbox.maxY + padY;
|
||||
const viewWidth = viewMaxX - viewMinX;
|
||||
const viewHeight = viewMaxY - viewMinY;
|
||||
|
||||
// on veut que Y augmente vers le bas en SVG => on va inverser Y en transformant
|
||||
// la vue en utilisant viewBox et en appliquant une transformation sur un <g>
|
||||
const mapWrap = document.getElementById('mapWrap');
|
||||
const svgNS = 'http://www.w3.org/2000/svg';
|
||||
|
||||
const svg = document.createElementNS(svgNS, 'svg');
|
||||
// viewBox: minX minY width height — on met minX, minY = viewMinX, viewMinY, mais
|
||||
// pour inverser Y on va créer un group <g transform="scale(1,-1) translate(0, -H)">
|
||||
svg.setAttribute('viewBox', `${viewMinX} ${viewMinY} ${viewWidth} ${viewHeight}`);
|
||||
svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
||||
|
||||
// container group : on doit inverser Y axis because coordinates are in projected system
|
||||
// Approach: apply transform to flip Y around center. Simpler: we apply transform to map coordinates to SVG coords when building path.
|
||||
// Here we'll use transform on group: scale(1,-1) and translate(0, - (viewMinY + viewMaxY) )
|
||||
const g = document.createElementNS(svgNS, 'g');
|
||||
// translate to flip around the middle of viewbox
|
||||
const translateY = -(viewMinY + viewMaxY);
|
||||
g.setAttribute('transform', `scale(1,-1) translate(0, ${translateY})`);
|
||||
|
||||
// for each parsed geometry create a path
|
||||
parsed.forEach((item, idx) => {
|
||||
const d = item.coords.map((pt, i) => {
|
||||
const [x, y] = pt;
|
||||
return (i === 0 ? 'M ' : 'L ') + x + ' ' + y;
|
||||
}).join(' ');
|
||||
|
||||
const path = document.createElementNS(svgNS, 'path');
|
||||
const cls = item.sens === 1 ? 'line-sens-1' : 'line-sens-2';
|
||||
path.setAttribute('d', d);
|
||||
path.setAttribute('class', cls);
|
||||
path.setAttribute('data-index', idx);
|
||||
path.setAttribute('data-uuid', item.fk_route_uuid);
|
||||
path.setAttribute('data-sens', item.sens);
|
||||
// cursor crosshair on hover:
|
||||
path.style.cursor = 'crosshair';
|
||||
|
||||
// events
|
||||
path.addEventListener('mouseenter', (e) => {
|
||||
path.classList.add('hovered');
|
||||
showTooltip(e, `route: ${item.fk_route_uuid}<br>sens: ${item.sens}`);
|
||||
});
|
||||
path.addEventListener('mousemove', (e) => {
|
||||
moveTooltip(e);
|
||||
});
|
||||
path.addEventListener('mouseleave', () => {
|
||||
path.classList.remove('hovered');
|
||||
hideTooltip();
|
||||
});
|
||||
path.addEventListener('dblclick', (e) => {
|
||||
e.stopPropagation();
|
||||
// action au double-clic : highlight et log — personnalise ici
|
||||
console.log('Double-clic sur', {
|
||||
uuid: item.fk_route_uuid,
|
||||
sens: item.sens,
|
||||
index: idx
|
||||
});
|
||||
// effet visuel bref
|
||||
path.classList.add('hovered');
|
||||
setTimeout(() => path.classList.remove('hovered'), 700);
|
||||
// si tu veux centrer / zoomer sur la géométrie, tu peux ajouter ici
|
||||
});
|
||||
|
||||
g.appendChild(path);
|
||||
|
||||
// optionally draw small vertices (disable pointer events to avoid fuss)
|
||||
item.coords.forEach(([x,y]) => {
|
||||
const c = document.createElementNS(svgNS, 'circle');
|
||||
c.setAttribute('cx', x);
|
||||
c.setAttribute('cy', y);
|
||||
c.setAttribute('r', 0.0); // mettre petit rayon si tu veux les voir, sinon 0
|
||||
c.setAttribute('class', 'vertex');
|
||||
g.appendChild(c);
|
||||
});
|
||||
});
|
||||
|
||||
svg.appendChild(g);
|
||||
mapWrap.appendChild(svg);
|
||||
|
||||
|
||||
/* Tooltip utilities */
|
||||
const tooltip = document.getElementById('tooltip');
|
||||
function showTooltip(evt, html) {
|
||||
tooltip.innerHTML = html;
|
||||
tooltip.style.display = 'block';
|
||||
moveTooltip(evt);
|
||||
}
|
||||
function moveTooltip(evt) {
|
||||
// placer le tooltip à côté du pointeur (page coords)
|
||||
const x = evt.clientX;
|
||||
const y = evt.clientY;
|
||||
tooltip.style.left = (x + 10) + 'px';
|
||||
tooltip.style.top = (y + 10) + 'px';
|
||||
}
|
||||
function hideTooltip() {
|
||||
tooltip.style.display = 'none';
|
||||
}
|
||||
|
||||
// clic global (ex: décocher highlight / action)
|
||||
svg.addEventListener('click', () => {
|
||||
// exemple : retirer highlight de toutes les lignes
|
||||
svg.querySelectorAll('path.hovered').forEach(p => p.classList.remove('hovered'));
|
||||
});
|
||||
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user