Compare commits

..

3 commits

2 changed files with 33 additions and 9 deletions

View file

@ -34,8 +34,8 @@ for state in states[:51]: # Just the 50 and DC, not Guam/American Samoa/PR/etc
for feature in data['features']: for feature in data['features']:
# {"type": "Feature", "properties": {"STATEFP": "01", "PLACEFP": "02260", "PLACENS": "02405163", "GEOID": "0102260", "NAME": "Ardmore", "NAMELSAD": "Ardmore town", "LSAD": "43", "CLASSFP": "C1", "PCICBSA": "N", "PCINECTA": "N", "MTFCC": "G4110", "FUNCSTAT": "A", "ALAND": 5289895, "AWATER": 21830, "INTPTLAT": "+34.9878376", "INTPTLON": "-086.8290225"}, "geometry": {"type": "Polygon", "coordinates": [[[-86.856689, 34.992046], [-86.855354, 34.992044], [-86.855101, 34.99204] # {"type": "Feature", "properties": {"STATEFP": "01", "PLACEFP": "02260", "PLACENS": "02405163", "GEOID": "0102260", "NAME": "Ardmore", "NAMELSAD": "Ardmore town", "LSAD": "43", "CLASSFP": "C1", "PCICBSA": "N", "PCINECTA": "N", "MTFCC": "G4110", "FUNCSTAT": "A", "ALAND": 5289895, "AWATER": 21830, "INTPTLAT": "+34.9878376", "INTPTLON": "-086.8290225"}, "geometry": {"type": "Polygon", "coordinates": [[[-86.856689, 34.992046], [-86.855354, 34.992044], [-86.855101, 34.99204]
state_place = (feature['properties']['STATEFP'], feature['properties']['PLACEFP']) state_place = (feature['properties']['STATEFP'], feature['properties']['PLACEFP'])
lat_lon = (float(feature['properties']['INTPTLAT']), float(feature['properties']['INTPTLON'])) lon_lat = (float(feature['properties']['INTPTLON']), float(feature['properties']['INTPTLAT']))
place_locations[state_place] = lat_lon place_locations[state_place] = lon_lat
print("done") print("done")
print("Fetching population data for all states…", flush=True, end="") print("Fetching population data for all states…", flush=True, end="")

View file

@ -61,6 +61,9 @@
</div> </div>
<div class="col-8 h-100"> <div class="col-8 h-100">
<canvas id="canvas" class="mx-3 my-auto" @vue:mounted="draw" width="800" height="600"></canvas> <canvas id="canvas" class="mx-3 my-auto" @vue:mounted="draw" width="800" height="600"></canvas>
<div>
<input type="checkbox" v-model="show_unguessed_cities" @change="draw"> Show unguessed cities
</div>
</div> </div>
</div> </div>
</div> </div>
@ -126,7 +129,7 @@
const cities = await response.json(); const cities = await response.json();
await state_shape_data_request; // We can't draw until this is in await state_shape_data_request; // We can't draw until this is in
const state_shape = state_shape_data.features.find(f => f.properties.NAME.toLowerCase() == stateName.toLowerCase()); const state_shape = state_shape_data.features.find(f => f.properties.NAME.toLowerCase() == stateName.toLowerCase());
const state_bounds = find_bounds(state_shape.geometry.coordinates[0]); const state_mercator_adjusted_bounds = find_bounds(state_shape.geometry.coordinates[0].map(mercator));
if (!state_shape) { if (!state_shape) {
console.error("Unable to find state in shapes"); console.error("Unable to find state in shapes");
} }
@ -137,6 +140,7 @@
simplified_cities: cities.map(city => simplify(city.name)), simplified_cities: cities.map(city => simplify(city.name)),
city_guess: "", city_guess: "",
message: "", message: "",
show_unguessed_cities: true,
achievements: { achievements: {
"Top Five": cities => cities.slice(0, 5), "Top Five": cities => cities.slice(0, 5),
"Top Ten": cities => cities.slice(0, 10), "Top Ten": cities => cities.slice(0, 10),
@ -169,14 +173,21 @@
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.clearRect(0, 0, canvas.width, canvas.height);
const [minx, maxx, miny, maxy] = state_mercator_adjusted_bounds;
const height_scale = (canvas.height - 8) / (maxy - miny);
const width_scale = (canvas.width - 8) / (maxx - minx);
const scale = Math.min(height_scale, width_scale);
const y_offset = miny - (canvas.height / scale - (maxy - miny)) / 2;
const x_offset = minx - (canvas.width / scale - (maxx - minx)) / 2;
function transform(pt) { function transform(pt) {
// TODO: this just works for MN, and not very well at that pt = mercator(pt)
return [(pt[0]+98)*80, 600-(pt[1]-42)*80]; return [scale * (pt[0] - x_offset), canvas.height - (scale * (pt[1] - y_offset))];
} }
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(...transform(state_shape.geometry.coordinates[0][0])) ctx.moveTo(...transform(state_shape.geometry.coordinates[0][0]))
console.log(transform(state_shape.geometry.coordinates[0][0]))
for (let coord of state_shape.geometry.coordinates[0].slice(1)) { for (let coord of state_shape.geometry.coordinates[0].slice(1)) {
ctx.lineTo(...transform(coord)); ctx.lineTo(...transform(coord));
} }
@ -184,16 +195,29 @@
for (let city of cities) { for (let city of cities) {
if (city.guessed) { if (city.guessed) {
ctx.beginPath(); ctx.beginPath();
const c = transform([city.location[1], city.location[0]]) const c = transform(city.location)
console.log(city.location, c) ctx.fillStyle = "black";
ctx.arc(c[0], c[1], 2, 0, 2*Math.PI, true);
} else if (this.show_unguessed_cities) {
ctx.beginPath();
const c = transform(city.location)
ctx.fillStyle = "lightgray";
ctx.arc(c[0], c[1], 2, 0, 2*Math.PI, true); ctx.arc(c[0], c[1], 2, 0, 2*Math.PI, true);
ctx.fill();
} }
ctx.fill();
} }
}, },
}).mount(); }).mount();
} }
function mercator(pt) {
const radians_from_eq = Math.abs(Math.PI/180 * pt[1]);
// https://en.wikipedia.org/wiki/Mercator_projection#Derivation
const y_radians = Math.log(Math.tan(Math.PI/4 + radians_from_eq/2));
const y_degrees = 180/Math.PI * y_radians;
return [pt[0], y_degrees];
}
// returns minx, maxx, miny, maxy // returns minx, maxx, miny, maxy
function find_bounds(coords) { function find_bounds(coords) {
return [ return [