should probably figure out how to detect clicks on geojson layer
should probably figure out how to center map on searched up layer from searchable select field.
| # browse-regions-compare-census-gist |
| [ | |
| { | |
| "id": 0, | |
| "text": "rent" | |
| }, | |
| { | |
| "id": 1, | |
| "text": "ami" | |
| }, | |
| { | |
| "id": 2, | |
| "text": "Aachen" | |
| }, | |
| { | |
| "id": 3, | |
| "text": "Aaliyah" | |
| }, | |
| { | |
| "id": 4, | |
| "text": "Aaron" | |
| }, | |
| { | |
| "id": 5, | |
| "text": "Abbas" | |
| }, | |
| { | |
| "id": 6, | |
| "text": "Abbasid" | |
| }, | |
| { | |
| "id": 7, | |
| "text": "Abbott" | |
| }, | |
| { | |
| "id": 8, | |
| "text": "Abby" | |
| }, | |
| { | |
| "id": 9, | |
| "text": "Abdul" | |
| }, | |
| { | |
| "id": 10, | |
| "text": "Abe" | |
| }, | |
| { | |
| "id": 11, | |
| "text": "Abel" | |
| }, | |
| { | |
| "id": 12, | |
| "text": "Abelard" | |
| }, | |
| { | |
| "id": 13, | |
| "text": "Abelson" | |
| }, | |
| { | |
| "id": 14, | |
| "text": "Aberdeen" | |
| }, | |
| { | |
| "id": 15, | |
| "text": "Abernathy" | |
| }, | |
| { | |
| "id": 16, | |
| "text": "Abidjan" | |
| }, | |
| { | |
| "id": 17, | |
| "text": "Abigail" | |
| }, | |
| { | |
| "id": 18, | |
| "text": "Abilene" | |
| }, | |
| { | |
| "id": 19, | |
| "text": "Abner" | |
| }, | |
| { | |
| "id": 20, | |
| "text": "Abraham" | |
| }, | |
| { | |
| "id": 21, | |
| "text": "Abram" | |
| }, | |
| { | |
| "id": 22, | |
| "text": "Abrams" | |
| }, | |
| { | |
| "id": 23, | |
| "text": "Absalom" | |
| }, | |
| { | |
| "id": 24, | |
| "text": "Abuja" | |
| }, | |
| { | |
| "id": 25, | |
| "text": "Abyssinia" | |
| }, | |
| { | |
| "id": 26, | |
| "text": "Abyssinian" | |
| }, | |
| { | |
| "id": 27, | |
| "text": "Ac" | |
| }, | |
| { | |
| "id": 28, | |
| "text": "Acadia" | |
| }, | |
| { | |
| "id": 29, | |
| "text": "Acapulco" | |
| }, | |
| { | |
| "id": 30, | |
| "text": "Accenture" | |
| }, | |
| { | |
| "id": 31, | |
| "text": "Accra" | |
| }, | |
| { | |
| "id": 32, | |
| "text": "Acevedo" | |
| }, | |
| { | |
| "id": 33, | |
| "text": "Achaean" | |
| }, | |
| { | |
| "id": 34, | |
| "text": "Achebe" | |
| }, | |
| { | |
| "id": 35, | |
| "text": "Achernar" | |
| }, | |
| { | |
| "id": 36, | |
| "text": "Acheson" | |
| }, | |
| { | |
| "id": 37, | |
| "text": "Achilles" | |
| }, | |
| { | |
| "id": 38, | |
| "text": "Aconcagua" | |
| }, | |
| { | |
| "id": 39, | |
| "text": "Acosta" | |
| }, | |
| { | |
| "id": 40, | |
| "text": "Acropolis" | |
| }, | |
| { | |
| "id": 41, | |
| "text": "Acrux" | |
| }, | |
| { | |
| "id": 42, | |
| "text": "Actaeon" | |
| }, | |
| { | |
| "id": 43, | |
| "text": "Acton" | |
| }, | |
| { | |
| "id": 44, | |
| "text": "Acts" | |
| }, | |
| { | |
| "id": 45, | |
| "text": "Acuff" | |
| }, | |
| { | |
| "id": 46, | |
| "text": "Ada" | |
| }, | |
| { | |
| "id": 47, | |
| "text": "Adam" | |
| }, | |
| { | |
| "id": 48, | |
| "text": "Adams" | |
| }, | |
| { | |
| "id": 49, | |
| "text": "Adan" | |
| }, | |
| { | |
| "id": 50, | |
| "text": "Adana" | |
| } | |
| ] |
| [ | |
| { | |
| "id": 0, | |
| "text": 2010 | |
| }, | |
| { | |
| "id": 1, | |
| "text": 2016 | |
| } | |
| ] |
| [ | |
| { | |
| "year": 2010, | |
| "field": "rent", | |
| "value": "high" | |
| }, | |
| { | |
| "year": 2010, | |
| "field": "ami", | |
| "value": "low" | |
| }, | |
| { | |
| "year": 2016, | |
| "field": "rent", | |
| "value": "higher" | |
| }, | |
| { | |
| "year": 2016, | |
| "field": "ami", | |
| "value": "high" | |
| } | |
| ] |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <!-- Required meta tags --> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | |
| <title>Browse Regions Click Compare Census</title> | |
| <!-- Bootstrap CSS --> | |
| <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous"> | |
| <!-- Optional JavaScript --> | |
| <!-- jQuery first, then Popper.js, then Bootstrap JS --> | |
| <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script> | |
| <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script> | |
| <!-- Select stuff --> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" /> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script> | |
| <!-- Leaflet stuff --> | |
| <link rel="stylesheet" href="https://unpkg.com/leaflet@1.4.0/dist/leaflet.css" | |
| integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA==" | |
| crossorigin=""/> | |
| <script src="https://unpkg.com/leaflet@1.4.0/dist/leaflet.js" | |
| integrity="sha512-QVftwZFqvtRNi0ZyCtsznlKSWOStnDORoefr1enyq5mVL4tmKB3S/EnC3rRJcxCPavG10IcrVGSmPh6Qw5lwrg==" | |
| crossorigin=""></script> | |
| <script src="/script.js"></script> | |
| <link rel="stylesheet" href="/style.css"> | |
| </head> | |
| <body> | |
| <div class="container-fluid"> | |
| <div class="row mt-5"> | |
| <div class="col-sm-5"> <!-- map stuff --> | |
| <h4>Click on a region.</h4> | |
| <div id="media-container" class="mt-3 mb-3"> | |
| <div class="d-inline p-3"> | |
| <label>Currently Selected:</label> | |
| <span id="selected-region" class="p-2" style="background-color: #eee; border-radius: 10px;">None</span> | |
| <script> | |
| function setRegion(region) { | |
| window.region = region; | |
| $('#selected-region').text(region); | |
| } | |
| </script> | |
| </div> | |
| </div> | |
| <div id="map-container" style="height: 400px;"></div> | |
| <h3 id="error" class="hidden"></h3> | |
| </div><!-- end map stuff --> | |
| <div class="col-sm-7"><!-- interface stuff --> | |
| <div class="row justify-content-between"> | |
| <div class="col-6"> | |
| <h4>pick No 1 census year</h4> | |
| <div class="m-3"> | |
| <select id="sel-census-1" class="c-select census sel-skinny"></select> | |
| <!-- <button id="clr-census-1" type="button" class="btn btn-light btn-sm">clear</button> --> | |
| </div> | |
| </div> | |
| <div class="col-6"> | |
| <h4>pick No 2 census year</h4> | |
| <div class="m-3"> | |
| <select id="sel-census-2" class="c-select census sel-skinny"></select> | |
| <!-- <button id="clr-census-2" type="button" class="btn btn-light btn-sm">clear</button> --> | |
| </div> | |
| </div> | |
| </div> | |
| <h4 class="mt-5 mb-2">Select Census Fields to compare</h4> | |
| <div class="m-3 d-inline"> | |
| <label class="mr-3">Select Fields</label> | |
| <select id="sel-fields" class="c-select sel-medium"></select> | |
| </div> | |
| <div id="comparison-table"></div> | |
| </div><!-- end interface stuff --> | |
| </div> | |
| </div> | |
| </body> | |
| </html> |
| $(async function () { | |
| // init map | |
| var middlePgh = { lat: 40.4479, lng: -79.9491 }; | |
| var mapContainer = document.getElementById('map-container'); | |
| var map = L.map(mapContainer, { center: middlePgh, zoom: 10 }); | |
| // add streets | |
| var layer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map); | |
| layer.addTo(map); | |
| // add regions | |
| var geojson = await fetch('regions.geojson').then(body => body.text()); | |
| // TDD lmao | |
| var error = false; | |
| try { | |
| geojson = JSON.parse(geojson); | |
| } catch (e) { error = true; } | |
| if (error) { | |
| var errorEl = $('#error'); | |
| errorEl.removeClass('hidden'); | |
| errorEl.text('regions is not a json yet'); | |
| return; | |
| } | |
| // proof | |
| alert(geojson); | |
| }); | |
| $(async function () { | |
| // get options and fill in census id/name | |
| var censuses = await fetch('census-files.json').then(body => body.json()); | |
| $('#sel-census-1').select2({ | |
| data: censuses, | |
| placeholder: '', | |
| allowClear: true | |
| }); | |
| $('#sel-census-2').select2({ | |
| data: censuses, | |
| placeholder: '', | |
| allowClear: true | |
| }); | |
| // get options and fill in fields id/name | |
| var fields = await fetch('census-fields.json').then(body => body.json()); | |
| $('#sel-fields').select2({ | |
| data: fields, | |
| multiple: true, | |
| maximumSelectionLength: 10 | |
| }); | |
| /** | |
| * A Note on Schema/Querying here: | |
| * | |
| * I expect that primary entity in DB will be year of census b/c sources. | |
| * | |
| * I expect that there are more fields than max cols for one table, so also | |
| * values table with relationship many-to-one census year & unique in field | |
| * name + year. | |
| * | |
| * In order to get field values, select from years where year is one of the | |
| * two, joining with fields to get the two years' fields. | |
| * | |
| * results look like: | |
| * +------+-------+--------+ | |
| * | year | field | value | | |
| * +------+-------+--------+ | |
| * | 2010 | rent | high | | |
| * | 2010 | ami | low | | |
| * | 2016 | rent | higher | | |
| * | 2016 | ami | high | | |
| * +------+-------+--------+ | |
| * | |
| * the table we want is: | |
| * | |
| * +------+-------+--------+ | |
| * | year | 2010 | 2016 | | |
| * +------+-------+--------+ | |
| * | rent | high | higher | | |
| * | ami | low | high | | |
| * +------+-------+--------+ | |
| * | |
| * This it turns out is weird and made for illegible code | |
| */ | |
| $('#sel-fields').on('change', async function(event) { | |
| // this is just the field names ['rent', 'ami'] | |
| var fields = $('#sel-fields').select2('data') | |
| .map(f => f.text) | |
| .sort(sortLowerCaseStrings); | |
| // this needs to be generated with querystring stuff (maybe $) | |
| var qs = 'region=region1&census1=1&census2=2&field=rent&field=ami'; | |
| var rows = await fetch('field-values.json?' + qs) | |
| .then(body => body.json()); | |
| // adjust for the table we want | |
| // TODO clean this up, i gave up and started making html. | |
| var years = rows.map(r => r.year).filter((v, i, a) => a.indexOf(v) === i); | |
| var trs = fields.map(function (field) { | |
| var rowHtml = '<td>' + field + '</td><td>' + years.map(year => { | |
| // get the value for this year and field | |
| var value = rows | |
| .filter(r => r.year === year && r.field === field).pop(); | |
| // un-necessary check of undefined | |
| return value && value.value | |
| ? value.value | |
| : ''; | |
| }).join('</td><td>') + '</td>'; | |
| return rowHtml; | |
| }).join('</tr><tr>'); | |
| trs = '<tr>' + trs + '</tr>'; | |
| var tableHtml = ` | |
| <table class="mt-3 table table-bordered table-hover"> | |
| <thead> | |
| <tr> | |
| <th>Year</th> | |
| <th>${years[0]}</th> | |
| <th>${years[1]}</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| ${trs} | |
| </tbody> | |
| </table> | |
| `; | |
| // reset and refill table div | |
| $('#comparison-table').html(''); | |
| $('#comparison-table').html(tableHtml); | |
| }); | |
| }); | |
| function sortLowerCaseStrings(a, b) { | |
| a = a.toLowerCase(); | |
| b = b.toLowerCase(); | |
| return a > b ? 1 : b > a ? -1 : 0; | |
| } |
| select.sel-skinny { | |
| width: 100px; | |
| } | |
| select.sel-medium { | |
| width: 50%; | |
| } |