Skip to content

Commit 0a7f147

Browse files
committed
Shifted code explanations to be comments
1 parent 6112531 commit 0a7f147

File tree

1 file changed

+29
-36
lines changed

1 file changed

+29
-36
lines changed

sqlite-cloud/tutorial-geopoly.mdx

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,19 @@ import 'dotenv/config';
140140
import geodata from '../../data/geodata.json' assert { type: 'json' };
141141

142142
async function createDatabase() {
143+
// open a connection to your `geopoly-demo` database
143144
const db = new Database(process.env.REACT_APP_CONNECTION_STRING);
144145

145146
const db_name = 'geopoly-demo';
146147
await db.sql`USE DATABASE ${db_name};`;
147148

149+
// create a table with 2 columns: `rowid` and `_shape`
148150
await db.sql`CREATE VIRTUAL TABLE polygons USING geopoly()`;
149151

152+
// create a table with 5 columns: `id`, `name`, `lng`, `lat`, and `coordinates`
150153
await db.sql`CREATE TABLE attractions (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, lng REAL NOT NULL, lat REAL NOT NULL, coordinates TEXT NOT NULL)`;
151154

155+
// populate the `attractions` table using the GeoJSON FeatureCollection in `geodata.json`
152156
for (const feature of geodata['features']) {
153157
const { name } = feature.properties;
154158
const { coordinates } = feature.geometry;
@@ -166,12 +170,6 @@ async function createDatabase() {
166170

167171
createDatabase();
168172
```
169-
170-
- The `createDatabase` function:
171-
- connects to your `geopoly-demo` database;
172-
- creates a virtual `polygons` table with 2 columns: `rowid` and `_shape`;
173-
- creates an `attractions` table with 5 columns: `id`, `name`, `lng`, `lat`, and `coordinates`; and
174-
- populates the `attractions` table using the GeoJSON FeatureCollection in `geodata.json`.
175173

176174
- Add the following to your `package.json`:
177175

@@ -397,21 +395,25 @@ function App() {
397395
const units = 'miles';
398396

399397
async function queryGeopoly(searchedLng, searchedLat) {
398+
// open a connection to your `geopoly-demo` database
400399
const db = new Database(process.env.REACT_APP_CONNECTION_STRING);
401400

402401
const db_name = 'geopoly-demo';
403402

404-
const radius = 0.05;
405-
const sides = 50;
403+
const radius = 0.05; // must be a positive number
404+
const sides = 50; // 3-1000
406405

406+
// generate a new polygon to be added to your `polygons` table
407407
const polygonCoords =
408408
await db.sql`USE DATABASE ${db_name}; INSERT INTO polygons(_shape) VALUES(geopoly_regular(${searchedLng}, ${searchedLat}, ${radius}, ${sides})) RETURNING geopoly_json(_shape);`;
409409

410+
// point-in-polygon query to get all attractions in the generated polygon's area
410411
const attractionsInPolygon =
411412
await db.sql`USE DATABASE ${db_name}; SELECT name, coordinates FROM attractions WHERE geopoly_contains_point(${polygonCoords[0]['geopoly_json(_shape)']}, lng, lat);`;
412413

413414
db.close();
414415

416+
// remove unnamed attractions
415417
const namedAttractions = attractionsInPolygon.filter(
416418
(attraction) => attraction.name !== null
417419
);
@@ -428,16 +430,18 @@ function App() {
428430
properties: {
429431
id: index,
430432
title: attraction['name'],
433+
// use Turf.js to calculate the distance between the searched location and the current attraction
431434
distance: distance(
432435
point([searchedLng, searchedLat]),
433436
point(attractionCoordinates),
434437
{
435-
units,
438+
units, // either miles or kilometers
436439
}
437440
),
438441
},
439442
};
440443

444+
// apply clickable markers for all attractions
441445
const marker = document.createElement('div');
442446
marker.key = `marker-${attractionFeature.properties.id}`;
443447
marker.id = `marker-${attractionFeature.properties.id}`;
@@ -454,6 +458,7 @@ function App() {
454458
return attractionFeature;
455459
});
456460

461+
// upsort attractions nearest the user's searched location
457462
attractionFeatures.sort((a, b) => {
458463
if (a.properties.distance > b.properties.distance) {
459464
return 1;
@@ -466,6 +471,7 @@ function App() {
466471

467472
setPlaces(attractionFeatures);
468473

474+
// use a helper function (defined in the next step) to fit/ zoom the map view to the searched location and its nearest attraction
469475
if (attractionFeatures[0]) {
470476
const bbox = getBbox(attractionFeatures, searchedLng, searchedLat);
471477
mapRef.current.fitBounds(bbox, {
@@ -484,6 +490,7 @@ function App() {
484490
.addTo(mapRef.current);
485491
}
486492

493+
// update the `geometry` state to hold the returned Polygon and attraction Point features
487494
setGeometry([
488495
{
489496
type: 'Feature',
@@ -566,23 +573,28 @@ function App() {
566573
}
567574

568575
useEffect(() => {
576+
// create, style, and center the map
569577
mapRef.current = new mapboxgl.Map({
570578
container: mapContainerRef.current,
571579
style: 'mapbox://styles/mapbox/streets-v12',
572580
center: [lng, lat],
573581
zoom,
574582
});
575583

584+
// apply 3 controls to the top right of the map
585+
// an address search input
576586
const geocoder = new MapboxGeocoder({
577587
accessToken: mapboxgl.accessToken,
578588
mapboxgl,
579589
zoom: 12,
580590
});
581591

592+
// toggle fullscreen mode
582593
const fullscreenCtrl = new mapboxgl.FullscreenControl({
583594
container: mapContainerRef.current,
584595
});
585596

597+
// locate the user on the map
586598
const geolocateCtrl = new mapboxgl.GeolocateControl({
587599
fitBoundsOptions: {
588600
maxZoom: 12,
@@ -597,6 +609,7 @@ function App() {
597609
mapRef.current.addControl(fullscreenCtrl);
598610
mapRef.current.addControl(geolocateCtrl);
599611

612+
// track the map center coordinates and zoom level (displayed on the top left of the app)
600613
function updateCoordinates() {
601614
const { lng, lat } = mapRef.current.getCenter();
602615
setLng(lng.toFixed(4));
@@ -606,6 +619,7 @@ function App() {
606619

607620
mapRef.current.on('move', updateCoordinates);
608621

622+
// call the `queryGeopoly` function when the user clicks a geocoder result
609623
geocoder.on('result', (e) => {
610624
const existingMarkers = document.getElementsByClassName('marker');
611625
while (existingMarkers[0]) {
@@ -630,8 +644,10 @@ function App() {
630644
};
631645
}, []); // eslint-disable-line react-hooks/exhaustive-deps
632646

647+
// triggered by a `geometry` state update
633648
useEffect(() => {
634649
if (geometry.length !== 0) {
650+
// draw the returned Polygon, its outline, and attraction Points on the map
635651
drawFeatureCollection();
636652
}
637653
}, [geometry]); // eslint-disable-line react-hooks/exhaustive-deps
@@ -673,28 +689,6 @@ function App() {
673689
export default App;
674690
```
675691

676-
- To understand how the app works, let's break down this monster component's core functionality:
677-
678-
- The first `useEffect`:
679-
- creates, styles, and centers the map;
680-
- applies 3 controls to the top right of the map: a geocoder (i.e. address search input), an icon to toggle the map to fullscreen mode, and an icon to locate the app user on the map;
681-
- sets an event listener to track the map center coordinates and zoom level, all displayed on the top left of the app; and
682-
- sets an event listener on the geocoder to call the `queryGeopoly` function when the user clicks a geocoder result.
683-
684-
- The `queryGeopoly` function:
685-
- connects to your `geopoly-demo` database;
686-
- uses the `geopoly_regular` function to generate a polygon and adds it to the `polygons` table (set a positive `radius` and up to 1000 `sides`);
687-
- returns all named attractions in the polygon area;
688-
- uses Turf.js to calculate the distance between the searched location and each attraction (set `units` to be miles or kilometers);
689-
- applies clickable markers for all attractions on the map;
690-
- upsorts attractions nearest the user's searched location;
691-
- uses the `getBbox` helper function (defined in the next step) to zoom the map to the attraction nearest the searched location; and
692-
- updates the `geometry` state to hold the returned Polygon and attraction Point features.
693-
694-
- The second `useEffect`:
695-
- is triggered by the `geometry` state update; and
696-
- calls the `drawFeatureCollection` function to draw the returned Polygon, its outline, and attraction Points on the map.
697-
698692
**8. Create a helper function**
699693

700694
- Create `src/helpers/getBbox.js`. Copy and paste in the following code:
@@ -727,17 +721,15 @@ export function getBbox(sortedEvents, locationLng, locationLat) {
727721
}
728722
return 0;
729723
});
724+
725+
// return a bounding box, defined by a southwest coordinate pair and northeast coordinate pair
730726
return [
731727
[sortedLons[0], sortedLats[0]],
732728
[sortedLons[1], sortedLats[1]],
733729
];
734730
}
735731
```
736732

737-
- The `getBbox` function:
738-
- generates a bounding box, defined by a southwest coordinate pair and northeast coordinate pair; and
739-
- is used in `queryGeopoly` to fit/ zoom the map view to a user's searched location and its nearest attraction.
740-
741733
**9. Run your app!**
742734

743735
- Replace your `package.json` code with the following, which includes all dependencies needed to run the app:
@@ -814,11 +806,12 @@ SELECT rowid, geopoly_json(_shape) FROM polygons;
814806

815807
- You can click on any attraction listing or marker to fly/ zoom to and center on that attraction on the map.
816808

817-
- Turf.js calculates straight-line distances between your searched location and each attraction. Expect discrepancies between this app's calculated distances vs, say, Google or Apple Maps.
809+
- [Turf.js uses the Haversine formula](https://turfjs.org/docs/api/distance) to account for global curvature when calculating the distance between your searched location and each attraction. However, you should still expect discrepancies between this app's calculated distances vs, say, Google or Apple Maps.
818810

819811
And that’s it! You’ve successfully built a local attractions finder app that utilizes Geopoly to write geodata to and read from a SQLite Cloud database.
820812

821813
### Additional Guidance on Overpass:
814+
822815
- To fetch other attractions or any other kind of location data in NY or another area of interest to you, refer to [OpenStreetMap's Map features documentation](https://wiki.openstreetmap.org/wiki/Map_features). As a starting point, modify the area or key-value pairs in the NY query.
823816

824817
- NOTE: The app works only with Point features (represented in the [Map features](https://wiki.openstreetmap.org/wiki/Map_features) tables' `Element` columns by an icon with a single dot). Be sure to query only nodes and the key-value pairs that can return Point data. For example, don't use most of the values available for the Boundary key.

0 commit comments

Comments
 (0)