Giter VIP home page Giter VIP logo

placekey-py's Introduction

Placekey-py

PyPI version PyPI downloads version

A Python library for working with Placekeys. Documentation for this package can be found here, and documentation for the Placekey service API can be found here. The Plackey design specification is available here. The details in Placekey encoding is here. We welcome your feedback.

Installation

This package can be installed from PyPI by

pip install placekey

MacOS Big Sur may need to run brew install geos if the installation of the shapely dependency fails.

Usage

The basic functionality of the Placekey library is conversion between Placekeys and latitude-longitude coordinates.

>>> import placekey as pk
>>> lat, long = 0.0, 0.0
>>> pk.geo_to_placekey(lat, long)
'@dvt-smp-tvz'
>>> pk.placekey_to_geo('@dvt-smp-tvz')
(0.00018033323813810344, -0.00018985758738881587)

The library also allows for conversion between Placekeys and H3 indices.

>>> pk.placekey_to_h3('@dvt-smp-tvz')
'8a754e64992ffff'
>>> pk.h3_to_placekey('8a754e64992ffff')
'@dvt-smp-tvz'

The distance in meters between two Placekeys can be found with the following function.

>>> pk.placekey_distance('@dvt-smp-tvz', '@5vg-7gq-tjv')
12795124.895573696

An upper bound on the maximal distance in meters between two Placekeys based on the length of their shared prefix is provided by placekey.get_prefix_distance_dict().

>>> pk.get_prefix_distance_dict()
{0: 20040000.0,
 1: 20040000.0,
 2: 2777000.0,
 3: 1065000.0,
 4: 152400.0,
 5: 21770.0,
 6: 8227.0,
 7: 1176.0,
 8: 444.3,
 9: 63.47}

Placekeys found in a data set can be partially validated by

>>> pk.placekey_format_is_valid('222-227@dvt-smp-tvz')
True
>>> pk.placekey_format_is_valid('@123-456-789')
False

API Client

This package also includes a client for the Placekey API. The methods in the client are automatically rate limited.

>>> from placekey.api import PlacekeyAPI
>>> placekey_api_key = "..."
>>> pk_api = PlacekeyAPI(placekey_api_key)

The PlacekeyAPI.lookup_placekey method can be used to lookup the Placekey for a single place.

>>> pk_api.lookup_placekey(latitude=37.7371, longitude=-122.44283)
{'query_id': '0', 'placekey': '@5vg-82n-kzz'}
>>> place = {
>>>   "location_name": "Twin Peaks Petroleum",
>>>   "street_address": "598 Portola Dr",
>>>   "city": "San Francisco",
>>>   "region": "CA",
>>>   "postal_code": "94131",
>>>   "iso_country_code": "US"
>>> }
>>> pk_api.lookup_placekey(**place, fields=["building_placekey","address_placekey"])
{'query_id': '0',
 'placekey': '227-223@5vg-82n-pgk',
 'address_placekey': '227@5vg-82n-pgk',
 'building_placekey': '227@5vg-82n-pgk'}

The PlacekeyAPI.lookup_placekeys method can be used to lookup Placekeys for multiple places.

>>> places = [
>>>   {
>>>     "street_address": "1543 Mission Street, Floor 3",
>>>     "city": "San Francisco",
>>>     "region": "CA",
>>>     "postal_code": "94105",
>>>     "iso_country_code": "US"
>>>   },
>>>   {
>>>     "query_id": "thisqueryidaloneiscustom",
>>>     "location_name": "Twin Peaks Petroleum",
>>>     "street_address": "598 Portola Dr",
>>>     "city": "San Francisco",
>>>     "region": "CA",
>>>     "postal_code": "94131",
>>>     "iso_country_code": "US"
>>>   },
>>>   {
>>>     "latitude": 37.7371,
>>>     "longitude": -122.44283
>>>   }
>>> ]
>>> pk_api.lookup_placekeys(places)
[{'query_id': 'place_0', 'placekey': '226@5vg-7gq-5mk'},
 {'query_id': 'thisqueryidaloneiscustom', 'placekey': '227-222@5vg-82n-pgk'},
 {'query_id': 'place_2', 'placekey': '@5vg-82n-kzz'}]

Full details on how to query the API and how to get an API key can be found here.

Notebooks

Jupyter notebooks demonstrating various Placekey functionality are contained in the placekey-notebooks repository.

Support

This package runs on Python 3.

placekey-py's People

Contributors

daniel-vetter-coverwhale avatar felixsafegraph avatar jarredsafegraph avatar medhatgayed avatar rmtrmt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

placekey-py's Issues

placekey_format_is_valid function fails when placekey has multiple @ symbols

When placekey contains invalid value with more than one @ symbol, placekey_format_is_valid function throws a ValueError

In [2]: import placekey as pk

In [3]: pk.placekey_format_is_valid("5yv-j89-g6k")
Out[3]: True

In [4]: pk.placekey_format_is_valid("@@5yv-j89-g6k")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In [4], line 1
----> 1 pk.placekey_format_is_valid("@@5yv-j89-g6k")

File ~/.virtualenvs/geocode-api/lib/python3.8/site-packages/placekey/placekey.py:338, in placekey_format_is_valid(placekey)
    329 def placekey_format_is_valid(placekey):
    330     """
    331     Boolean for whether or not the format of a Placekey is valid, including
    332     checks for valid encoding of location.
   (...)
    336 
    337     """
