| const canvas = document.getElementById('mapCanvas');
|
| const ctx = canvas.getContext('2d');
|
|
|
| const img = document.querySelector('.map-container img');
|
| canvas.width = img.clientWidth;
|
| canvas.height = img.clientHeight;
|
|
|
| const mapElement = document.getElementById('vietnam-map');
|
|
|
| let areas = [];
|
| let locationsData = [];
|
|
|
| fetch('provinces.json')
|
| .then(response => {
|
| if (!response.ok) {
|
| throw new Error('Network response was not ok');
|
| }
|
| return response.json();
|
| })
|
| .then(data => {
|
| locationsData = data;
|
| areas = locationsData.map(location => {
|
| if (!location.coords) {
|
| console.error('Missing coords for location:', location);
|
| return { coords: [], shape: 'poly' };
|
| }
|
| const coords = location.coords.split(',').map(Number);
|
| const shape = 'poly';
|
| return { coords, shape };
|
| });
|
| createMapAreas();
|
| })
|
| .catch(error => {
|
| console.error('Error loading the JSON file:', error);
|
| });
|
|
|
| function updateCanvasSize() {
|
|
|
| canvas.width = img.offsetWidth;
|
| canvas.height = img.offsetHeight;
|
| canvas.style.width = `${img.offsetWidth}px`;
|
| canvas.style.height = `${img.offsetHeight}px`;
|
| }
|
|
|
|
|
| window.addEventListener('resize', () => {
|
| updateCanvasSize();
|
| createMapAreas();
|
| });
|
|
|
|
|
| img.addEventListener('load', () => {
|
| updateCanvasSize();
|
| createMapAreas();
|
| });
|
|
|
|
|
| function createMapAreas() {
|
| mapElement.innerHTML = '';
|
| const scaleX = img.offsetWidth / img.naturalWidth;
|
| const scaleY = img.offsetHeight / img.naturalHeight;
|
|
|
| areas.forEach((area) => {
|
| const scaledCoords = area.coords.map((val, index) => {
|
| return index % 2 === 0
|
| ? Math.round(val * scaleX)
|
| : Math.round(val * scaleY);
|
| });
|
|
|
| const areaElement = document.createElement('area');
|
| areaElement.setAttribute('shape', area.shape);
|
| areaElement.setAttribute('coords', scaledCoords.join(','));
|
| areaElement.setAttribute('href', 'javascript:void(0)');
|
| mapElement.appendChild(areaElement);
|
| });
|
|
|
| setupEventListeners();
|
| }
|
|
|
| const drawOutline = (coords) => {
|
| ctx.clearRect(0, 0, canvas.width, canvas.height);
|
| ctx.beginPath();
|
| const scaleX = canvas.width / img.naturalWidth;
|
| const scaleY = canvas.height / img.naturalHeight;
|
|
|
| ctx.moveTo(coords[0] * scaleX, coords[1] * scaleY);
|
| for (let i = 2; i < coords.length; i += 2) {
|
| ctx.lineTo(coords[i] * scaleX, coords[i + 1] * scaleY);
|
| }
|
|
|
| ctx.closePath();
|
|
|
|
|
| ctx.fillStyle = 'rgba(255, 165, 0, 0.3)';
|
| ctx.fill();
|
|
|
|
|
| ctx.strokeStyle = 'red';
|
| ctx.lineWidth = 2;
|
| ctx.stroke();
|
| };
|
|
|
| const clearOutline = () => {
|
| ctx.clearRect(0, 0, canvas.width, canvas.height);
|
| };
|
|
|
|
|
| const tooltip = document.querySelector('.tooltip-box');
|
|
|
| const showTooltip = (data, event) => {
|
| tooltip.style.display = 'block';
|
|
|
| let nameText = '';
|
| let areaText = '';
|
| let areaTotal = 0;
|
| let popText = '';
|
| let popTotal = 0;
|
|
|
| if (typeof data.name === 'object' && Object.keys(data.name).length > 1) {
|
|
|
| nameText = Object.values(data.name).join(' + ');
|
|
|
|
|
| for (const key in data.naturalArea) {
|
| const value = parseFloat(data.naturalArea[key].replace('.', '').replace(',', '.'));
|
| areaText += `<p>${data.name[key]}: ${data.naturalArea[key]}</p>`;
|
| areaTotal += value;
|
| }
|
|
|
|
|
| for (const key in data.population) {
|
| const value = parseFloat(data.population[key].replace('.', '').replace(',', '.'));
|
| popText += `<p>${data.name[key]}: ${data.population[key]}</p>`;
|
| popTotal += value;
|
| }
|
|
|
| tooltip.innerHTML = `
|
| <h2>${nameText}</h2>
|
| <hr>
|
| <h3>DIỆN TÍCH TỰ NHIÊN (KM²)</h3>
|
| ${areaText}
|
| <p><strong>Tổng diện tích:</strong> ${areaTotal.toLocaleString('vi-VN')}</p>
|
| <hr>
|
| <h3>QUY MÔ DÂN SỐ (NGHÌN NGƯỜI)</h3>
|
| ${popText}
|
| <p><strong>Tổng dân số:</strong> ${popTotal.toLocaleString('vi-VN')}</p>
|
| <hr>
|
| <p>Tên tỉnh, thành sau sáp nhập: <span class="highlight">${data.mergedName}</span></p>
|
| <p>Trung tâm chính trị - hành chính: <span class="highlight">${data.center}</span></p>
|
| `;
|
| } else {
|
|
|
| tooltip.innerHTML = `
|
| <h2>${data.name}</h2>
|
| <hr>
|
| <h3>DIỆN TÍCH TỰ NHIÊN (KM²)</h3>
|
| <p>${data.name}: ${data.naturalArea}</p>
|
| <p><strong>Tổng diện tích:</strong> ${data.naturalArea}</p>
|
| <hr>
|
| <h3>QUY MÔ DÂN SỐ (NGHÌN NGƯỜI)</h3>
|
| <p>${data.name}: ${data.population}</p>
|
| <p><strong>Tổng dân số:</strong> ${data.population}</p>
|
| <hr>
|
| <!-- <p>Tên tỉnh, thành sau sáp nhập: <span class="highlight">${data.mergedName}</span></p>
|
| <p>Trung tâm chính trị - hành chính: <span class="highlight">${data.center}</span></p>
|
| -->
|
| `;
|
| }
|
|
|
|
|
| tooltip.style.visibility = 'hidden';
|
| tooltip.style.display = 'block';
|
|
|
| const tooltipWidth = tooltip.offsetWidth;
|
| const tooltipHeight = tooltip.offsetHeight;
|
|
|
|
|
| const viewportWidth = window.innerWidth;
|
| const viewportHeight = window.innerHeight;
|
| const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
| const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
|
|
|
|
|
| let left = event.pageX + 10;
|
| let top = event.pageY + 10;
|
|
|
|
|
| if (left + tooltipWidth > viewportWidth + scrollLeft) {
|
| left = event.pageX - tooltipWidth - 10;
|
| }
|
|
|
|
|
| if (top + tooltipHeight > viewportHeight + scrollTop) {
|
| top = event.pageY - tooltipHeight - 10;
|
|
|
|
|
| if (top < scrollTop) {
|
|
|
| top = scrollTop + (viewportHeight - tooltipHeight) / 2;
|
| }
|
| }
|
|
|
|
|
| left = Math.max(scrollLeft + 10, Math.min(left, viewportWidth + scrollLeft - tooltipWidth - 10));
|
| top = Math.max(scrollTop + 10, Math.min(top, viewportHeight + scrollTop - tooltipHeight - 10));
|
|
|
|
|
| tooltip.style.left = `${left}px`;
|
| tooltip.style.top = `${top}px`;
|
| tooltip.style.visibility = 'visible';
|
| tooltip.classList.add('active');
|
| };
|
|
|
|
|
| window.addEventListener('resize', () => {
|
| if (tooltip.classList.contains('active')) {
|
| tooltip.classList.remove('active');
|
| }
|
| });
|
|
|
| const hideTooltip = () => {
|
| tooltip.classList.remove('active');
|
| };
|
|
|
| function setupEventListeners() {
|
| document.querySelectorAll('area').forEach((area, index) => {
|
| area.addEventListener('mouseenter', () => {
|
| drawOutline(areas[index].coords);
|
| });
|
| area.addEventListener('mouseleave', clearOutline);
|
|
|
| area.addEventListener('mouseenter', (event) => {
|
| showTooltip(locationsData[index], event);
|
| });
|
| area.addEventListener('mousemove', (event) => {
|
|
|
| const tooltipRect = tooltip.getBoundingClientRect();
|
| const viewportHeight = window.innerHeight;
|
| const viewportWidth = window.innerWidth;
|
|
|
|
|
| if (tooltipRect.width + event.clientX < viewportWidth &&
|
| tooltipRect.height + event.clientY < viewportHeight) {
|
| tooltip.style.left = `${event.pageX + 10}px`;
|
| tooltip.style.top = `${event.pageY + 10}px`;
|
| }
|
| });
|
| area.addEventListener('mouseleave', hideTooltip);
|
| });
|
|
|
| document.addEventListener('mousemove', (event) => {
|
| if (!event.target.closest('area') && !tooltip.contains(event.target)) {
|
| tooltip.style.display = 'none';
|
| tooltip.innerHTML = '';
|
| }
|
| });
|
| } |