Overview
The editor developed in this project is a web-based tool for recording and managing arbitrary coordinates on IIIF-compatible high-resolution images. It is designed as a general-purpose coordinate recording tool that can specify images via URL parameters and be used across various research projects.

Technology Stack
- OpenSeadragon: IIIF image viewer library (v4.1)
- SVG Overlay: For marker display
- localStorage: Data persistence
- Vanilla JavaScript: Framework-free implementation
Technical Features
1. Image Specification via URL Parameters
The tool’s most distinctive feature is the ability to specify any IIIF image via URL parameters:
function getImageUrlFromQuery() {
const urlParams = new URLSearchParams(window.location.search);
const urlParam = urlParams.get('u');
if (urlParam) {
try {
return decodeURIComponent(urlParam);
} catch (e) {
console.error('Error decoding URL parameter:', e);
alert('Failed to decode URL parameter. Using default image.');
}
}
// Default image URL
return 'https://img.toyobunko-lab.jp/iiif/premodern_chinese/suikeichuzu/Suikeichuuzu_grid_l.tif';
}
const imageUrl = getImageUrlFromQuery();
Usage example:
intersection_editor.html?u=https%3A%2F%2Fexample.com%2Fiiif%2Fimage.tif
Simply pass a URL-encoded image URL via the ?u= parameter to open any image.
2. Per-Image Data Separation
By including the image URL in the localStorage key, data is managed independently for each image:
const imageUrl = getImageUrlFromQuery();
const storageKey = `intersection_points_${btoa(imageUrl).substring(0, 50)}`;
By Base64-encoding the image URL and using it as part of the key, multiple image projects can be managed simultaneously.
3. Automatic IIIF Image Loading
The IIIF info.json URL is automatically generated from the image URL:
function getIIIFInfoUrl(imageUrl) {
if (imageUrl.endsWith('info.json')) {
return imageUrl;
}
let baseUrl = imageUrl.replace(/\.(jpg|jpeg|png|tif|tiff)$/i, '');
return `${baseUrl}/info.json`;
}
const viewer = OpenSeadragon({
id: "viewer",
tileSources: imageUrl.startsWith('http') ? getIIIFInfoUrl(imageUrl) : imageUrl,
showNavigationControl: true,
showNavigator: true
});
File extensions such as .tif and .jpg are automatically handled, and an info.json compliant with IIIF Image API 2.0 is requested.
4. Coordinate Transformation System
OpenSeadragon uses three coordinate systems:
- Pixel coordinates: Display position in the browser
- Viewport coordinates: Normalized coordinates (0 to 1)
- Image coordinates: Actual image pixel coordinates
This tool performs conversions as follows:
// Click position -> Image coordinates
const viewportPoint = viewer.viewport.pointFromPixel(event.position);
const imagePoint = viewer.viewport.viewportToImageCoordinates(viewportPoint);
const x = Math.round(imagePoint.x);
const y = Math.round(imagePoint.y);
5. Dynamic Marker Display with SVG Overlay
Markers that follow zoom and pan operations are implemented:
function updateMarkerPositions() {
const markers = svgOverlay.querySelectorAll('g');
markers.forEach(marker => {
const imageX = parseFloat(marker.getAttribute('data-image-x'));
const imageY = parseFloat(marker.getAttribute('data-image-y'));
const imagePoint = new OpenSeadragon.Point(imageX, imageY);
const viewportPoint = viewer.viewport.imageToViewportCoordinates(imagePoint);
const pixelPoint = viewer.viewport.pixelFromPoint(viewportPoint, true);
marker.setAttribute('transform', `translate(${pixelPoint.x}, ${pixelPoint.y})`);
});
}
Auto-updating on viewport change events:
viewer.addHandler('update-viewport', updateMarkerPositions);
viewer.addHandler('resize', updateMarkerPositions);
6. Auto-Navigation Feature
To maximize user efficiency, the tool predicts the next intersection position and automatically moves the view.
Basic Algorithm
function predictAndNavigateToNext() {
const lastPoint = points[len - 1];
const secondLastPoint = points[len - 2];
// Vector calculation
const dx = lastPoint.x - secondLastPoint.x;
const dy = lastPoint.y - secondLastPoint.y;
// Predicted next position
const predictedX = lastPoint.x + dx;
const predictedY = lastPoint.y + dy;
// Move the view
const imagePoint = new OpenSeadragon.Point(predictedX, predictedY);
const viewportPoint = viewer.viewport.imageToViewportCoordinates(imagePoint);
viewer.viewport.panTo(viewportPoint, true);
}
Distance Change Detection and Warning
When an abnormal movement distance is detected, the user is prompted for confirmation:
const currentDistance = Math.sqrt(dx * dx + dy * dy);
const prevDistance = Math.sqrt(prevDx * prevDx + prevDy * prevDy);
const distanceRatio = currentDistance / prevDistance;
if (distanceRatio > 1.5 || distanceRatio 0.67) {
const shouldMove = confirm(
`The movement distance differs significantly from the previous one.\n` +
`Previous: ${Math.round(prevDistance)}px\n` +
`Current: ${Math.round(currentDistance)}px\n\n` +
`Move to the predicted position?`
);
if (!shouldMove) return;
}
This allows detection of accidental clicks or pattern changes.
7. Data Persistence and Backup
Auto-Save with localStorage
function savePointsToStorage() {
const data = {
imageUrl: imageUrl,
timestamp: new Date().toISOString(),
points: points
};
localStorage.setItem(storageKey, JSON.stringify(data));
}
Auto-saving is performed in an independent data space associated with each image URL.
Export Features
CSV format:
number,x,y,memo
1,12345,23456,"Important point"
2,12389,23456,""
JSON format:
{
"imageUrl": "https://example.com/iiif/image.tif",
"count": 2,
"coordinates": [
{
"id": 1,
"x": 12345,
"y": 23456,
"memo": "Important point"
},
{
"id": 2,
"x": 12389,
"y": 23456
}
]
}
JSON exports include the image URL, allowing later identification of which image the data belongs to.
8. Switching Between Edit Mode and View Mode
function toggleEditMode(enabled) {
editMode = enabled;
// Disable new additions
if (!editMode) {
// Early return on canvas-click event
if (!editMode) return;
}
// UI update
const clearBtn = document.getElementById('clear-btn');
const importBtn = document.getElementById('import-btn');
clearBtn.disabled = !enabled;
importBtn.disabled = !enabled;
}
In view mode, adding and editing memos is still possible, but adding new points and deleting existing ones are disabled.
9. Point Reordering Feature
For cases where reordering is needed:
function movePointTo(fromIndex, toIndex) {
// Extract the point
const [movedPoint] = points.splice(fromIndex, 1);
// Calculate new position
let newIndex = toIndex;
if (fromIndex toIndex) {
newIndex = toIndex;
} else {
newIndex = toIndex + 1;
}
// Insert
points.splice(newIndex, 0, movedPoint);
// Redraw
redrawMarkers();
}
10. Keyboard Shortcuts
- Delete: Delete the selected point
- Up/Down arrows: Navigate between points
document.addEventListener('keydown', function(e) {
if (e.key === 'Delete' && selectedPointIndex !== null) {
deletePoint(selectedPointIndex);
}
if (e.key === 'ArrowDown' && selectedPointIndex !== null) {
selectedPointIndex++;
// Move the view
const point = points[selectedPointIndex];
const imagePoint = new OpenSeadragon.Point(point.x, point.y);
const viewportPoint = viewer.viewport.imageToViewportCoordinates(imagePoint);
viewer.viewport.panTo(viewportPoint);
}
});
User Interface Design
Responsive Layout
body {
display: flex;
height: 100vh;
}
#viewer {
flex: 1;
height: 100vh;
}
#sidebar {
width: 400px;
display: flex;
flex-direction: column;
}
#sidebar-content {
flex: 1;
overflow-y: auto;
}
The viewer and sidebar are positioned at fixed height, with a scrollable list area.
Visual Feedback
- Normal points: Red circle markers + number labels
- Predicted points: Blue dashed circles + crosshair
- Selected: Green background
- Move mode: Orange background
Performance Optimization
1. Marker Update Optimization
When the viewport changes, all markers are recalculated, but the DOM is not regenerated. Only the transform attribute is updated:
marker.setAttribute('transform', `translate(${pixelPoint.x}, ${pixelPoint.y})`);
2. Event Delegation
Instead of setting event listeners on individual markers, clicks are handled at the parent element:
headerDiv.onclick = () => {
selectedPointIndex = index;
updatePointsList();
};
Data Format
Internal Data Structure
points = [
{
x: 12345, // X coordinate on the image
y: 23456, // Y coordinate on the image
memo: "Note" // Optional memo
},
// ...
]
localStorage Format
{
"imageUrl": "https://img.toyobunko-lab.jp/iiif/...",
"timestamp": "2025-10-29T12:34:56.789Z",
"points": [...]
}
Usage Examples
Basic Workflow
- Specify an image via URL parameter (or use the default image)
intersection_editor.html?u=https%3A%2F%2Fexample.com%2Fiiif%2Fimage.tif
- The page automatically loads previously saved data upon opening
- Click on coordinates in the image to record them
- The view automatically moves to the next predicted position
- Add memos as needed
- Export in CSV/JSON format
Managing Multiple Image Projects
When working on multiple projects with different images simultaneously:
- Create dedicated URL bookmarks for each image
- Data for each image is saved independently in localStorage
- No risk of data mixing between images
- JSON exports include the image URL
Processing Large Datasets
When recording hundreds to thousands of coordinates:
- Enable auto-navigation to improve efficiency
- Periodically back up in JSON format
- Use the reordering feature to correct mistakes
- After completion, export in CSV format for analysis
Security Considerations
XSS Prevention
Memo input values are escaped during CSV export:
const memo = (point.memo || '').replace(/"/g, '""');
csv += `${index + 1},${point.x},${point.y},"${memo}"\n`;
Data Validation
JSON structure is validated during import (maintaining backward compatibility with older formats):
const coordsArray = data.coordinates || data.intersections;
if (!coordsArray || !Array.isArray(coordsArray)) {
alert('Invalid JSON format.\nA "coordinates" or "intersections" array is required.');
return;
}
Future Improvements
- Undo/Redo: Implementing operation history
- Export format expansion: Supporting GeoJSON and Allmaps formats
- Collaborative editing: Real-time sharing via WebSocket
- Precision improvement: Sub-pixel precision coordinate recording
- AI assistance: Automatic intersection detection
Summary
This tool combines flexible image specification via URL parameters, per-image data separation, IIIF image display capabilities, and auto-navigation to achieve a general-purpose system for efficiently recording large volumes of coordinate data.
References
Created: October 2025 Version: 1.0