Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Added ability to use global context #675

Merged
merged 1 commit into from
Jul 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ matrix:
- python: 3.6
env:
- PROJSYNC=ALL
- python: 3.6
env:
- PYPROJ_GLOBAL_CONTEXT=ON
- python: 3.7.2
env:
- DOC=true
Expand Down
20 changes: 19 additions & 1 deletion docs/advanced_examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ Multithreading
--------------

The :class:`pyproj.transformer.Transformer` and :class:`pyproj.crs.CRS`
classes each have their own PROJ context. However, contexts cannot be
classes each have their own PROJ context by default. However, contexts cannot be
shared across threads. As such, it is recommended to create the object
within the thread that uses it.

Expand All @@ -162,3 +162,21 @@ Here is a simple demonstration:
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
for result in executor.map(transform_point, range(5)):
print(result)


Optimizing Single-Threaded Applications
----------------------------------------

If you have a single-threaded application that generates many objects,
enabling the use of the global context can provide performance enhancements.

For information about using the global context, see: :ref:`global_context`

Here is an example where enabling the global context can help:

.. code-block:: python

import pyproj

codes = pyproj.get_codes("EPSG", pyproj.enums.PJType.PROJECTED_CRS, False)
crs_list = [pyproj.CRS.from_epsg(code) for code in codes]
33 changes: 33 additions & 0 deletions docs/api/global_context.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.. _global_context:

Global Context
==============

If you have a single-threaded application that generates many objects,
enabling the use of the global context can provide performance enhancements.

.. warning:: The global context is not thread safe.
.. warning:: The global context does not autoclose the database.
snowman2 marked this conversation as resolved.
Show resolved Hide resolved

How to enable:

- Using :func:`pyproj.set_use_global_context`.
- Using the environment variable `PYPROJ_GLOBAL_CONTEXT`.


pyproj.set_use_global_context
-----------------------------

.. autofunction:: pyproj.set_use_global_context


pyproj.set_global_context_network
-----------------------------------

.. autofunction:: pyproj.set_global_context_network


pyproj.is_global_context_network_enabled
------------------------------------------

