Last active
August 2, 2018 11:08
-
-
Save rochoa/4e67ec932e8bb6b17831e0f4a2e0e55d to your computer and use it in GitHub Desktop.
[CARTO] basic viewer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
var DEFAULTS = { | |
endpoint: 'https://rochoa.carto.com', | |
sql: 'select * from airbnb_madrid_oct_2015_listings', | |
cartocss: `#layer['mapnik::geometry_type'=1] { | |
marker-width: 7; | |
marker-fill: #EE4D5A; | |
marker-fill-opacity: 0.9; | |
marker-line-color: #FFFFFF; | |
marker-line-width: 1; | |
marker-line-opacity: 1; | |
marker-placement: point; | |
marker-type: ellipse; | |
marker-allow-overlap: true; | |
} | |
#layer['mapnik::geometry_type'=2] { | |
line-color: #4CC8A3; | |
line-width: 1.5; | |
line-opacity: 1; | |
} | |
#layer['mapnik::geometry_type'=3] { | |
polygon-fill: #826DBA; | |
polygon-opacity: 0.9; | |
::outline { | |
line-color: #FFFFFF; | |
line-width: 1; | |
line-opacity: 0.5; | |
} | |
}`, | |
center: [40.419769381446194, -3.715095520019531], | |
zoom: 12 | |
}; | |
var LOCAL_STORAGE_KEY = 'cartodb-viewer-config'; | |
var Config = { | |
get: function() { | |
var config = { | |
endpoint: DEFAULTS.endpoint, | |
sql: DEFAULTS.sql, | |
cartocss: DEFAULTS.cartocss, | |
center: DEFAULTS.center, | |
zoom: DEFAULTS.zoom | |
}; | |
if (window.localStorage) { | |
var storedConfig = localStorage.getItem(LOCAL_STORAGE_KEY); | |
if (storedConfig) { | |
try { | |
storedConfig = JSON.parse(storedConfig); | |
config.endpoint = storedConfig.endpoint || config.endpoint; | |
config.sql = storedConfig.sql || config.sql; | |
config.cartocss = storedConfig.cartocss || config.cartocss; | |
config.center = storedConfig.center || config.center; | |
config.zoom = storedConfig.zoom || config.zoom; | |
} catch (e) { | |
// pass | |
} | |
} | |
} | |
return config; | |
}, | |
set: function(endpoint, sql, cartocss, center, zoom) { | |
var config = {}; | |
if (endpoint !== DEFAULTS.endpoint) { | |
config.endpoint = endpoint; | |
} | |
if (sql !== DEFAULTS.sql) { | |
config.sql = sql; | |
} | |
if (cartocss !== DEFAULTS.cartocss) { | |
config.cartocss = cartocss; | |
} | |
config.center = center; | |
config.zoom = zoom; | |
if (window.localStorage) { | |
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(config)); | |
return true; | |
} | |
return false; | |
} | |
}; | |
var map = L.map('map', { | |
scrollWheelZoom: false, | |
center: [40.419769381446194, -3.715095520019531], | |
zoom: 12 | |
}); | |
function onMapChanged() { | |
console.log('Current zoom = %d', map.getZoom()); | |
var center = [map.getCenter().lat, map.getCenter().lng]; | |
Config.set(currentMapsEndpoint(), sqlEditor.getValue(), cssEditor.getValue(), center, map.getZoom()); | |
} | |
map.on('zoomend', onMapChanged); | |
map.on('dragend', onMapChanged); | |
L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}@2x.png', { | |
attribution: '<a href="http://cartodb.com">CartoDB</a> © 2014', | |
maxZoom: 18 | |
}).addTo(map); | |
L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager_only_labels/{z}/{x}/{y}@2x.png', { | |
attribution: '<a href="http://cartodb.com">CartoDB</a> © 2014', | |
maxZoom: 18 | |
}).setZIndex(3).addTo(map); | |
var sqlEditor = CodeMirror.fromTextArea(document.getElementById('sql_editor'), { | |
theme: 'monokai', | |
lineNumbers: true, | |
lineWrapping: true, | |
mode: "text/x-plsql", | |
height: '400px' | |
}); | |
var cssEditor = CodeMirror.fromTextArea(document.getElementById('css_editor'), { | |
theme: 'monokai', | |
lineNumbers: true, | |
lineWrapping: true, | |
mode: "text/x-scss", | |
height: "200px" | |
}); | |
function tilesEndpoint(layergroupId) { | |
return currentMapsEndpoint() + '/' + layergroupId + '/{z}/{x}/{y}@2x.png?api_key=' + currentApiKey(); | |
} | |
var tilesLayer = null; | |
async function updateMap() { | |
if (tilesLayer) { | |
map.removeLayer(tilesLayer); | |
} | |
const schemaResponse = await fetch(`${currentSQLEndpoint()}?q=${encodeURIComponent(`SELECT * FROM (${sqlEditor.getValue()}) _q LIMIT 0`)}`); | |
const schemaResult = await schemaResponse.json(); | |
const geometryColumns = Object.keys(schemaResult.fields).filter(f => schemaResult.fields[f].type === 'geometry'); | |
const geometriesResponse = await fetch(`${currentSQLEndpoint()}?q=${encodeURIComponent(`SELECT ${geometryColumns.map(c => `ST_SRID(${c}) as ${c}`)} FROM (${sqlEditor.getValue()}) _q LIMIT 1`)}`); | |
const geometriesResult = await geometriesResponse.json(); | |
let srid = 3857; | |
let geom_column = 'the_geom_webmercator'; | |
// This will use the last geometry column found | |
if (geometriesResult && geometriesResult.rows && geometriesResult.rows.length) { | |
const row = geometriesResult.rows[0]; | |
for (let i = 0, len = geometryColumns.length; i < len; i++) { | |
const column = geometryColumns[i]; | |
const columnSrid = row[column]; | |
srid = columnSrid; | |
geom_column = column; | |
} | |
} | |
var config = { | |
version: '1.7.0', | |
layers: [ | |
{ | |
type: 'cartodb', | |
options: { | |
sql: sqlEditor.getValue(), | |
srid, | |
geom_column, | |
cartocss: cssEditor.getValue(), | |
cartocss_version: '3.0.12' | |
} | |
} | |
] | |
}; | |
var request = new XMLHttpRequest(); | |
request.open('POST', currentMapsEndpoint() + '?api_key=' + currentApiKey(), true); | |
request.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); | |
request.onload = function() { | |
if (this.status >= 200 && this.status < 400){ | |
var layergroup = JSON.parse(this.response); | |
console.log(layergroup.metadata.layers[0].meta.cartocss); | |
tilesLayer = L.tileLayer(tilesEndpoint(layergroup.layergroupid), { | |
maxZoom: 18 | |
}).setZIndex(2).addTo(map); | |
onMapChanged(); | |
} else { | |
throw 'Error calling server: Error ' + this.status + ' -> ' + this.response; | |
} | |
}; | |
request.send(JSON.stringify(config)); | |
} | |
function currentSQLEndpoint() { | |
return currentEndpointOrigin() + '/api/v2/sql'; | |
} | |
function currentMapsEndpoint() { | |
return currentEndpointOrigin() + '/api/v1/map'; | |
} | |
function currentEndpointOrigin() { | |
return new URL(document.getElementById('endpoint').value).origin; | |
} | |
function currentApiKey() { | |
return document.getElementById('apikey').value; | |
} | |
document.getElementById('endpoint').addEventListener('blur', updateMap, false); | |
CodeMirror.commands.save = function() { | |
updateMap(); | |
}; | |
var config = Config.get(); | |
document.getElementById('endpoint').value = config.endpoint; | |
sqlEditor.setValue(config.sql); | |
cssEditor.setValue(config.cartocss); | |
map.setView(config.center, config.zoom); | |
updateMap(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head lang="en"> | |
<meta charset="UTF-8"> | |
<title></title> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.3/leaflet.css" /> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.39.2/codemirror.min.css"> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.39.2/theme/monokai.min.css"> | |
<style> | |
body { | |
margin: 0; | |
padding: 0; | |
border: 0; | |
height: 100%; | |
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; | |
} | |
.wrap { | |
width: 100%; | |
margin: 0 auto; | |
} | |
textarea { | |
padding: 0; | |
margin: 0; | |
border: solid 1px #999; | |
height: 64px; | |
} | |
.editor { | |
float: left; | |
width: 40%; | |
height: 100vh; | |
} | |
.editor p { | |
margin: 8px 0; | |
} | |
.editor label, .editor input, .editor select { | |
width: 80%; | |
margin-bottom: 8px; | |
margin-left: 10px; | |
display: block; | |
} | |
.CodeMirror { | |
float: left; | |
width: 100%; | |
height: 40vh; | |
margin-bottom: 8px; | |
} | |
#map { | |
width: 60%; | |
height: 100vh; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="wrap"> | |
<form class="editor"> | |
<p> | |
<label for="endpoint">Maps API endpoint</label> | |
<input type="text" name="endpoint" id="endpoint" value="https://rochoa.carto.com/api/v1/map"> | |
</p> | |
<p> | |
<label for="apikey">API key</label> | |
<input type="text" name="apikey" id="apikey" value="" placeholder="YOUR API KEY"> | |
</p> | |
<p> | |
<label for="sql_editor">SQL</label> | |
<textarea id="sql_editor" class="code"></textarea> | |
</p> | |
<p> | |
<label for="css_editor">CartoCSS</label> | |
<textarea id="css_editor" class="code"></textarea> | |
</p> | |
</form> | |
<div id="map"></div> | |
</div> | |
</body> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.3/leaflet.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.39.2/codemirror.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.39.2/mode/javascript/javascript.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.39.2/mode/sql/sql.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.39.2/mode/css/css.min.js"></script> | |
<script src="app.js"></script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment