Skip to content

Commit d5e8297

Browse files
committed
Completed Geopoly tutorial doc
1 parent 252c9c1 commit d5e8297

2 files changed

Lines changed: 99 additions & 66 deletions

File tree

sqlite-cloud/_nav.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const sidebarNav: SidebarNavStruct = [
2121
{ title: "PHP / Laravel", filePath: "quick-start-php-laravel", type: "inner", level: 1 },
2222
{ title: "Gin", filePath: "quick-start-gin", type: "inner", level: 1 },
2323
{ title: "Tutorials", type: "inner", level: 0 },
24-
{ title: "SQLite Spotlight: Geopoly Extension", filePath: "tutorial-geopoly", type: "inner", level: 1 },
24+
{ title: "Using SQLite Extensions - Geopoly", filePath: "tutorial-geopoly", type: "inner", level: 1 },
2525

2626
{ title: "Platform", type: "secondary", icon: "docs-plat" },
2727
{ title: "Edge Functions", filePath: "edge-functions", type: "inner", level: 0 },
@@ -143,7 +143,7 @@ const sidebarNav: SidebarNavStruct = [
143143
{ title: "Node.js", ref: "/docs/quick-start-node", type: "inner", level: 2 },
144144
{ title: "Next.js", ref: "/docs/quick-start-next", type: "inner", level: 2 },
145145
{ title: "Tutorials", type: "inner", level: 1 },
146-
{ title: "SQLite Spotlight: Geopoly Extension", ref: "/docs/tutorial-geopoly", type: "inner", level: 2 },
146+
{ title: "Using SQLite Extensions - Geopoly", ref: "/docs/tutorial-geopoly", type: "inner", level: 2 },
147147
{ title: "Classes", type: "inner", level: 1 },
148148
{ title: "Database", filePath: 'sqlite-cloud/sdks/js/classes/Database', type: "inner", level: 2 },
149149
{ title: "SQLiteCloudConnection", filePath: 'sqlite-cloud/sdks/js/classes/SQLiteCloudConnection', type: "inner", level: 2 },

sqlite-cloud/tutorial-geopoly.mdx

Lines changed: 97 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,20 @@
11
---
2-
title: SQLite Spotlight - Geopoly Extension
2+
title: Using SQLite Extensions - Geopoly
33
description: Build a local attractions finder app using SQLite Cloud, SQLite's built-in Geopoly extension, Mapbox, and React.
44
category: getting-started
55
status: publish
66
slug: tutorial-geopoly
77
---
88

9-
This tutorial demonstrates how to build a local attractions finder app. The app uses:
10-
- GeoJSON data
11-
- a SQLite Cloud database
12-
- Mapbox JavaScript Graphics Library (GL JS)
13-
- user has to create an account/ get a token!
14-
- React
15-
- SQLite's built-in Geopoly extension
9+
In this tutorial you will build a local attractions finder map-plication using GeoJSON data, a SQLite Cloud database, [Mapbox GL JS](https://docs.mapbox.com/mapbox-gl-js/guides) (JavaScript Graphics Library), React, and SQLite's built-in [Geopoly extension](https://devdocs.io/sqlite/geopoly).
1610

17-
Time to complete: ~15-20 mins.
11+
**Time to complete: 15-20 mins.**
1812

19-
If you get stuck in the tutorial or prefer to play with the finished product, check out [the example app on GitHub](https://github.com/sqlitecloud/examples/tree/main/geopoly).
13+
If you get stuck in the tutorial or prefer to play with the finished product, check out [the example app on GitHub](https://github.com/sqlitecloud/examples/tree/main/geopoly-demo).
2014

2115
---
2216

23-
1. **Initialize your app**
17+
**1. Initialize your app**
2418
- Create a new directory `sqlc-geopoly-demo`. From this directory, bootstrap a Node.js project.
2519

2620
```bash
@@ -29,11 +23,11 @@ cd sqlc-geopoly-demo
2923
npm init -y
3024
```
3125

32-
2. **Curate your GeoJSON data**
26+
**2. Curate your GeoJSON data**
3327

34-
- We will leverage the [Overpass API](https://wiki.openstreetmap.org/wiki/Overpass_API) (an open-source, read-only API for fetching OpenStreetMap data) to query NYC attractions.
28+
- We will leverage the [Overpass API](https://wiki.openstreetmap.org/wiki/Overpass_API) (an open-source, read-only API for fetching OpenStreetMap data) to query NY attractions.
3529

36-
- Visit [Overpass Turbo](https://overpass-turbo.eu/), the Overpass GUI. Copy and paste in the below query which:
30+
- Visit [Overpass Turbo](https://overpass-turbo.eu/), the Overpass GUI. Copy and paste in the below query, which:
3731
- defines New York as the area of interest;
3832
- fetches nodes in the specified area that are tagged with the keys `amenity`, `historic`, `tourism`, `leisure`, etc.; and
3933
- outputs the data.
@@ -67,9 +61,7 @@ out skel qt;
6761

6862
- Click Export. Under Data, copy the GeoJSON.
6963

70-
- Use [a JSON formatter](https://jsonformatter.org/) to correctly format the copied JSON.
71-
72-
- Back in your project dir, create `data/geodata.json`. Paste the formatted GeoJSON into the file. It should look as follows:
64+
- Back in your project dir, create `data/geodata.json`. Paste the formatted GeoJSON into the file. It should look similar to the following:
7365

7466
```json
7567
{
@@ -110,34 +102,35 @@ out skel qt;
110102
]
111103
}
112104
```
105+
- For this tutorial, we'll use the NY geodata. Once you have the app up-and-running, you can run your own Overpass queries to customize the geodata per your needs. See **Additional Guidance on Overpass** at the end of this tutorial.
113106

114-
- To pull other types of attractions or any other kind of location data in an area of interest to you, refer to [OpenStreetMap's Map features documentation](https://wiki.openstreetmap.org/wiki/Map_features) and modify the above query's area and key-value pairs.
107+
**3. Create a new SQLite Cloud database**
115108

116-
- NOTE: The app currently only works with Point features (represented in the [doc's](https://wiki.openstreetmap.org/wiki/Map_features) Element column by an icon containing a dot), so be sure to query only:
117-
- nodes, and
118-
- the key-value pairs that can return Point data. For example, don't use most values available for the Boundary key.
109+
- If you haven't already, [sign up for a SQLite Cloud account](https://sqlitecloud.io/register) and create a new project.
119110

120-
- To implement more complex or granular Point queries, refer to the [Overpass QL documentation](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL).
111+
- In your account dashboard's left nav, click Databases, then Create Database. Name your new database `geopoly-demo`.
121112

122-
3. **Create a new / empty SQLite Cloud database**
113+
**4. Create a Mapbox account**
123114

124-
- If you haven't already, [sign up for a SQLite Cloud account](https://sqlitecloud.io/register) and create a new project.
115+
- [Sign up](https://account.mapbox.com/auth/signup/) for an Individual Mapbox account. (We'll stay on the free tier.)
125116

126-
- In your account dashboard's left nav, click Databases, then the Create Database button. To minimize code changes, name your new database `geopoly-demo`.
117+
**5. Set your environment variables**
127118

128-
4. **Set an environment variable**
119+
- In your project dir, create a `.env` file.
120+
- This app will use `react-scripts`, which leverages Create React App under-the-hood. Create React App offers built-in support for env vars. You will not need to manually configure `webpack` or another bundler, but all vars will need to be prefixed with `REACT_APP_`.
129121

130-
- In your project dir, create a `.env` file with a new environment variable `REACT_APP_CONNECTION_STRING`. Copy your connection string from your account dashboard and assign it to the variable.
131-
- CONTEXT: This app will use `react-scripts`, which leverages Create React App under-the-hood. Create React App offers built-in support for env vars. You will not need to manually configure `webpack` or another bundler, but all vars must be prefixed with `REACT_APP_`.
122+
- Add 2 env vars to the file:
123+
- `REACT_APP_CONNECTION_STRING`. Copy and paste your connection string from your SQLite Cloud account dashboard.
124+
- `REACT_APP_MAPBOX_TOKEN`. In your Mapbox account dashboard's nav, click Tokens. Copy and paste your default public token.
132125

133-
- Install the SQLite Cloud JS SDK and dotenv package as dependencies:
126+
- Install the SQLite Cloud JS SDK and `dotenv` package as dependencies:
134127

135128
```bash
136129
npm i @sqlitecloud/drivers
137130
npm i -D dotenv
138131
```
139132

140-
5. **Create your DB tables**
133+
**6. Create your database tables**
141134

142135
- In your project dir, create `src/helpers/createDatabase.js`. Copy and paste in the following code:
143136

@@ -175,32 +168,31 @@ createDatabase();
175168
```
176169

177170
- The `createDatabase` function:
178-
- connects to your `geopoly-demo` database
179-
- creates a virtual Geopoly table with 2 columns: `rowid` (which does not display in SQLite Cloud's Console but can be queried) and `_shape`
180-
- creates an `attractions` table with 5 columns: `id`, `name`, `lng`, `lat`, and `coordinates`
181-
- populates the `attractions` table using the GeoJSON FeatureCollection you generated earlier
182-
- closes the database connection.
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`.
183175

184-
- Add the following script to your `package.json`:
176+
- Add the following to your `package.json`:
185177

186178
```json
187179
"scripts": {
188180
"create-tables": "node src/helpers/createDatabase.js"
189-
}
181+
},
182+
"type": "module",
190183
```
191184

192185
- Run `npm run create-tables`.
193186

194-
- The time it will take for the command to finish creating tables and inserting the geodata will depend on the size of your FeatureCollection. The example Overpass query provided above returns ~2000 NYC Point features and takes a couple of minutes.
187+
- The time it will take for the command to finish creating the tables and inserting the geodata will depend on the size of your FeatureCollection. The NY Overpass query returns ~2000 Point features, so row insertion takes a couple of minutes.
195188

196-
- As an aside: To see the inserted attractions geodata, in your SQLite Cloud account dashboard's left nav, click Console. Copy, paste in, and run the following query:
189+
- To see the inserted NY attractions geodata, in your SQLite Cloud account dashboard's left nav, click Console. In the database dropdown, select `geopoly-demo`. Copy, paste in, and run the following query:
197190

198191
```sql
199192
SELECT * FROM attractions ORDER BY id DESC;
200193
```
201-
- If you used the provided Overpass query, ~2000 rows are added.
202194

203-
6. **Setup the frontend**
195+
**7. Set up the frontend**
204196

205197
- In your project dir, create `public/index.html`. Copy and paste in the following code:
206198

@@ -214,7 +206,7 @@ SELECT * FROM attractions ORDER BY id DESC;
214206
name="description"
215207
content="Create a React web app that uses Mapbox GL JS to render a map"
216208
/>
217-
<title>Local Attraction Finder</title>
209+
<title>Local Attractions Finder</title>
218210
</head>
219211
<body>
220212
<noscript>You need to enable JavaScript to run this app.</noscript>
@@ -223,7 +215,7 @@ SELECT * FROM attractions ORDER BY id DESC;
223215
</html>
224216
```
225217

226-
- In the `src` dir, add 3 files: `index.css` (for app styling), `index.js` (the app entrypoint file), and `App.js` (the one-and-only component). Copy and paste in the following code for each file:
218+
- In the `src` dir, add 3 files: `index.css` (for app styling), `index.js` (the app entrypoint file), and `App.js` (the app's sole component). Copy and paste in the following code for each file:
227219

228220
`index.css`
229221
```css
@@ -340,8 +332,8 @@ body {
340332
.marker {
341333
border: none;
342334
cursor: pointer;
343-
width: 26px;
344-
height: 34px;
335+
width: 32px;
336+
height: 40px;
345337
background-image: url('https://docs.mapbox.com/mapbox-gl-js/assets/custom_marker.png');
346338
}
347339

@@ -360,7 +352,7 @@ body {
360352
}
361353
```
362354

363-
- NOTE: To simplify this tutorial, `.marker.background-image` uses a custom Mapbox marker for the map pins marking attractions. [The example app on GitHub](https://github.com/sqlitecloud/examples/tree/main/geopoly) uses a custom marker image included in the repo's `images` dir (excluded here).
355+
- NOTE: To simplify this tutorial, `.marker.background-image` uses a custom Mapbox marker for the pins marking attractions on the map. [The example app on GitHub](https://github.com/sqlitecloud/examples/tree/main/geopoly) uses a custom marker image included in the repo's `images` dir (excluded here).
364356

365357
`index.js`
366358
```js
@@ -389,8 +381,7 @@ import { point, distance } from '@turf/turf';
389381
import { Database } from '@sqlitecloud/drivers';
390382
import { getBbox } from './helpers/getBbox.js';
391383

392-
mapboxgl.accessToken =
393-
'pk.eyJ1IjoidW5hdGFyYWphbiIsImEiOiJjbDFpcW82MGYxeDE1M2RwNjU4MmZ1YndsIn0.HyxwEtZz-pQ_7R6e48l0-g';
384+
mapboxgl.accessToken = process.env.REACT_APP_MAPBOX_TOKEN;
394385

395386
function App() {
396387
const mapContainerRef = useRef();
@@ -682,9 +673,29 @@ function App() {
682673
export default App;
683674
```
684675

685-
- To understand how the app works, let's break down this monster component:
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.
686697

687-
7. **Create the last helper function**
698+
**8. Create a helper function**
688699

689700
- Create `src/helpers/getBbox.js`. Copy and paste in the following code:
690701

@@ -723,13 +734,17 @@ export function getBbox(sortedEvents, locationLng, locationLat) {
723734
}
724735
```
725736

726-
8. **Run your app!**
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+
741+
**9. Run your app!**
727742

728743
- Replace your `package.json` code with the following, which includes all dependencies needed to run the app:
729744

730745
```json
731746
{
732-
"name": "geopoly-demo",
747+
"name": "sqlc-geopoly-demo",
733748
"version": "1.0.0",
734749
"private": true,
735750
"description": "",
@@ -774,28 +789,46 @@ npm i
774789
npm start
775790
```
776791

777-
- Visit `http://localhost:3000/` (adjust the port as-needed) in your browser to view the application.
792+
- Visit `http://localhost:3000/` (adjust the port as-needed) in your browser to view the app.
778793

779-
9. **Find attractions near you!**
794+
**10. Find attractions!**
780795

781-
- USE CASE 1: You used the NYC geodata fetched with the provided Overpass query.
796+
- On app load, the map is centered on Central Park, NY.
782797

783-
- On app load, the map is centered on Central Park, NYC.
798+
- In the geocoder (i.e. search input) at the top right of the map, enter "Empire" and click on the "Empire State Building" result. You can also search coordinates (see [reverse geocoding](https://docs.mapbox.com/api/search/geocoding/)).
784799

785-
- In the geocoder (i.e. search input) at the top right of the map, enter "Empire" and select the "Empire State Building" search result.
800+
- When you select a geocoder result:
801+
- a polygon is generated by Geopoly, added to your `polygons` table, and displayed on the map; and
786802

787-
- When you select a geocoder result:
788-
- a polygon is generated by Geopoly, added to your `polygons` table, and displayed on the map.
803+
- all attractions in your `attractions` table inside the polygon area are listed in the left sidebar AND marked on the map. NOTE: the sidebar upsorts attractions nearest your searched location, in this case the "Empire State Building".
789804

790-
- all attractions from your `attractions` table calculated to be inside the polygon area (outline inclusive) are listed in the left sidebar AND marked on the map. NOTE: the sidebar upsorts attractions nearest the searched location.
805+
- To see the inserted polygon data, in your SQLite Cloud account dashboard's left nav, click Console. In the database dropdown, select `geopoly-demo`. Copy, paste in, and run the following query.
791806

792-
- As an aside: To see the inserted polygon data, in your SQLite Cloud account dashboard's left nav, click Console. Copy, paste in, and run the following query:
807+
- The `geopoly_json` function parses the `_shape` column's `[object ArrayBuffer]` data into an array of coordinate pairs representing the polygon's vertices: `[[-73.9355,40.7485],[-73.9359,40.7547], ...,[-73.9359,40.7422],[-73.9355,40.7485]]`. The array should contain (1 + # of polygon sides) coordinate pairs. The polygon is closed, so the first and last pairs both represent the same vertex.
793808

794809
```sql
795810
SELECT rowid, geopoly_json(_shape) FROM polygons;
796811
```
797-
- `geopoly_json()` parses the Array(buffer) data into an array of coordinate pairs representing the polygon vertices. The array should contain (1 + # of polygon sides) coordinate pairs.
798812

799-
- The map will zoom in to the closest attraction to your searched location, in this case the "Empire State Building". However, you can click any attraction listing or marker to zoom in to that attraction on the map.
813+
- The map zooms in to the nearest attraction to your searched location and highlights its corresponding top listing in the sidebar.
814+
815+
- You can click on any attraction listing or marker to fly/ zoom to and center on that attraction on the map.
816+
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.
818+
819+
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.
820+
821+
### Additional Guidance on Overpass:
822+
- 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.
800823

801-
- USE CASE 2: If you used a custom Overpass query to fetch geodata near your location, then after the map loads, click on the map's GeolocateControl and allow the browser to use your IP to locate you on the map. The map will fly to and center on your location. This way you will get more relevant results from the geocoder search and your polygon will contain (more) attractions.
824+
- 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.
825+
826+
- To implement more complex or granular Point queries, refer to the [Overpass QL documentation](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL).
827+
828+
- If you run a custom Overpass query:
829+
- Add to or replace the FeatureCollection in `geodata.json`.
830+
- In your SQLite Cloud account dashboard's left nav, click Databases. In the `geopoly-demo` row, click the down chevron and then Delete Database.
831+
- Create Database with the same name.
832+
- From your project dir, run `npm run create-tables`. Your database tables will be re-created, and the `attractions` table will be populated with your updated geodata.
833+
834+
- If you queried and stored attractions near your location, then after the app's initial load, click on the [GeolocateControl icon](https://docs.mapbox.com/mapbox-gl-js/example/locate-user/) at the top right of the map and allow the browser to quickly center the map on your location. Search away!

0 commit comments

Comments
 (0)