diff --git a/AUTHORS.rst b/AUTHORS.rst index 1c514c29..bcc6a4bb 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -30,6 +30,7 @@ A big thanks to all the people that help contribute: - esy_ - Submitted Github Issues - Sergei Grabalin (Сергей Грабалин) - Fixed Python2 Unicode Issues - `Matthieu Rigal`_ - Added session support +- `Nick Masi`_ - Added New York State provider. .. _`Virus Warnning`: https://github.com/virus-warnning .. _`Kevin Brolly`: https://twitter.com/KevinBrolly @@ -53,3 +54,4 @@ A big thanks to all the people that help contribute: .. _patrickyan: https://github.com/patrickyan .. _esy: https://github.com/lambda-conspiracy .. _Matthieu Rigal: https://github.com/MRigal +.. _Nick Masi: https://github.com/N-Masi diff --git a/README.md b/README.md index 1cdecfdb..4ba35d50 100644 --- a/README.md +++ b/README.md @@ -222,6 +222,7 @@ $ geocode "Ottawa, ON" \ | [MapQuest][MapQuest] | World | API key | yes | yes | | yes | | [~~Mapzen~~][Mapzen] | Shutdown | API key | yes | yes | | | | [MaxMind][MaxMind] | World | | | | | | +| [NewYorkState][NewYorkState] | NYS | | yes | | | | | [OpenCage][OpenCage] | World | API key | yes | yes | | | | [OpenStreetMap][OpenStreetMap] | World | [Policy][OpenStreetMap-Policy] | yes | yes | | | | [Tamu][Tamu] | US | API key | | | | | @@ -342,3 +343,4 @@ See [CHANGELOG.md](./CHANGELOG.md) [Komoot]: http://photon.komoot.de [USCensus]: https://geocoding.geo.census.gov/geocoder/Geocoding_Services_API.html [Gisgraphy]: https://premium.gisgraphy.com/ +[NewYorkState]: https://gis.ny.gov/system/files/documents/2022/07/geocoding-help-101.pdf diff --git a/geocoder/__init__.py b/geocoder/__init__.py index cef6d70d..348b01b0 100755 --- a/geocoder/__init__.py +++ b/geocoder/__init__.py @@ -37,7 +37,7 @@ from geocoder.api import nokia, osm, tomtom, geolytica, arcgis, opencage, locationiq # noqa from geocoder.api import maxmind, ipinfo, freegeoip, ottawa, here, baidu, gaode, w3w # noqa from geocoder.api import yandex, mapzen, komoot, tamu, geocodefarm, tgos, uscensus # noqa -from geocoder.api import gisgraphy # noqa +from geocoder.api import gisgraphy, newyorkstate # noqa # EXTRAS from geocoder.api import timezone, elevation, places, ip, canadapost, reverse, distance, location # noqa diff --git a/geocoder/api.py b/geocoder/api.py index 546d4cd1..61ac6e2a 100755 --- a/geocoder/api.py +++ b/geocoder/api.py @@ -23,6 +23,7 @@ from geocoder.mapquest import MapquestQuery from geocoder.mapzen import MapzenQuery from geocoder.maxmind import MaxmindQuery +from geocoder.newyorkstate import NewYorkStateQuery from geocoder.opencage import OpenCageQuery from geocoder.osm import OsmQuery, OsmQueryDetail from geocoder.ottawa import OttawaQuery @@ -103,6 +104,7 @@ 'reverse': MapboxReverse, }, 'maxmind': {'geocode': MaxmindQuery}, + 'newyorkstate': {'geocode': NewYorkStateQuery}, 'ipinfo': {'geocode': IpinfoQuery}, 'geonames': { 'geocode': GeonamesQuery, @@ -659,3 +661,12 @@ def gisgraphy(location, **kwargs): :param ``location``: Your search location you want geocoded. """ return get(location, provider='gisgraphy', **kwargs) + + +def newyorkstate(location, **kwargs): + """New York State Office of Information Technology Services Provider + + :param ``location``: Your search location you want geocoded. + :param ``maxRows``: (default=1) Max number of results to fetch + """ + return get(location, provider='newyorkstate', **kwargs) diff --git a/geocoder/newyorkstate.py b/geocoder/newyorkstate.py new file mode 100644 index 00000000..0fa6f260 --- /dev/null +++ b/geocoder/newyorkstate.py @@ -0,0 +1,102 @@ +#!/usr/bin/python +# coding: utf8 + +from __future__ import absolute_import + +import logging +import re + +from geocoder.base import OneResult, MultipleResultsQuery + +class NewYorkStateResult(OneResult): + + @property + def lat(self): + return self.raw.get('location', {}).get('y') + + @property + def lng(self): + return self.raw.get('location', {}).get('x') + + @property + def address(self): + return self.raw.get('address') + + @property + def housenumber(self): + expression = r'\d+' + matches = re.findall(expression, self.address) + if matches: + return matches[0] + + @property + def street(self): + expression = r'^\d*\s*([A-Za-z ]+)(?=,)' + matches = re.findall(expression, self.address) + if matches: + return matches[0] + + @property + def city(self): + expression = r',\s*([^,]+)\s*,' + matches = re.findall(expression, self.address) + if matches: + return matches[0] + + @property + def state(self): + return 'NY' + + @property + def postal(self): + expression = r'\d{5}' + matches = re.findall(expression, self.address) + if matches: + return matches[-1] + + @property + def country(self): + return 'USA' + + @property + def address(self): + return self.raw.get('address') + + @property + def accuracy(self): + return self.raw.get('score') + +class NewYorkStateQuery(MultipleResultsQuery): + """ + NYS ITS Geocoder REST Services + ======================= + Geocoding is the use of technology and reference data to return a geographic coordinate when a street address is entered. Geocoding is used when lists of addresses need to be placed on a map, when an address is entered in an application to center a map or return information for that location, or any other time you have an address, and a geographic coordinate is needed. An address is entered either manually or by bulk input from a database or other source. The geocoder then compares the entered address to a set of reference data. The geocoder returns a coordinate pair and standardized address for each input address it can match. The NYS ITS Geospatial Services geocoder uses a series of combinations of reference data and configuration parameters to optimize both the likelihood of a match and the quality of the results. The reference data supporting the geocoder is stored in NENA GIS Data Model standard. + + API Reference + ------------- + https://gisservices.its.ny.gov/arcgis/rest/services/Locators/Street_and_Address_Composite/GeocodeServer + https://gis.ny.gov/system/files/documents/2022/07/geocoding-help-101.pdf + https://gis.ny.gov/address-geocoder + """ + provider = 'newyorkstate' + method = 'geocode' + + _URL = 'https://gisservices.its.ny.gov/arcgis/rest/services/Locators/Street_and_Address_Composite/GeocodeServer/findAddressCandidates' + _RESULT_CLASS = NewYorkStateResult + _KEY_MANDATORY = False + + def _build_params(self, location, provider_key, **kwargs): + return { + 'SingleLine': location, + 'f': 'json', + 'outSR': 4326, + 'maxLocations': kwargs.get('maxRows', 1) + } + + def _adapt_results(self, json_response): + return json_response.get('candidates', []) + +if __name__ == '__main__': + logging.basicConfig(level=logging.INFO) + g = NewYorkStateQuery('65 Niagara Square, Buffalo, NY 14202') + g.debug() diff --git a/tests/test_newyorkstate.py b/tests/test_newyorkstate.py new file mode 100644 index 00000000..9d69e819 --- /dev/null +++ b/tests/test_newyorkstate.py @@ -0,0 +1,14 @@ +# coding: utf8 + +import geocoder + +location = '65 Niagara Square, Buffalo, NY 14202' + + +def test_newyorkstate(): + g = geocoder.newyorkstate(location) + assert g.ok + osm_count, fields_count = g.debug()[0] + assert osm_count >= 6 + assert fields_count >= 13 +