.. autofunction:: pyproj.is_global_context_network_enabled
1 change: 1 addition & 0 deletions docs/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ API Documentation
list
datadir
sync
global_context
enums
exceptions
show_versions
1 change: 1 addition & 0 deletions docs/history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Change Log
* ENH: Add support for coordinate systems with CRS using CF conventions (issue #536)
* ENH: Use `proj_is_equivalent_to_with_ctx` in the place of `proj_is_equivalent_to` internally (issue #666)
* BUG: Add support for identifying engineering/parametric/temporal datums (issue #670)
* ENH: Added ability to use global context (issue #661)

2.6.1
~~~~~
Expand Down
9 changes: 7 additions & 2 deletions pyproj/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@
]
import warnings

from pyproj import _datadir
from pyproj._datadir import ( # noqa: F401
_pyproj_global_context_initialize,
is_global_context_network_enabled,
set_global_context_network,
set_use_global_context,
)
from pyproj._list import ( # noqa: F401
get_authorities,
get_codes,
Expand All @@ -71,6 +76,6 @@


try:
_datadir.pyproj_global_context_initialize()
_pyproj_global_context_initialize()
except DataDirError as err:
warnings.warn(str(err))
70 changes: 23 additions & 47 deletions pyproj/_crs.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import re
import warnings
from collections import OrderedDict

from pyproj._datadir cimport pyproj_context_initialize
from pyproj._datadir cimport pyproj_context_create
from pyproj.compat import cstrencode, pystrdecode
from pyproj.crs.datum import CustomEllipsoid
from pyproj.crs.enums import CoordinateOperationType, DatumType
Expand Down Expand Up @@ -169,8 +169,7 @@ cdef _get_concatenated_operations(PJ_CONTEXT* context, PJ* concatenated_operatio
cdef int iii = 0
operations = []
for iii in range(step_count):
sub_context = proj_context_create()
pyproj_context_initialize(sub_context, True, network=network_enabled)
sub_context = pyproj_context_create(network=network_enabled)
operation = proj_concatoperation_get_step(
sub_context,
concatenated_operation,
Expand Down Expand Up @@ -652,8 +651,7 @@ cdef class CoordinateSystem(_CRSParts):
-------
CoordinateSystem
"""
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* coordinate_system_pj = proj_create(
context,
cstrencode(coordinate_system_string)
Expand Down Expand Up @@ -861,8 +859,7 @@ cdef class Ellipsoid(_CRSParts):
-------
Ellipsoid
"""
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* ellipsoid_pj = proj_create_from_database(
context,
cstrencode(auth_name),
Expand Down Expand Up @@ -916,8 +913,7 @@ cdef class Ellipsoid(_CRSParts):
-------
Ellipsoid
"""
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* ellipsoid_pj = proj_create(
context,
cstrencode(ellipsoid_string)
Expand Down Expand Up @@ -1020,8 +1016,7 @@ cdef class Ellipsoid(_CRSParts):
-------
Ellipsoid
"""
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* ellipsoid_pj = _from_name(
context,
ellipsoid_name,
Expand Down Expand Up @@ -1142,9 +1137,7 @@ cdef class PrimeMeridian(_CRSParts):
-------
PrimeMeridian
"""
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)

cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* prime_meridian_pj = proj_create_from_database(
context,
cstrencode(auth_name),
Expand Down Expand Up @@ -1198,8 +1191,7 @@ cdef class PrimeMeridian(_CRSParts):
-------
PrimeMeridian
"""
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* prime_meridian_pj = proj_create(
context,
cstrencode(prime_meridian_string)
Expand Down Expand Up @@ -1308,8 +1300,7 @@ cdef class PrimeMeridian(_CRSParts):
-------
PrimeMeridian
"""
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* prime_meridian_pj = _from_name(
context,
prime_meridian_name,
Expand Down Expand Up @@ -1405,8 +1396,7 @@ cdef class Datum(_CRSParts):
-------
Datum
"""
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()

cdef PJ* datum_pj = proj_create_from_database(
context,
Expand Down Expand Up @@ -1460,8 +1450,7 @@ cdef class Datum(_CRSParts):
-------
Datum
"""
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* datum_pj = proj_create(
context,
cstrencode(datum_string)
Expand Down Expand Up @@ -1532,8 +1521,7 @@ cdef class Datum(_CRSParts):
Datum
"""
pj_datum_type = _PJ_DATUM_TYPE_MAP[datum_type]
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* datum_pj = _from_name(
context,
datum_name,
Expand Down Expand Up @@ -1643,8 +1631,7 @@ cdef class Datum(_CRSParts):
"""
if self._ellipsoid is not None:
return None if self._ellipsoid is False else self._ellipsoid
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* ellipsoid_pj = proj_get_ellipsoid(
context,
self.projobj,
Expand All @@ -1667,8 +1654,7 @@ cdef class Datum(_CRSParts):
"""
if self._prime_meridian is not None:
return None if self._prime_meridian is False else self._prime_meridian
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* prime_meridian_pj = proj_get_prime_meridian(
context,
self.projobj,
Expand Down Expand Up @@ -1971,9 +1957,7 @@ cdef class CoordinateOperation(_CRSParts):
-------
CoordinateOperation
"""
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)

cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* coord_operation_pj = proj_create_from_database(
context,
cstrencode(auth_name),
Expand Down Expand Up @@ -2026,8 +2010,7 @@ cdef class CoordinateOperation(_CRSParts):
-------
CoordinateOperation
"""
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* coord_operation_pj = proj_create(
context,
cstrencode(coordinate_operation_string)
Expand Down Expand Up @@ -2147,8 +2130,7 @@ cdef class CoordinateOperation(_CRSParts):
pj_coordinate_operation_type = _PJ_COORDINATE_OPERATION_TYPE_MAP[
CoordinateOperationType.create(coordinate_operation_type)
]
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* coordinate_operation_pj = _from_name(
context,
coordinate_operation_name,
Expand Down Expand Up @@ -2341,8 +2323,7 @@ cdef class _CRS(Base):
self.type_name = "undefined"

def __init__(self, proj_string):
self.context = proj_context_create()
pyproj_context_initialize(self.context, False)
self.context = pyproj_context_create()
# initialize projection
self.projobj = proj_create(
self.context,
Expand Down Expand Up @@ -2407,8 +2388,7 @@ cdef class _CRS(Base):
"""
if self._ellipsoid is not None:
return None if self._ellipsoid is False else self._ellipsoid
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* ellipsoid_pj = proj_get_ellipsoid(
context,
self.projobj
Expand All @@ -2433,8 +2413,7 @@ cdef class _CRS(Base):
"""
if self._prime_meridian is not None:
return None if self._prime_meridian is True else self._prime_meridian
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* prime_meridian_pj = proj_get_prime_meridian(
context,
self.projobj,
Expand All @@ -2458,8 +2437,7 @@ cdef class _CRS(Base):
"""
if self._datum is not None:
return None if self._datum is False else self._datum
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* datum_pj = proj_crs_get_datum(
context,
self.projobj,
Expand Down Expand Up @@ -2488,8 +2466,7 @@ cdef class _CRS(Base):
"""
if self._coordinate_system is not None:
return None if self._coordinate_system is False else self._coordinate_system
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* coord_system_pj = proj_crs_get_coordinate_system(
context,
self.projobj
Expand Down Expand Up @@ -2521,8 +2498,7 @@ cdef class _CRS(Base):
if self._coordinate_operation is False
else self._coordinate_operation
)
cdef PJ_CONTEXT* context = proj_context_create()
pyproj_context_initialize(context, True)
cdef PJ_CONTEXT* context = pyproj_context_create()
cdef PJ* coord_pj = proj_crs_get_coordoperation(
context,
self.projobj
Expand Down
6 changes: 2 additions & 4 deletions pyproj/_datadir.pxd
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
include "proj.pxi"

cdef void pyproj_context_initialize(
PJ_CONTEXT* context,
bint free_context_on_error,
network=*) except *

cdef PJ_CONTEXT* pyproj_context_create(network=*) except *
8 changes: 7 additions & 1 deletion pyproj/_datadir.pyi
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
def pyproj_global_context_initialize() -> None: ...
from typing import Optional

def _pyproj_global_context_initialize() -> None: ...
def get_user_data_dir(create: bool = False) -> str: ...
def _global_context_set_data_dir() -> None: ...
def set_use_global_context(active: Optional[bool] = None) -> None: ...
def set_global_context_network(active: Optional[bool] = None) -> None: ...
def is_global_context_network_enabled() -> bool: ...
Loading