Hacking the SDR:

Adding an Interactive Web App to Highlight Your Data Deposit!

We will not be working IN the actual SDR!

What we will NOT do!

We will be using GitHub.com as our stand-in for the Stanford Digital Repository (SDR)

What we WILL do!

This workshop really boils down to learning how and when to use TWO THINGS:

 

Absolute Paths

&

Relative Paths

Absolute Paths start from the root of a file system or a complete URL:
 

  • https://stacks.stanford.edu/file/druid:vb564st1676/graduation-stanford.tif
     
  • C:\Users\YourName\Documents\project\index.html (Windows)
     
  • /Users/YourName/Documents/project/index.html (Mac/Linux)

https://stacks.stanford.edu/file/druid:vb564st1676/graduation-stanford.tif

https://stacks.stanford.edu/file/druid:vb564st1676/graduation-stanford.tif

https://
stacks.stanford.edu/

file/druid:vb564st1676/

graduation-stanford.tif

Relative Paths are relative to the current file's location. So if you have index.html in a folder:
 

  • ./image.jpg - file in the same directory as the HTML file
  • ../data/info.json - file in a data folder one level up
  • collection/map.geojson - file in a collection subfolder

So that if all of our digital content is in the same place on the SDR:

https://stacks.stanford.edu/file/druid:vb564st1676/index.html

We can address each of the files from index.html using relative paths:

  • index.html
  • ./images/image.png
  • ./collection/a_point_layer.geojson
  • ./collection/a_polygon_layer.geojson
  • ./collection/satellite_image.tif
<!DOCTYPE html>
<html lang="en">
<head>
    <base target="_top">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

</body>
</html>

Step 1: The HTML Boilerplate

<!DOCTYPE html>
<html lang="en">
<head>
    <base target="_top">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Stanford Campus Map - Step 1: Basic HTML</title>
</head>
<body>

<h1>Stanford Public Art Map</h1>
<p>Step 1: Basic HTML structure with map container</p>

<div id="map" style="width: 600px; height: 400px;"></div>

</body>
</html>

Step 2: Make a Map Container 

   <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>

    <style>
        html, body {
            height: 100%;
            margin: 0;
        }
        .leaflet-container {
            height: 400px;
            width: 600px;
            max-width: 100%;
            max-height: 100%;
        }
    </style>
</head>
<body>

<h1>Stanford Public Art Map</h1>
<p>Step 2: Initialize Leaflet map with OpenStreetMap basemap</p>

<div id="map" style="width: 600px; height: 400px;"></div>
<script>

    // Initialize map centered on Stanford campus
    const map = L.map('map').setView([37.427, -122.169], 15);

    // Add OpenStreetMap tile layer
    const tiles = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 19,
        attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
    }).addTo(map);

</script>

Step 3: Initialize the Map

// Load GeoJSON data
    fetch('../stanford_public_art.geojson')
        .then(response => response.json())
        .then(data => {
            // Add GeoJSON layer with custom circle markers
            const artworkLayer = L.geoJSON(data, {
                pointToLayer: function(feature, latlng) {
                    return L.circleMarker(latlng, {
                        radius: 6,
                        color: 'blue',
                        weight: 2,
                        fillColor: 'blue',
                        fillOpacity: 0.7
                    });
                }
            }).addTo(map);

            // Zoom to fit all markers
            map.fitBounds(artworkLayer.getBounds());
        })
        .catch(error => console.error('Error loading GeoJSON:', error));

Step 4: Adding a Geojson Layer

                onEachFeature: function(feature, layer) {
                    const props = feature.properties || {};
                    const title = props.name || 'Artwork';
                    const artist = props.artist_name;
                    const type = props.artwork_type;

                    // Build popup content
                    let popupContent = '<div style="min-width:200px;">';
                    popupContent += '<b>' + title + '</b><br>';
                    if (artist) popupContent += '<b>Artist:</b> ' + artist + '<br>';
                    if (type) popupContent += '<b>Type:</b> ' + type;
                    popupContent += '</div>';

                    layer.bindPopup(popupContent);
                }

Step 4: Creating Popups

  // URL to the COG file - using relative path
    var url_to_geotiff_file = new URL("collection/stanford_campus_irg.tif", window.location.href).href;

    // Parse and display the COG
    parseGeoraster(url_to_geotiff_file).then(georaster => {
      console.log("georaster:", georaster);

Step 4: Adding the COG

  // URL to the COG file - using relative path
    var url_to_geotiff_file = new URL("collection/stanford_campus_irg.tif", window.location.href).href;

    // Parse and display the COG
    parseGeoraster(url_to_geotiff_file).then(georaster => {
      console.log("georaster:", georaster);

Step 4: Adding the COG

Hacking the SDR: Making an App to Highlight Your Deposit!

By Stace Maples

Hacking the SDR: Making an App to Highlight Your Deposit!

Slides to accompany the workshop: https://bit.ly/InteractiveSDR

  • 13