Ok, so still not quite on par with the amazing
<Algolia-Places>
...
re:places is a serverless database of 41,000 global cities for your browser. Designed as a light-weight polyfill for ‘cities’ from Algolia's Places API, ahead of the service’s sunset in May 2022. It also runs standalone.
Try it out: Search a city [demo]
Install
- Faster than you need; a round-trip query averages 20-30ms1
- Lazy-load the global cities database in under 400kB2 over the wire
- Filter by country to optimise file size: France weighs in at 20kB2 and Australia comes in under 5kB2
- Filter results based on a user's
IP
, proximity to alat,lng
, or the area inside apolygon
orbounding box
- Standalone, or as a zero-config proxy to extend the life of Algolia's Places.js (beyond their API shutdown)
- Entirely static, nothing required beyond your favourite CDN or storage bucket
- Entirely free and open source (code is MIT licensed, database is CC BY 4.0)3
- Contributions celebrated 🎉
re:places.js is currently intended to be installed from a CDN, with the exception of the polyfill (service worker) script. While modules are in use, they rely on remote imports. This is primarily because the library was coded by hand, without a package manager, and so doesn't have any build steps that might facilitate different versions (UMD, local es6 modules, etc.) Future versions should resolve this shortcoming and provide for a straightforward npm i
process. That said, porting the library to run local is trivial.
Standalone install [example]
- Import re.places.js into your script (i.e. a module) or document
import replaces from 'https://cdn.jsdelivr.net/npm/re.places.js@0.1.3/re.places.js'
<script type="module" async src="https://cdn.jsdelivr.net/npm/re.places.js@0.1.3/re.places.js?global"></script>
- Search a city:
// this downloads the Australia database (5kb) and runs a search
replaces.search({
query: 'Cairns',
countries: ['au'],
})
.then(e => console.log(e));
// you can provide default options that apply to every search
// refer to the algolia documentation for more info:
// community.algolia.com/places/documentation.html#reconfigurable-options
replaces.init({
// preload the database
preload: true,
// limit it to France and Australia ~= 26Kb download
countries: ['au', 'fr'],
// by default we weight results by proximity to an IP location
// (or at least Algolia do - this library limits it to country)
// you can turn that off here .. but it's not great for user experience
aroundLatLngViaIP: false,
// specify a geo location and distance (in meters), instead of using the IP
aroundLatLng: '-16, 145',
aroundRadius: 3000000, // =300km
// any results outside this bounding box are excluded
insideBoundingBox: '-10,154,-29,138',
// any results outside this polygon are excluded
insidePolygon: '-10,138,-10,154,-29,154,-34,152,-34,141,-29,138',
})
// any options you provide to a search are merged with your default options
const result = await replaces.search({
query: 'ca',
// for this search we want to weight by IP location (even though we turned it off above)
aroundLatLngViaIP: true,
});
Polyfill Algolia Places API [example]
- Download re.places.algolia.js and place the file at the root of your app
- Import the library directly after Algolia Places.js, this will install a service worker to intercept any API calls
import 'https://cdn.jsdelivr.net/npm/places.js@1.19.0'
// import directly after places.js
import '/re.places.algolia.js'
- That's it. Test the <input> your previously set up for places.js
The above install will throw an error where a service worker is already installed on a domain. To resolve, import re.places.algolia.sw.js at the top of the existing service worker script. Re.Places only intercepts calls to the Algolia API and does not use any caching strategy. Imported at the top of your script it will work in tandem with any logic already in place.
- Edit your main service worker to add the following line to the top of the file
self.importScripts('https://cdn.jsdelivr.net/npm/re.places.js@0.1.3/src/re.places.algolia.sw.js')
// YOUR SERVICE WORKER LOGIC GOES HERE
To self-host the library, you can simply switch out the CDN hosted libraries for local versions. This involves ensuring you have a local copy of each script required for your setup, and replacing any remote urls with your local path. The library was originally built for local use, but later adapted to use a CDN due to the nature of the beast. Future iterations will allow for either approach.
- Download the repository or create a local fork
npm install
- build the database
npm run build-db
- search and replace all the remote imports with local paths (we use
cdn.jsdelivr.net
and all files should be in/
,/src
and/demo
) - test the demo pages
npm run demo
(you'll need HTTPS on localhost)
Check out the source of the demo pages for more detailed explanations of each setup. The scripts themselves are also commented, but there's no guarantees as to coherence. Please feel free to submit an issue or PR to resolve any confusion. Thanks for reading!
re:places © 2022 theprojectsomething | MIT license | Github | Support the project
Hand made with 🖤 in Cairns, Australia
Footnotes
-
In Cairns, Australia, queries to the Algolia Places API average 250ms (and it feels nice!). With re:places the initial query will run slower due to lazy-loading the database (and an actual network connection being required!) ¯\(ツ)/¯ ... after that you're latency free. See the advanced setup for pre-loading instructions. ↩
-
The included build script generates a brotli compressed global database weighing in at 396kB, useful for self hosting. CDN compression varies, e.g. ~480kB gzip from JSDelivr / 508kB br from unpkg ↩ ↩2 ↩3
-
re:places depends on @lucaong/MiniSearch and @rowanwins/point-in-polygon-hao (both MIT / 0 dependencies).
The database is derived from the basic World Cities Database available from SimpleMaps.com (CC BY 4.0).
Ideas mixed in from @turfjs and this stackoverflow answer. Cloudflare's 1.1.1.1 supplies location hints.
Informing everything, of course, is the masterwork that is Algolia Places.
All amazing efforts. Thank you so much. ↩