--> 338     what, where = _parse_placekey(placekey)
    340     if what:
    341         return _where_part_is_valid(where) and bool(WHAT_REGEX.match(what))

File ~/.virtualenvs/geocode-api/lib/python3.8/site-packages/placekey/placekey.py:369, in _parse_placekey(placekey)
    361 """
    362 Split a Placekey in to what and where parts.
    363 
   (...)
    366 
    367 """
    368 if '@' in placekey:
--> 369     what, where = placekey.split('@')
    370 else:
    371     what, where = None, placekey

ValueError: too many values to unpack (expected 2)

Possible issue with PyPi upload (package does not include several needed packages in requirements)

First of all, thanks for building such a great tool for referencing the identity and location in space of different entities!

There seems to a mismatch between the requirements.txt file in the current GitHub repo (which works fine when installed to my machine) and the requires.txt file included in the source code of the PlaceKey-py package posted on PyPI.

The GitHub requirements.txt includes requests, ratelimit, backoff, all of which are required to have the main contents of the API submodule work correctly. Thus, when I currently download the package from PyPi and try to call the API submodule, it immediately fails owing to not having downloaded ratelimit as well.

Just wanted to bring this to your attention in case it wasn't intention -- thanks for reading!

Possible Infinite Loop

while True:

Our account/apikey was locked out today due to making too many requests in 1 day. We then started seeing some of our applications hang. We traced it down to the line of code referenced above. It seems to go into an infinite loop for as long as 429 response code is returned. If our account is locked out for 1 day, for example, that means this code loops continually making calls to placekey for maybe 1 day (not sure how you guys perform your rate limiting calculations). For example, if we make our entire day's allocations of calls in 1 hour, then we would have to wait 23 hours until our account is unlocked which means this code will loop for 23 hours. A library should not hang for 23 hours so I think this is a bug.

Cryptic error message when HTTP 504 error code is received

The issue

When a response with an error status code other than 429 is received (like 504), a cryptic error message about parsing the results is printed to the console.

Details

Currently when I use the following code to make a couple hundred requests, I get the following error after about 1 minute of spinning:

pk_api.lookup_placekeys(some_queries)
JSONDecodeError: Expecting value: line 1 column 1 (char 0)

With the following stack trace:

----> pk_api.lookup_placekeys(query)

/.../python3.7/site-packages/placekey/api.py in lookup_placekeys(self, places, strict_address_match, strict_name_match, batch_size, verbose)
    200                     places[i:max_batch_idx],
    201                     strict_address_match=strict_address_match,
--> 202                     strict_name_match=strict_name_match
    203                 )
    204             except RateLimitException:

/.../python3.7/site-packages/placekey/api.py in lookup_batch(self, places, strict_address_match, strict_name_match)
    270                 break
    271 
--> 272         return json.loads(result.text)
    273 
    274     def _validate_query(self, query_dict):

/.../python3.7/json/__init__.py in loads(s, encoding, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    346             parse_int is None and parse_float is None and
    347             parse_constant is None and object_pairs_hook is None and not kw):
--> 348         return _default_decoder.decode(s)
    349     if cls is None:
    350         cls = JSONDecoder

/.../python3.7/json/decoder.py in decode(self, s, _w)
    335 
    336         """
--> 337         obj, end = self.raw_decode(s, idx=_w(s, 0).end())
    338         end = _w(s, end).end()
    339         if end != len(s):

/.../python3.7/json/decoder.py in raw_decode(self, s, idx)
    353             obj, end = self.scan_once(s, idx)
    354         except StopIteration as err:
--> 355             raise JSONDecodeError("Expecting value", s, err.value) from None
    356         return obj, end

This was quite puzzling, especially since the exact same code worked just 30 minutes prior (with the exact same queries, too).

It seemed obvious that whatever lookup_batch received was not status code 429 (thus broke the while loop), but did not contain any JSON in the response text

placekey-py/placekey/api.py

Lines 266 to 272 in 6d13e53

# Make request, and retry if there is a server-side rate limit error
while True:
result = self.make_bulk_request(batch_payload)
if result.status_code != 429:
break
return json.loads(result.text)

I called make_bulk_request directly to see what the real result was and got more helpful output.

pk_api.make_bulk_request({"queries": query[:100]})
<Response [504]>

Suggested solution

Perhaps lookup_batch could raise an error if a non-200 HTTP status is received? Or perhaps just certain 500 codes? Or more of a catch-all error if the result text isn't JSON?

# Make request, and retry if there is a server-side rate limit error
while True: 
    result = self.make_bulk_request(batch_payload)
    if result.status_code != 429: 
        break

if not result.text:
    raise RuntimeError(f"Error: Status code {result.status_code}")

return json.loads(result.text) 

Placekey API is missing the Lineage API call

The Placekey python package is capable of querying the single locations and bulk API endpoints, but it is missing the Lineage API endpoint.

Placekey recommends pinging the Lineage endpoint every 3-5 months in order to detect and correct any changes to existing Placekey data - essential to maintaining a Placekey-based dataset. Right now, users must write their own Lineage requests, even though they can use placekey-py for everything else.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.