Permalink
Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
Cannot retrieve contributors at this time.
Cannot retrieve contributors at this time
| <!-- This file is the root of https://cdn.proj.org/ --> | |
| <!-- Part of it is generated by the regenerate_index_html.py script --> | |
| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> | |
| <html lang="en"> | |
| <head> | |
| <meta http-equiv="Content-Type" content="text/html;charset=utf-8" > | |
| <title>PROJ Datumgrid CDN</title> | |
| <!-- | |
| <script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v6.1.1/build/ol.js"></script> | |
| <link rel="stylesheet" href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v6.1.1/css/ol.css"> | |
| --> | |
| <script src="ol.js"></script> | |
| <link rel="stylesheet" href="ol.css"> | |
| <!-- corresponding to https://github.com/rouault/geotiff.js/pull/new/cumulative_fixes --> | |
| <script src="geotiff.bundle.min.js"></script> | |
| <style> | |
| .map { | |
| width: 50%; | |
| height: 600px; | |
| } | |
| .ol-popup { | |
| position: absolute; | |
| background-color: white; | |
| -webkit-filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2)); | |
| filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2)); | |
| padding: 15px; | |
| border-radius: 10px; | |
| border: 1px solid #cccccc; | |
| bottom: 12px; | |
| left: -50px; | |
| min-width: 280px; | |
| } | |
| .ol-popup:after, .ol-popup:before { | |
| top: 100%; | |
| border: solid transparent; | |
| content: " "; | |
| height: 0; | |
| width: 0; | |
| position: absolute; | |
| pointer-events: none; | |
| } | |
| .ol-popup:after { | |
| border-top-color: white; | |
| border-width: 10px; | |
| left: 48px; | |
| margin-left: -10px; | |
| } | |
| .ol-popup:before { | |
| border-top-color: #cccccc; | |
| border-width: 11px; | |
| left: 48px; | |
| margin-left: -11px; | |
| } | |
| .ol-popup-closer { | |
| text-decoration: none; | |
| position: absolute; | |
| top: 2px; | |
| right: 8px; | |
| } | |
| .ol-popup-closer:after { | |
| content: "✖"; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>PROJ.org Datumgrid CDN</h1> | |
| <p> | |
| The s3://cdn.proj.org bucket is hosted by the | |
| <a href="https://aws.amazon.com/opendata/public-datasets/">Amazon Public Datasets</a> program, and it | |
| can be accessed via HTTP/S at <a href="https://cdn.proj.org">cdn.proj.org</a>. CDN services are | |
| provided by the AWS Public Dataset team via <a href="https://aws.amazon.com/cloudfront/">CloudFront</a>. | |
| </p> | |
| <p> | |
| The CDN contains <a href="https://www.cogeo.org/">Cloud Optimized GeoTIFF</a> datasets which are mirrored and | |
| managed by the | |
| <a href="https://github.com/OSGeo/PROJ-data/">https://github.com/OSGeo/PROJ-data/</a> | |
| GitHub project. Files in the CDN are designed to be used by <a href="https://proj.org">PROJ</a> 7 or later, but any software | |
| project wishing to use the CDN for shifting support are encouraged to participate in the project and leverage the CDN. | |
| </p> | |
| <h2>Mirroring</h2> | |
| <p>If you are able, you are encouraged to mirror the grids via AWS S3 command line: </p> | |
| <pre>aws s3 sync s3://cdn.proj.org .</pre> | |
| <p>If direct S3 access is not possible, you can also use <i>wget</i> to locally mirror | |
| the data: </p> | |
| <pre>wget --mirror https://cdn.proj.org/</pre> | |
| <h2>Content</h2> | |
| <div id="type_control" style="height:20px"> | |
| <label>Types:</label> | |
| </div> | |
| <br> | |
| <div id="agency_control" style="height:80px"> | |
| <label>Agencies:</label> | |
| </div> | |
| <div> | |
| <div id="map" class="map" style="float: left;"></div> | |
| <div id="details" style="height: 600px; overflow-y: scroll;"> | |
| Click on the map to display the names of the files under the pointer, | |
| and get the values of the shift(s). | |
| </div> | |
| <div style="clear: both;"></div> | |
| </div> | |
| <div id="popup" class="ol-popup"> | |
| <a href="#" id="popup-closer" class="ol-popup-closer"></a> | |
| <div id="popup-content"></div> | |
| </div> | |
| <script> | |
| var fullGeoJSON = null; | |
| var agencies_enabled = {} | |
| var list_agencies = [] | |
| var featureSource = new ol.source.Vector({ | |
| format: new ol.format.GeoJSON(), | |
| loader: function(extent, resolution, projection) { | |
| var url = 'files.geojson'; | |
| var xhr = new XMLHttpRequest(); | |
| xhr.open('GET', url); | |
| xhr.onError = function() {}; | |
| xhr.onload = function() { | |
| if (xhr.status == 200) { | |
| fullGeoJSON = JSON.parse(xhr.responseText); | |
| fullGeoJSON.features.forEach(function(f) { | |
| if( agencies_enabled[f.properties.source_id] === undefined ) { | |
| agencies_enabled[f.properties.source_id] = true; | |
| list_agencies.push(f.properties.source_id); | |
| } | |
| }); | |
| list_agencies.sort(); | |
| var agency_control = document.getElementById('agency_control'); | |
| list_agencies.forEach(function(x){ | |
| create_checkbox(x, x, agency_control, agencies_enabled); | |
| }); | |
| featureSource.addFeatures( | |
| featureSource.getFormat().readFeatures(fullGeoJSON, | |
| { | |
| dataProjection: 'EPSG:4326', | |
| featureProjection: projection | |
| })); | |
| } | |
| } | |
| xhr.send(); | |
| } | |
| }); | |
| var style = new ol.style.Style({ | |
| stroke : new ol.style.Stroke( | |
| { | |
| width : 1 | |
| }), | |
| fill : new ol.style.Fill( | |
| { | |
| color : 'rgba(0, 0, 0, 0.01)' | |
| }) | |
| }); | |
| var featureLayer = new ol.layer.Vector({ | |
| source: featureSource, | |
| style: style | |
| }); | |
| var types_enabled = {}; | |
| types_enabled['horizontal'] = true; | |
| types_enabled['geoid'] = true; | |
| types_enabled['vertical_adjustments'] = true; | |
| types_enabled['velocity'] = true; | |
| types_enabled['other'] = true; | |
| function refresh_source() { | |
| featureSource.clear(); | |
| var filtered = { | |
| "type": "FeatureCollection", | |
| "features": fullGeoJSON.features.filter(function(feature){ | |
| var ok_type = false; | |
| if( types_enabled['horizontal'] && | |
| feature.properties.type == 'HORIZONTAL_OFFSET') { | |
| ok_type = true; | |
| } | |
| if( types_enabled['geoid'] && | |
| feature.properties.type == 'VERTICAL_OFFSET_GEOGRAPHIC_TO_VERTICAL') { | |
| ok_type = true; | |
| } | |
| if( types_enabled['vertical_adjustments'] && | |
| feature.properties.type == 'VERTICAL_OFFSET_VERTICAL_TO_VERTICAL') { | |
| ok_type = true; | |
| } | |
| if( types_enabled['velocity'] && | |
| feature.properties.type == 'VELOCITY') { | |
| ok_type = true; | |
| } | |
| if( types_enabled['other'] && | |
| feature.properties.type != 'HORIZONTAL_OFFSET' && | |
| feature.properties.type != 'VERTICAL_OFFSET_GEOGRAPHIC_TO_VERTICAL' && | |
| feature.properties.type != 'VERTICAL_OFFSET_VERTICAL_TO_VERTICAL' && | |
| feature.properties.type != 'VELOCITY') { | |
| ok_type = true; | |
| } | |
| var ok_agency = agencies_enabled[feature.properties.source_id]; | |
| return ok_type && ok_agency; | |
| }) | |
| }; | |
| featureSource.addFeatures( | |
| featureSource.getFormat().readFeatures(filtered, | |
| { | |
| dataProjection: 'EPSG:4326', | |
| featureProjection: 'EPSG:3857' | |
| } | |
| )); | |
| } | |
| function create_checkbox(id, label_name, group, map) { | |
| var checkbox = document.createElement('input'); | |
| checkbox.type = "checkbox"; | |
| id_checkbox = 'id_checkbox_' + id; | |
| checkbox.id = id_checkbox; | |
| checkbox.checked = true; | |
| var label = document.createElement('label'); | |
| label.htmlFor = id_checkbox; | |
| label.appendChild(document.createTextNode(label_name)); | |
| group.appendChild(checkbox); | |
| group.appendChild(label); | |
| document.getElementById(id_checkbox).addEventListener('change', function() { | |
| map[id] = this.checked; | |
| refresh_source(); | |
| }); | |
| } | |
| var type_control = document.getElementById('type_control'); | |
| create_checkbox('horizontal', 'Horizontal shift grids', type_control, types_enabled); | |
| create_checkbox('geoid', 'Geoid models', type_control, types_enabled); | |
| create_checkbox('vertical_adjustments', 'Vertical shifts', type_control, types_enabled); | |
| create_checkbox('velocity', 'Velocity grids', type_control, types_enabled); | |
| create_checkbox('other', 'Other grids', type_control, types_enabled); | |
| /** | |
| * Elements that make up the popup. | |
| */ | |
| var container = document.getElementById('popup'); | |
| var content = document.getElementById('popup-content'); | |
| var closer = document.getElementById('popup-closer'); | |
| var details = document.getElementById('details'); | |
| /** | |
| * Create an overlay to anchor the popup to the map. | |
| */ | |
| var overlay = new ol.Overlay({ | |
| element: container, | |
| autoPan: true, | |
| autoPanAnimation: { | |
| duration: 250 | |
| } | |
| }); | |
| /** | |
| * Add a click handler to hide the popup. | |
| * @return {boolean} Don't follow the href. | |
| */ | |
| closer.onclick = function() { | |
| overlay.setPosition(undefined); | |
| closer.blur(); | |
| return false; | |
| }; | |
| var map = new ol.Map({ | |
| layers: [ | |
| new ol.layer.Tile({source: new ol.source.OSM()}), | |
| featureLayer], | |
| renderer: 'canvas', | |
| target: 'map', | |
| overlays: [overlay], | |
| view: new ol.View({ | |
| center: [0, 0], | |
| zoom: 0 | |
| }) | |
| }); | |
| // Add an event handler for the map "singleclick" event | |
| map.on('singleclick', function(evt) { | |
| var coordinate = evt.coordinate; | |
| let longlat = ol.proj.transform(coordinate, 'EPSG:3857', 'EPSG:4326'); | |
| while( longlat[0] > 180 ) { | |
| longlat[0] -= 360; | |
| } | |
| while( longlat[0] < -180 ) { | |
| longlat[0] += 360; | |
| } | |
| //content_innerHTML = ''; | |
| details_innerHTML = 'Latitude: ' + (longlat[1]).toFixed(8); | |
| details_innerHTML += '. Longitude: ' + (longlat[0]).toFixed(8); | |
| details_innerHTML += '.<br>Values displayed in their interpolation CRS, and at the closest node.' | |
| count = 0; | |
| requests = []; | |
| map.forEachFeatureAtPixel(evt.pixel, function(feature, layer) { | |
| var props = feature.getProperties(); | |
| count ++; | |
| //content_innerHTML += '<p><a href="' + props.name + '">' + props.name + '</a></p>'; | |
| details_innerHTML += '<hr>'; | |
| details_innerHTML += '<p><a href="' + props.name + '">' + props.name + '</a>: ' | |
| if( props.area_of_use ) { | |
| details_innerHTML += props.area_of_use + ', '; | |
| } | |
| details_innerHTML += props.source; | |
| details_innerHTML += ', ' + props.description; | |
| const div_id = 'value_' + count + '_' + coordinate; | |
| details_innerHTML += '<div id="' + div_id + '"><p>Loading in progress...</p></div>'; | |
| details_innerHTML += '</p>'; | |
| requests.push([async function(props, count) { | |
| //const url = "./" + props.source_id + "/" + props.name; | |
| const url = props.url; | |
| const tiff = await GeoTIFF.fromUrl(url); | |
| let best_image = -1; | |
| const imageCount = await tiff.getImageCount(); | |
| for( let idx_image = 0; idx_image < imageCount; idx_image++ ) { | |
| const image = await tiff.getImage(idx_image); | |
| const width = image.getWidth(); | |
| const height = image.getHeight(); | |
| const origin = image.getOrigin(); | |
| const resolution = image.getResolution(); | |
| let origin_lon = origin[0]; | |
| if( longlat[0] < origin_lon ) | |
| origin_lon -= 360; | |
| else if( longlat[0] > origin_lon + (width-1) * resolution[0] ) | |
| origin_lon += 360; | |
| const origin_lat = origin[1]; | |
| let left = Math.round((longlat[0] - origin_lon) / resolution[0]); | |
| let top = Math.round((longlat[1] - origin_lat) / resolution[1]); | |
| let right = left + 1; | |
| let bottom = top + 1; | |
| if( left >= 0 && top >= 0 && right <= width && bottom <= height ) { | |
| best_image = idx_image; | |
| } | |
| } | |
| if( best_image < 0 ) { | |
| document.getElementById(div_id).innerHTML = '<p>Out of grid</p>'; | |
| return; | |
| } | |
| const first_image = await tiff.getImage(0); | |
| const md = first_image.getGDALMetadata(); | |
| const image = await tiff.getImage(best_image); | |
| const width = image.getWidth(); | |
| const height = image.getHeight(); | |
| const origin = image.getOrigin(); | |
| const resolution = image.getResolution(); | |
| let origin_lon = origin[0]; | |
| if( longlat[0] < origin_lon ) | |
| origin_lon -= 360; | |
| else if( longlat[0] > origin_lon + (width-1) * resolution[0] ) | |
| origin_lon += 360; | |
| const origin_lat = origin[1]; | |
| let left = Math.round((longlat[0] - origin_lon) / resolution[0]); | |
| let top = Math.round((longlat[1] - origin_lat) / resolution[1]); | |
| let right = left + 1; | |
| let bottom = top + 1; | |
| if( left >= 0 && top >= 0 && right <= width && bottom <= height ) { | |
| const data = await image.readRasters({ window: [left, top, right, bottom] }); | |
| valueHTML = '' | |
| for (let i = 0; i < data.length; ++i) { | |
| const md_band = first_image.getGDALMetadata(i); | |
| const scale = (md_band['SCALE'] != null) ? Number(md_band['SCALE']) : 1.0; | |
| const offset = (md_band['OFFSET'] != null) ? Number(md_band['OFFSET']) : 0.0; | |
| const unscaled_val = data[i][0]; | |
| const nodata = image.getGDALNoData(); | |
| const band_desc = (md_band['DESCRIPTION'] != null) ? md_band['DESCRIPTION'] : 'Band ' + (i+1); | |
| if( nodata != null && unscaled_val == nodata ) { | |
| if( i > 0 ) { | |
| valueHTML += '<br>'; | |
| } | |
| valueHTML += band_desc + ': nodata'; | |
| } else { | |
| const unit = md_band['UNITTYPE']; | |
| const scaled_val = unscaled_val * scale + offset; | |
| if( (band_desc == 'latitude_offset_accuracy' || band_desc == 'longitude_offset_accuracy') && scaled_val <= 0 ) { | |
| // nothing | |
| } | |
| else { | |
| const precision = (unit && unit == "metre") ? 3 : 6; | |
| if( i > 0 ) { | |
| valueHTML += '<br>'; | |
| } | |
| valueHTML += band_desc + ': ' + (scaled_val).toFixed(precision); | |
| if( unit ) { | |
| valueHTML += ' ' + unit; | |
| } | |
| } | |
| } | |
| } | |
| document.getElementById(div_id).innerHTML = valueHTML; | |
| } | |
| }, props, count]); | |
| }); | |
| details.innerHTML = details_innerHTML; | |
| for( let i = 0; i < requests.length; i++ ) { | |
| requests[i][0](requests[i][1], requests[i][2]); | |
| } | |
| /* | |
| if( count >= 5 ) { | |
| content_innerHTML = ''; | |
| } | |
| content.innerHTML = content_innerHTML; | |
| if (content_innerHTML != '') { | |
| overlay.setPosition(coordinate); | |
| } else { | |
| overlay.setPosition(undefined); | |
| } | |
| */ | |
| }); | |
| </script> | |
| <p>The bucket contains the following files:</p> | |
| <ul> | |
| <li><a href="README.DATA">README.DATA</a></li> | |
| ${LINKS_WILL_BE_ADDED_HERE_BY_REGENERATE_INDEX_HTML} | |
| </ul> | |
| <p> | |
| Total size of content: ${TOTAL_SIZE} | |
| </p> | |
| <h2>Logs</h2> | |
| <p>Access logs to this resource are permanently deleted after one day, are not | |
| mirrored or stored, and are not publicly available. If this policy is not | |
| sufficient, users are encourage to mirror a local copy of the grid files and | |
| access them directly. | |
| </p> | |
| <div style="text-align: center;"> | |
| <a href="https://aws.amazon.com/what-is-cloud-computing"> | |
| <img src="https://d0.awsstatic.com/logos/powered-by-aws.png" style="position: relative; top: 50%; transform: translateY(50%);" alt="Powered by AWS Cloud Computing"> | |
| </a> for use by | |
| <a href="https://proj.org"> | |
| <img src="https://proj.org/_static/logo.png" style="position: relative; top: 50%; transform: translateY(50%);" alt="PROJ"></a> | |
| </div> | |
| </body> | |
| </html> |