diff --git a/.gitignore b/.gitignore index 601cc3d6..4578489a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ morphed_data/ # dev setup .vscode/ +.idea/ # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9d5f3e2a..46b7047e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,14 +25,14 @@ repos: exclude: (\.(svg|png|pdf)$)|(CODE_OF_CONDUCT.md) - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.9 + rev: v0.5.7 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix, --show-fixes] - id: ruff-format - repo: https://github.com/numpy/numpydoc - rev: v1.7.0 + rev: v1.8.0 hooks: - id: numpydoc-validation exclude: (tests|docs)/.* @@ -44,7 +44,7 @@ repos: files: tests/.* - repo: https://github.com/tox-dev/pyproject-fmt - rev: 2.1.3 + rev: 2.2.1 hooks: - id: pyproject-fmt args: [--keep-full-version, --no-print-diff] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5063d17..6f8b4509 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ Set up the pre-commit hooks to make sure you can pass the CI checks: $ pre-commit install ``` -All commits will be squashed, so just make sure that the final commit passes all of the linting, documentation, and testing checks. These will run in GitHub Actions when you open a pull request, but you should also run them locally: +All commits will be squashed, so just make sure that the final commit passes all linting, documentation, and testing checks. These will run in GitHub Actions when you open a pull request, but you should also run them locally: ```shell $ pre-commit run --all-files # linting and documentation format checks @@ -29,7 +29,7 @@ $ cd docs && make html # build the documentation locally Some things to remember: - All code must be documented using docstrings in the [numpydoc style](https://numpydoc.readthedocs.io/en/latest/format.html) – the pre-commit hooks will check for this. -- Any changes to the API must be accompanied with either an additional test case or a new test. Run `pytest` to make sure your changes are covered. +- Any changes to the API must be accompanied by either an additional test case or a new test. Run `pytest` to make sure your changes are covered. - Documentation for the project is built with Sphinx. Your changes must render correct in the output. Run `make html` from the `docs` directory and inspect the result. ## 3. Open a pull request @@ -43,13 +43,13 @@ In your description, please do the following: When you create your pull request, first-time contributors will need to wait for a maintainer to approve running the GitHub Actions workflows. Please be patient until this happens. -Once it does, the same checks described above (testing, documentation, linting) that you ran on your machine will run on Linux, MacOS, and Windows with multiple versions of Python. Please note that it is possible that differences in operating systems and/or Python versions results in a failure, despite it working on your machine. +Once it does, the same checks described above (testing, documentation, linting) that you ran on your machine will run on Linux, macOS, and Windows with multiple versions of Python. Please note that it is possible that differences in operating systems and/or Python versions results in a failure, despite it working on your machine. If anything fails, please attempt to fix it as we're unlikely to review your code until everything passes. If stuck, please feel free to leave a note in the pull request enumerating what you have already tried and someone may be able to offer assistance. ## 4. Code review -After all of the checks in your pull request pass, a maintainer will review your code. In many cases, there will be some feedback to address, and this may require a few iterations to get to the best implementation. Remember to be patient and polite during this process. +After all checks in your pull request pass, a maintainer will review your code. In many cases, there will be some feedback to address, and this may require a few iterations to get to the best implementation. Remember to be patient and polite during this process. ## 5. Congratulations! diff --git a/README.md b/README.md index e49a6687..bbd81217 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@
-Data Morph transforms an input dataset of 2D points into select shapes, while preserving the summary statistics to a given number of decimal points through simulated annealing. +Data Morph transforms an input dataset of 2D points into select shapes, while preserving the summary statistics to a given number of decimal points through simulated annealing. It is intended to be used as a teaching tool to illustrate the importance of data visualization (see the [Data Morph in the Classroom](https://github.com/stefmolin/data-morph/#data-morph-in-the-classroom) section for ideas).
Morphing the panda dataset into the star shape. @@ -152,15 +152,25 @@ Note that the `result` variable in the above code block is a `pandas.DataFrame` In this example, we morphed the built-in panda `Dataset` into the star `Shape`. Be sure to try out the other built-in options: -* The `DataLoader.AVAILABLE_DATASETS` attribute contains a list of available datasets, which are also visualized in the `DataLoader` documentation. +* The `DataLoader.AVAILABLE_DATASETS` attribute contains a list of available datasets, which are also visualized in the `DataLoader` documentation [here](https://stefaniemolin.com/data-morph/stable/api/data_morph.data.loader.html#data_morph.data.loader.DataLoader). -* The `ShapeFactory.AVAILABLE_SHAPES` attribute contains a list of available shapes, which are also visualized in the `ShapeFactory` documentation. +* The `ShapeFactory.AVAILABLE_SHAPES` attribute contains a list of available shapes, which are also visualized in the `ShapeFactory` documentation [here](https://stefaniemolin.com/data-morph/stable/api/data_morph.shapes.factory.html#data_morph.shapes.factory.ShapeFactory). + +## Data Morph in the Classroom + +Data Morph is intended to be used as a teaching tool to illustrate the importance of data visualization. Here are some potential classroom activities for instructors: + +- **Statistics Focus**: Have students pick one of the [built-in datasets](https://stefaniemolin.com/data-morph/stable/api/data_morph.data.loader.html#data_morph.data.loader.DataLoader), and morph it into all available [target shapes](https://stefaniemolin.com/data-morph/stable/api/data_morph.shapes.factory.html#data_morph.shapes.factory.ShapeFactory). Ask students to comment on which transformations worked best and why. +- **Creativity Focus**: Have students [create a new dataset](https://stefaniemolin.com/data-morph/stable/custom_datasets.html) (*e.g.*, your school logo or something that the student designs), and morph that into multiple [target shapes](https://stefaniemolin.com/data-morph/stable/api/data_morph.shapes.factory.html#data_morph.shapes.factory.ShapeFactory). Ask students to comment on which transformations worked best and why. +- **Math and Coding Focus**: Have students [create a custom shape](https://stefaniemolin.com/data-morph/dev/shape_creation.html) by inheriting from `LineCollection` or `PointCollection`, and try morphing a couple of the [built-in datasets](https://stefaniemolin.com/data-morph/stable/api/data_morph.data.loader.html#data_morph.data.loader.DataLoader) into that shape. Ask students to explain how they chose to calculate the shape, and comment on which transformations worked best and why. + +If you end up using Data Morph in your classroom, I would love to hear about it. Please [send me a message](https://stefaniemolin.com/contact/) detailing how you used it and how it went. ## Acknowledgements This code has been altered by Stefanie Molin ([@stefmolin](https://github.com/stefmolin)) to work for other input datasets by parameterizing the target shapes with information from the input shape. The original code works for a specific dataset called the "Datasaurus" and was created for the paper *Same Stats, Different Graphs: Generating Datasets with Varied Appearance and Identical Statistics through Simulated Annealing* by Justin Matejka and George Fitzmaurice (ACM CHI 2017). -The paper and video can be found on the Autodesk Research website [here](https://www.research.autodesk.com/publications/same-stats-different-graphs-generating-datasets-with-varied-appearance-and-identical-statistics-through-simulated-annealing/). The version of the code placed on GitHub at [jmatejka/same-stats-different-graphs](https://github.com/jmatejka/same-stats-different-graphs), served as the starting point for the Data Morph code base, which is on GitHub at [stefmolin/data-morph](https://github.com/stefmolin/data-morph). +The paper and video can be found on the Autodesk Research website [here](https://www.research.autodesk.com/publications/same-stats-different-graphs-generating-datasets-with-varied-appearance-and-identical-statistics-through-simulated-annealing/). The version of the code placed on GitHub at [jmatejka/same-stats-different-graphs](https://github.com/jmatejka/same-stats-different-graphs), served as the starting point for the Data Morph codebase, which is on GitHub at [stefmolin/data-morph](https://github.com/stefmolin/data-morph). Read more about the creation of Data Morph [here](https://stefaniemolin.com/articles/data-science/introducing-data-morph/) and [here](https://stefaniemolin.com/data-morph-talk/#/). diff --git a/docs/_static/shapes_uml.svg b/docs/_static/shapes_uml.svg new file mode 100644 index 00000000..71ecff2c --- /dev/null +++ b/docs/_static/shapes_uml.svg @@ -0,0 +1,200 @@ + + + + + + +classes_data_morph + + + +data_morph.shapes.bases.shape.Shape + +Shape + + +distance +(x: Number, y: Number): float +plot +(ax: Axes): Axes + + + +data_morph.shapes.circles.Rings + +Rings + +circles : list[Circle] + +distance(x: Number, y: Number): float +plot(ax: Axes): Axes + + + +data_morph.shapes.circles.Rings->data_morph.shapes.bases.shape.Shape + + + + + +data_morph.shapes.circles.Circle + +Circle + +cx : Number +cy : Number +r : Number + +distance(x: Number, y: Number): float +plot(ax: Axes): Axes + + + +data_morph.shapes.circles.Rings->data_morph.shapes.circles.Circle + + + + + +data_morph.shapes.circles.Circle->data_morph.shapes.bases.shape.Shape + + + + + +data_morph.shapes.bases.line_collection.LineCollection + +LineCollection + +lines : tuple + +distance(x: Number, y: Number): float +plot(ax: Axes): Axes + + + +data_morph.shapes.bases.line_collection.LineCollection->data_morph.shapes.bases.shape.Shape + + + + + +data_morph.shapes.polygons.Diamond + +Diamond + + + + + + +data_morph.shapes.polygons.Diamond->data_morph.shapes.bases.line_collection.LineCollection + + + + + +data_morph.shapes.lines.SlantUpLines + +SlantUpLines + + + + + + +data_morph.shapes.lines.SlantUpLines->data_morph.shapes.bases.line_collection.LineCollection + + + + + +data_morph.shapes.polygons.Star + +Star + + + + + + +data_morph.shapes.polygons.Star->data_morph.shapes.bases.line_collection.LineCollection + + + + + +data_morph.shapes.lines.others + +... + + + +data_morph.shapes.lines.others->data_morph.shapes.bases.line_collection.LineCollection + + + + + +data_morph.shapes.bases.point_collection.PointCollection + +PointCollection + +points : ndarray + +distance(x: Number, y: Number): float +plot(ax: Axes): Axes + + + +data_morph.shapes.bases.point_collection.PointCollection->data_morph.shapes.bases.shape.Shape + + + + + +data_morph.shapes.points.Heart + +Heart + + + + + + +data_morph.shapes.points.Heart->data_morph.shapes.bases.point_collection.PointCollection + + + + + +data_morph.shapes.points.Scatter + +Scatter + + +distance(x: Number, y: Number): int + + + +data_morph.shapes.points.Scatter->data_morph.shapes.bases.point_collection.PointCollection + + + + + +data_morph.shapes.points.others + +... + + + +data_morph.shapes.points.others->data_morph.shapes.bases.point_collection.PointCollection + + + + + diff --git a/docs/classroom-ideas.rst b/docs/classroom-ideas.rst new file mode 100644 index 00000000..b2495c37 --- /dev/null +++ b/docs/classroom-ideas.rst @@ -0,0 +1,24 @@ +Data Morph in the classroom +--------------------------- + +Data Morph is intended to be used as a teaching tool to illustrate the importance +of data visualization. Here are some potential classroom activities for instructors: + +* **Statistics Focus**: Have students pick one of the `built-in datasets + <./api/data_morph.data.loader.html#data_morph.data.loader.DataLoader>`_, + and morph it into all available `target shapes + <./api/data_morph.shapes.factory.html#data_morph.shapes.factory.ShapeFactory>`_. + Ask students to comment on which transformations worked best and why. +* **Creativity Focus**: Have students :doc:`create a new dataset ` + (*e.g.*, your school logo or something that the student designs), and morph that into multiple + `target shapes <./api/data_morph.shapes.factory.html#data_morph.shapes.factory.ShapeFactory>`_. + Ask students to comment on which transformations worked best and why. +* **Math and Coding Focus**: Have students :doc:`create a custom shape ` + by inheriting from :class:`.LineCollection` or :class:`.PointCollection`, and try morphing a + couple of the `built-in datasets <./api/data_morph.data.loader.html#data_morph.data.loader.DataLoader>`_ + into that shape. Ask students to explain how they chose to calculate the shape, and + comment on which transformations worked best and why. + +If you end up using Data Morph in your classroom, I would love to hear about it. Please +`send me a message `_ detailing how you used it and +how it went. diff --git a/docs/cli.rst b/docs/cli.rst index 2af092fa..b786a28d 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -1,10 +1,11 @@ CLI Reference ============= -.. argparse:: - :ref: data_morph.cli._generate_parser_for_docs +.. sphinx_argparse_cli:: + :module: data_morph.cli + :func: generate_parser :prog: data-morph - :noepilog: + :group_title_prefix: ---- diff --git a/docs/conf.py b/docs/conf.py index 124315d3..ffdd05b2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -40,7 +40,7 @@ 'sphinx.ext.napoleon', 'sphinx.ext.viewcode', 'sphinx_copybutton', - 'sphinxarg.ext', + 'sphinx_argparse_cli', 'matplotlib.sphinxext.plot_directive', ] @@ -63,7 +63,6 @@ 'Pillow': ('https://pillow.readthedocs.io/en/stable/', None), 'pytest': ('https://pytest.org/en/stable/', None), 'python': ('https://docs.python.org/3/', None), - 'scipy': ('https://docs.scipy.org/doc/scipy/', None), } @@ -148,6 +147,7 @@ 'custom_datasets': [], 'quickstart': [], 'release_notes': [], + 'shape_creation': [], } diff --git a/docs/custom_datasets.rst b/docs/custom_datasets.rst index 11e4c056..b3945484 100644 --- a/docs/custom_datasets.rst +++ b/docs/custom_datasets.rst @@ -113,7 +113,14 @@ Here is an example animation generated from a custom dataset: (Optional) Contribute the dataset --------------------------------- -If you have the rights to distribute the dataset and you think it would be a good -inclusion as a built-in dataset, make a PR to add it to Data Morph. Be sure to consult -the `contributing guidelines `_ +If you have the rights to distribute the dataset and you think it would +be a good built-in dataset, `create an issue `_ +in the Data Morph repository proposing its inclusion. Be sure to consult the +`contributing guidelines `_ before doing so. + +If and only if you are given the go ahead: + +1. Add your CSV file to the ``src/data_morph/data/starter_shapes/`` directory. +2. Add an entry to the ``DataLoader._DATASETS`` dictionary in ``src/data_morph/data/loader.py``. +3. Submit your pull request. diff --git a/docs/index.rst b/docs/index.rst index 35f0735e..b5cd4667 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -8,6 +8,7 @@ Data Morph quickstart custom_datasets + shape_creation cli api release_notes @@ -18,6 +19,8 @@ Data Morph .. include:: quickstart.rst :start-after: .. INSTALLATION +.. include:: classroom-ideas.rst + Citations --------- diff --git a/docs/quickstart.rst b/docs/quickstart.rst index ed3c9ee1..bc955dac 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -141,4 +141,5 @@ out the other built-in options: * The :attr:`.ShapeFactory.AVAILABLE_SHAPES` attribute contains a list of available shapes, which are also visualized in the :class:`.ShapeFactory` documentation. -For further customization, the :doc:`custom_datasets` tutorial discusses how to generate custom input datasets. +For further customization, the :doc:`custom_datasets` tutorial discusses how to generate custom input datasets, +and the :doc:`shape_creation` tutorial discusses how to generate custom target shapes. diff --git a/docs/shape_creation.rst b/docs/shape_creation.rst new file mode 100644 index 00000000..8900158a --- /dev/null +++ b/docs/shape_creation.rst @@ -0,0 +1,118 @@ +Shape Creation +============== + +This tutorial walks you through the process of creating a new shape +for use as a target in the morphing process. + +.. contents:: Steps + :depth: 2 + :local: + :backlinks: none + +---- + +Create a class for the shape +---------------------------- + +All Data Morph shapes are defined as classes inside the :mod:`.shapes` subpackage. +In order to register a new target shape for the CLI, you will need to fork and clone +`the Data Morph repository `_, and then add +a class defining your shape. + +Select the appropriate base class +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Data Morph uses a hierarchy of shapes that all descend from an abstract +base class (:class:`.Shape`), which defines the basics of how a shape +needs to behave (*i.e.*, it must have a ``distance()`` method and a +``plot()`` method). + +.. figure:: _static/shapes_uml.svg + :alt: UML diagram showing the hierarchy of the shape classes. + :align: center + +Any new shape must inherit from :class:`.Shape` or one of its +child classes: + +* If your shape is composed of lines, inherit from :class:`.LineCollection` + (*e.g.*, :class:`.Star`). +* If your shape is composed of points, inherit from :class:`.PointCollection` + (*e.g.*, :class:`.Heart`). +* If your shape isn't composed of lines or points you can inherit directly from + :class:`.Shape` (*e.g.*, :class:`.Circle`). Note that in this case you must + define both the ``distance()`` and ``plot()`` methods (this is done for your + if you inherit from :class:`.LineCollection` or :class:`.PointCollection`). + +Define the scale and placement of the shape based on the dataset +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Each shape will be initialized with a :class:`.Dataset` instance. Use the dataset +to determine where in the *xy*-plane the shape should be placed and also to scale it +to the data. If you take a look at the existing shapes, you will see that they use +various bits of information from the dataset, such as the automatically-calculated +bounds (*e.g.*, :attr:`.Dataset.data_bounds`, which form the bounding box of the +starting data, and :attr:`.Dataset.morph_bounds`, which define the limits of where +the algorithm can move the points) or percentiles using the data itself (see +:attr:`.Dataset.df`). For example, the :class:`.XLines` shape inherits from +:class:`.LineCollection` and uses the morph bounds (:attr:`.Dataset.morph_bounds`) +to calculate its position and scale: + +.. code:: python + + class XLines(LineCollection): + + def __init__(self, dataset: Dataset) -> None: + xmin, xmax = dataset.morph_bounds.x_bounds + ymin, ymax = dataset.morph_bounds.y_bounds + + super().__init__([[xmin, ymin], [xmax, ymax]], [[xmin, ymax], [xmax, ymin]]) + + def __str__(self) -> str: + return 'x' + +Since we inherit from :class:`.LineCollection` here, we don't need to define +the ``distance()`` and ``plot()`` methods (unless we want to override them). +We do override the ``__str__()`` method here since the default will result in +a value of ``xlines`` and ``x`` makes more sense for use in the documentation +(see :class:`.ShapeFactory`). + +Register the shape +------------------ + +For the ``data-morph`` CLI to find your shape, you need to register it with the +:class:`.ShapeFactory`: + +1. Add your shape class to the appropriate file inside the ``src/data_morph/shapes/`` + directory. Note that the filenames correspond to the type of shape (*e.g.*, use + ``src/data_morph/shapes/points.py`` for a new shape inheriting from :class:`.PointCollection`). +2. Add an entry to the ``ShapeFactory._SHAPE_MAPPING`` dictionary in + ``src/data_morph/shapes/factory.py``. + +Test out the shape +------------------ + +Defining how your shape should be generated from the input dataset will require +a few iterations. Be sure to test out your shape on different datasets: + +.. code:: shell + + $ data-morph --start-shape panda music soccer --target-shape + +Some shapes will work better on certain datasets, and that's fine. However, +if your shape only works well on one of the built-in datasets (see the +:class:`.DataLoader`), then you need to keep tweaking your implementation. + +(Optional) Contribute the shape +------------------------------- + +If you think that your shape would be a good addition to Data Morph, `create an issue +`_ in the Data Morph repository proposing +its inclusion. Be sure to consult the `contributing guidelines +`_ before doing so. + +If and only if you are given the go ahead: + +1. Prepare a docstring for your shape following what the other shapes have. + Be sure to change the plotting code in the docstring to use your shape. +2. Add test cases for your shape to the ``tests/shapes/`` directory. +3. Submit your pull request. diff --git a/pyproject.toml b/pyproject.toml index b621e320..ff8c831e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ maintainers = [ { name = "Stefanie Molin" }, ] authors = [ - { name = "Stefanie Molin", email = "24376333+stefmolin@users.noreply.github.com" }, + { name = "Stefanie Molin", email = "data-morph@stefaniemolin.com" }, { name = "Aaron Stevens", email = "bheklilr2@gmail.com" }, { name = "Justin Matejka", email = "Justin.Matejka@Autodesk.com" }, ] @@ -47,7 +47,6 @@ dependencies = [ "matplotlib>=3.3", "numpy>=1.20", "pandas>=1.2", - "scipy>=1.10.0", "tqdm>=4.64.1", ] optional-dependencies.dev = [ @@ -60,7 +59,7 @@ optional-dependencies.dev = [ optional-dependencies.docs = [ "pydata-sphinx-theme>=0.15.3", "sphinx>=7.2.6", - "sphinx-argparse>=0.4.0", + "sphinx-argparse-cli>=1.16.0", "sphinx-copybutton>=0.5.1", ] urls."Bug Tracker" = "https://github.com/stefmolin/data-morph/issues" diff --git a/src/data_morph/__init__.py b/src/data_morph/__init__.py index c326b761..425dcb14 100644 --- a/src/data_morph/__init__.py +++ b/src/data_morph/__init__.py @@ -2,7 +2,9 @@ Data Morph. Morph an input dataset of 2D points into select shapes, while preserving the summary -statistics to a given number of decimal points through simulated annealing. +statistics to a given number of decimal points through simulated annealing. It is intended +to be used as a teaching tool to illustrate the importance of data visualization (see +`Data Morph in the classroom`_ for ideas). Notes ----- @@ -17,7 +19,7 @@ `_. The version of the code placed on GitHub at `jmatejka/same-stats-different-graphs `_, -served as the starting point for the Data Morph code base, which is on GitHub at +served as the starting point for the Data Morph codebase, which is on GitHub at `stefmolin/data-morph `_. Read more about the creation of Data Morph in `this article diff --git a/src/data_morph/cli.py b/src/data_morph/cli.py index e33e93cf..5f599dd9 100644 --- a/src/data_morph/cli.py +++ b/src/data_morph/cli.py @@ -2,7 +2,6 @@ import argparse import sys -import textwrap from typing import Sequence, Union from . import __version__ @@ -18,8 +17,6 @@ 'freeze': 0, } -USAGE_WIDTH_FOR_DOCS = 80 - def generate_parser() -> argparse.ArgumentParser: """ @@ -209,27 +206,6 @@ def generate_parser() -> argparse.ArgumentParser: return parser -def _generate_parser_for_docs() -> argparse.ArgumentParser: - """ - Generate an argument parser for the documentation only. - - Returns - ------- - argparse.argparse.ArgumentParser - Modified argument parser class for the documentation. - """ - parser = generate_parser() - usage_text = parser.format_usage() - parser.format_usage = lambda: textwrap.fill( - usage_text.replace(' ', ' '), - width=USAGE_WIDTH_FOR_DOCS, - subsequent_indent='\t', - break_on_hyphens=False, - break_long_words=False, - ) - return parser - - def main(argv: Union[Sequence[str], None] = None) -> None: """ Run Data Morph as a script. diff --git a/src/data_morph/data/dataset.py b/src/data_morph/data/dataset.py index b3a2c705..a7c74815 100644 --- a/src/data_morph/data/dataset.py +++ b/src/data_morph/data/dataset.py @@ -65,7 +65,7 @@ def __init__( def __repr__(self) -> str: return f'<{self.__class__.__name__} name={self.name} scaled={self._scaled}>' - def _derive_data_bounds(self) -> None: + def _derive_data_bounds(self) -> BoundingBox: """ Derive bounds based on the data. @@ -81,7 +81,7 @@ def _derive_data_bounds(self) -> None: ] ) - def _derive_morphing_bounds(self) -> None: + def _derive_morphing_bounds(self) -> BoundingBox: """ Derive morphing bounds based on the data. @@ -99,7 +99,7 @@ def _derive_morphing_bounds(self) -> None: morph_bounds.adjust_bounds(x=x_offset, y=y_offset) return morph_bounds - def _derive_plotting_bounds(self) -> None: + def _derive_plotting_bounds(self) -> BoundingBox: """ Derive plotting bounds based on the morphing bounds. diff --git a/src/data_morph/data/loader.py b/src/data_morph/data/loader.py index 72bf949a..e2094b0a 100644 --- a/src/data_morph/data/loader.py +++ b/src/data_morph/data/loader.py @@ -29,7 +29,7 @@ class DataLoader: from data_morph.data.loader import DataLoader DataLoader.plot_available_datasets() - .. _Datasaurus: http://www.thefunctionalart.com/2016/08/download-datasaurus-never-trust-summary.html + .. _Datasaurus: https://web.archive.org/web/20240620205540/http://www.thefunctionalart.com/2016/08/download-datasaurus-never-trust-summary.html .. important:: All logos are used with permission. @@ -48,9 +48,11 @@ class DataLoader: 'dog': 'dog.csv', 'music': 'music.csv', 'panda': 'panda.csv', + 'pi': 'pi.csv', 'Python': 'python.csv', 'SDS': 'superdatascience.csv', 'sheep': 'sheep.csv', + 'soccer': 'soccer_ball.csv', } AVAILABLE_DATASETS = sorted(_DATASETS.keys(), key=str.lower) """list[str]: List of available built-in starter datasets, diff --git a/src/data_morph/data/starter_shapes/pi.csv b/src/data_morph/data/starter_shapes/pi.csv new file mode 100644 index 00000000..20469889 --- /dev/null +++ b/src/data_morph/data/starter_shapes/pi.csv @@ -0,0 +1,305 @@ +x,y +13.0,-44.0 +9.0,-48.0 +6.0,-54.0 +4.0,-62.0 +5.0,-67.0 +9.0,-68.0 +17.0,-66.0 +22.0,-62.0 +29.0,-56.0 +33.0,-51.0 +37.0,-46.0 +45.0,-45.0 +51.0,-44.0 +58.0,-44.0 +59.0,-50.0 +60.0,-58.0 +60.0,-64.0 +57.0,-74.0 +57.0,-79.0 +57.0,-91.0 +56.0,-97.0 +54.0,-111.0 +52.0,-118.0 +51.0,-125.0 +50.0,-129.0 +46.0,-142.0 +43.0,-147.0 +38.0,-156.0 +32.0,-164.0 +32.0,-164.0 +25.0,-176.0 +22.0,-184.0 +27.0,-194.0 +29.0,-198.0 +37.0,-199.0 +43.0,-203.0 +53.0,-200.0 +57.0,-198.0 +63.0,-191.0 +66.0,-184.0 +68.0,-180.0 +72.0,-169.0 +75.0,-157.0 +76.0,-147.0 +78.0,-135.0 +78.0,-121.0 +81.0,-110.0 +81.0,-98.0 +82.0,-88.0 +85.0,-75.0 +85.0,-68.0 +87.0,-56.0 +87.0,-50.0 +88.0,-44.0 +94.0,-44.0 +101.0,-43.0 +110.0,-43.0 +115.0,-43.0 +125.0,-42.0 +132.0,-45.0 +131.0,-51.0 +129.0,-63.0 +128.0,-70.0 +126.0,-80.0 +124.0,-94.0 +124.0,-103.0 +124.0,-118.0 +122.0,-129.0 +123.0,-143.0 +125.0,-155.0 +127.0,-169.0 +129.0,-180.0 +134.0,-191.0 +139.0,-196.0 +147.0,-203.0 +156.0,-204.0 +169.0,-204.0 +183.0,-200.0 +188.0,-194.0 +195.0,-186.0 +201.0,-178.0 +204.0,-168.0 +207.0,-156.0 +209.0,-147.0 +205.0,-138.0 +199.0,-140.0 +194.0,-147.0 +189.0,-157.0 +184.0,-159.0 +170.0,-162.0 +162.0,-153.0 +155.0,-143.0 +156.0,-130.0 +153.0,-117.0 +155.0,-106.0 +155.0,-94.0 +155.0,-80.0 +159.0,-70.0 +157.0,-57.0 +161.0,-47.0 +165.0,-42.0 +173.0,-42.0 +180.0,-41.0 +188.0,-42.0 +199.0,-40.0 +206.0,-39.0 +208.0,-35.0 +209.0,-31.0 +208.0,-21.0 +208.0,-13.0 +207.0,-7.0 +200.0,-5.0 +193.0,-5.0 +182.0,-6.0 +175.0,-6.0 +162.0,-7.0 +141.0,-8.0 +124.0,-6.0 +107.0,-8.0 +87.0,-6.0 +71.0,-7.0 +57.0,-7.0 +45.0,-9.0 +36.0,-13.0 +33.0,-15.0 +27.0,-20.0 +21.0,-25.0 +16.0,-33.0 +15.0,-36.0 +14.0,-56.0 +20.0,-50.0 +23.0,-39.0 +30.0,-46.0 +33.0,-29.0 +39.0,-40.0 +46.0,-23.0 +51.0,-33.0 +60.0,-17.0 +67.0,-35.0 +68.0,-23.0 +54.0,-29.0 +39.0,-30.0 +75.0,-18.0 +75.0,-33.0 +72.0,-39.0 +81.0,-36.0 +87.0,-22.0 +91.0,-15.0 +99.0,-18.0 +95.0,-32.0 +94.0,-34.0 +101.0,-37.0 +107.0,-33.0 +113.0,-27.0 +111.0,-21.0 +118.0,-16.0 +124.0,-23.0 +124.0,-28.0 +125.0,-33.0 +134.0,-35.0 +136.0,-29.0 +139.0,-21.0 +130.0,-20.0 +118.0,-35.0 +116.0,-23.0 +103.0,-27.0 +97.0,-24.0 +84.0,-29.0 +125.0,-15.0 +139.0,-16.0 +150.0,-16.0 +155.0,-17.0 +147.0,-27.0 +155.0,-30.0 +147.0,-36.0 +164.0,-23.0 +164.0,-23.0 +174.0,-19.0 +189.0,-21.0 +180.0,-27.0 +194.0,-32.0 +201.0,-12.0 +175.0,-29.0 +163.0,-33.0 +157.0,-38.0 +147.0,-47.0 +139.0,-50.0 +143.0,-60.0 +135.0,-69.0 +142.0,-74.0 +140.0,-87.0 +146.0,-91.0 +136.0,-99.0 +135.0,-110.0 +139.0,-119.0 +144.0,-117.0 +146.0,-105.0 +147.0,-97.0 +130.0,-117.0 +131.0,-129.0 +132.0,-134.0 +140.0,-135.0 +142.0,-130.0 +146.0,-124.0 +149.0,-138.0 +132.0,-147.0 +130.0,-141.0 +142.0,-147.0 +151.0,-150.0 +142.0,-160.0 +138.0,-155.0 +133.0,-160.0 +135.0,-169.0 +147.0,-165.0 +151.0,-156.0 +160.0,-160.0 +160.0,-167.0 +157.0,-173.0 +148.0,-172.0 +143.0,-180.0 +136.0,-179.0 +143.0,-188.0 +152.0,-189.0 +148.0,-198.0 +160.0,-199.0 +158.0,-190.0 +152.0,-180.0 +162.0,-181.0 +170.0,-170.0 +170.0,-179.0 +183.0,-169.0 +192.0,-165.0 +198.0,-155.0 +202.0,-145.0 +199.0,-165.0 +191.0,-176.0 +190.0,-183.0 +184.0,-186.0 +179.0,-179.0 +177.0,-171.0 +177.0,-184.0 +168.0,-188.0 +166.0,-194.0 +172.0,-196.0 +178.0,-193.0 +66.0,-46.0 +75.0,-48.0 +70.0,-59.0 +66.0,-66.0 +72.0,-73.0 +75.0,-66.0 +78.0,-54.0 +69.0,-53.0 +65.0,-72.0 +67.0,-84.0 +71.0,-87.0 +77.0,-78.0 +79.0,-66.0 +76.0,-92.0 +67.0,-99.0 +63.0,-92.0 +75.0,-102.0 +66.0,-105.0 +61.0,-102.0 +61.0,-114.0 +69.0,-116.0 +73.0,-107.0 +75.0,-117.0 +68.0,-124.0 +60.0,-119.0 +60.0,-124.0 +58.0,-130.0 +64.0,-133.0 +74.0,-133.0 +69.0,-140.0 +61.0,-141.0 +54.0,-136.0 +50.0,-135.0 +54.0,-145.0 +62.0,-151.0 +69.0,-149.0 +68.0,-157.0 +58.0,-158.0 +51.0,-151.0 +47.0,-155.0 +46.0,-160.0 +50.0,-165.0 +54.0,-167.0 +63.0,-167.0 +68.0,-162.0 +60.0,-176.0 +48.0,-176.0 +42.0,-170.0 +42.0,-164.0 +38.0,-176.0 +33.0,-180.0 +40.0,-188.0 +51.0,-186.0 +59.0,-183.0 +45.0,-180.0 +32.0,-186.0 +35.0,-195.0 +46.0,-195.0 +53.0,-191.0 diff --git a/src/data_morph/data/starter_shapes/soccer_ball.csv b/src/data_morph/data/starter_shapes/soccer_ball.csv new file mode 100644 index 00000000..32ae9136 --- /dev/null +++ b/src/data_morph/data/starter_shapes/soccer_ball.csv @@ -0,0 +1,2220 @@ +x,y +15.42056074766355,-8.878504672897197 +14.485981308411215,-34.57943925233645 +33.87850467289719,-12.149532710280374 +43.22429906542056,-32.242990654205606 +13.785046728971963,-33.64485981308411 +30.373831775700936,-37.149532710280376 +33.177570093457945,-32.71028037383178 +13.08411214953271,-20.794392523364486 +28.97196261682243,-38.08411214953271 +47.66355140186916,-29.906542056074766 +32.47663551401869,-32.71028037383178 +26.869158878504674,-42.05607476635514 +33.177570093457945,-13.08411214953271 +15.654205607476635,-19.1588785046729 +21.261682242990656,-7.710280373831775 +28.738317757009344,-39.018691588785046 +33.64485981308411,-22.19626168224299 +29.906542056074766,-37.149532710280376 +16.588785046728972,-21.02803738317757 +29.906542056074766,-19.626168224299064 +16.822429906542055,-25.934579439252335 +18.69158878504673,-22.897196261682243 +28.037383177570092,-20.093457943925234 +19.626168224299064,-25.233644859813083 +23.598130841121495,-40.654205607476634 +32.00934579439252,-32.47663551401869 +15.88785046728972,-18.69158878504673 +15.42056074766355,-30.14018691588785 +19.39252336448598,-26.16822429906542 +15.186915887850468,-26.635514018691588 +46.02803738317757,-30.373831775700936 +16.35514018691589,-8.177570093457945 +48.83177570093458,-19.85981308411215 +33.41121495327103,-20.794392523364486 +36.21495327102804,-20.794392523364486 +25.46728971962617,-7.242990654205608 +18.925233644859812,-26.401869158878505 +21.02803738317757,-7.4766355140186915 +23.364485981308412,-3.0373831775700935 +25.0,-20.093457943925234 +25.46728971962617,-20.327102803738317 +17.289719626168225,-22.897196261682243 +36.44859813084112,-15.186915887850468 +32.47663551401869,-15.88785046728972 +22.19626168224299,-6.308411214953271 +21.72897196261682,-5.607476635514018 +18.69158878504673,-9.813084112149532 +32.71028037383178,-39.7196261682243 +19.626168224299064,-38.78504672897196 +18.22429906542056,-5.373831775700935 +48.598130841121495,-26.869158878504674 +18.925233644859812,-5.140186915887851 +13.317757009345794,-26.869158878504674 +10.046728971962617,-26.16822429906542 +35.981308411214954,-15.42056074766355 +26.16822429906542,-36.44859813084112 +39.48598130841121,-7.710280373831775 +27.102803738317757,-35.981308411214954 +32.71028037383178,-27.102803738317757 +44.39252336448598,-30.60747663551402 +12.850467289719626,-11.448598130841122 +32.242990654205606,-29.906542056074766 +31.074766355140188,-11.91588785046729 +18.22429906542056,-5.607476635514018 +37.149532710280376,-14.018691588785046 +35.74766355140187,-13.08411214953271 +37.38317757009346,-19.626168224299064 +17.05607476635514,-25.700934579439252 +18.925233644859812,-25.0 +27.570093457943926,-35.981308411214954 +19.1588785046729,-22.897196261682243 +22.897196261682243,-7.4766355140186915 +42.757009345794394,-7.009345794392523 +38.78504672897196,-5.140186915887851 +47.66355140186916,-27.570093457943926 +46.495327102803735,-29.906542056074766 +44.626168224299064,-35.981308411214954 +35.51401869158879,-40.654205607476634 +31.30841121495327,-11.91588785046729 +31.542056074766354,-18.457943925233646 +22.19626168224299,-22.19626168224299 +40.654205607476634,-38.3177570093458 +38.55140186915888,-17.05607476635514 +28.97196261682243,-13.785046728971963 +15.88785046728972,-20.327102803738317 +35.046728971962615,-14.953271028037383 +31.77570093457944,-41.822429906542055 +40.88785046728972,-33.64485981308411 +49.532710280373834,-19.39252336448598 +32.71028037383178,-40.42056074766355 +49.299065420560744,-19.85981308411215 +31.77570093457944,-13.551401869158878 +11.91588785046729,-26.16822429906542 +10.981308411214954,-14.953271028037383 +34.81308411214953,-38.78504672897196 +34.81308411214953,-41.35514018691589 +27.102803738317757,-2.336448598130841 +35.981308411214954,-17.990654205607477 +32.47663551401869,-36.21495327102804 +45.32710280373832,-22.897196261682243 +33.177570093457945,-12.850467289719626 +48.13084112149533,-25.934579439252335 +32.242990654205606,-34.81308411214953 +27.570093457943926,-10.046728971962617 +31.542056074766354,-41.58878504672897 +23.598130841121495,-6.775700934579439 +48.598130841121495,-28.738317757009344 +16.35514018691589,-29.906542056074766 +50.0,-21.72897196261682 +9.813084112149532,-23.130841121495326 +48.83177570093458,-21.02803738317757 +48.36448598130841,-14.953271028037383 +21.02803738317757,-5.373831775700935 +32.47663551401869,-41.58878504672897 +24.299065420560748,-34.81308411214953 +22.897196261682243,-4.4392523364485985 +36.6822429906542,-15.654205607476635 +32.94392523364486,-13.08411214953271 +42.757009345794394,-7.242990654205608 +31.30841121495327,-36.91588785046729 +49.06542056074766,-26.401869158878505 +17.05607476635514,-21.49532710280374 +33.177570093457945,-20.560747663551403 +33.177570093457945,-24.766355140186917 +33.177570093457945,-21.02803738317757 +35.74766355140187,-11.91588785046729 +17.523364485981308,-12.149532710280374 +29.205607476635514,-37.149532710280376 +16.822429906542055,-20.327102803738317 +49.06542056074766,-17.757009345794394 +47.66355140186916,-29.672897196261683 +18.22429906542056,-10.046728971962617 +47.66355140186916,-21.02803738317757 +16.121495327102803,-24.53271028037383 +32.242990654205606,-38.08411214953271 +35.74766355140187,-12.149532710280374 +13.551401869158878,-20.093457943925234 +35.74766355140187,-16.588785046728972 +32.242990654205606,-21.49532710280374 +10.514018691588785,-17.05607476635514 +27.33644859813084,-38.3177570093458 +48.13084112149533,-27.33644859813084 +36.44859813084112,-14.953271028037383 +30.60747663551402,-33.41121495327103 +17.990654205607477,-21.261682242990656 +10.981308411214954,-17.289719626168225 +31.074766355140188,-14.485981308411215 +28.037383177570092,-35.51401869158879 +31.30841121495327,-13.551401869158878 +30.8411214953271,-35.046728971962615 +36.44859813084112,-21.02803738317757 +33.41121495327103,-11.91588785046729 +28.738317757009344,-39.7196261682243 +28.27102803738318,-42.05607476635514 +17.757009345794394,-20.327102803738317 +25.46728971962617,-40.18691588785047 +39.7196261682243,-34.11214953271028 +12.616822429906541,-25.46728971962617 +47.89719626168224,-22.429906542056074 +17.757009345794394,-25.0 +35.74766355140187,-14.719626168224298 +47.19626168224299,-25.233644859813083 +11.91588785046729,-22.897196261682243 +13.785046728971963,-24.299065420560748 +31.77570093457944,-20.794392523364486 +39.953271028037385,-19.85981308411215 +39.953271028037385,-7.009345794392523 +49.299065420560744,-21.962616822429908 +26.401869158878505,-40.18691588785047 +39.018691588785046,-18.925233644859812 +21.02803738317757,-22.897196261682243 +29.906542056074766,-38.08411214953271 +33.177570093457945,-36.6822429906542 +19.85981308411215,-31.542056074766354 +11.214953271028037,-16.35514018691589 +27.570093457943926,-36.91588785046729 +29.906542056074766,-18.22429906542056 +19.626168224299064,-24.065420560747665 +32.00934579439252,-21.72897196261682 +32.47663551401869,-17.523364485981308 +25.934579439252335,-7.94392523364486 +28.27102803738318,-17.289719626168225 +46.728971962616825,-32.47663551401869 +35.74766355140187,-40.42056074766355 +35.51401869158879,-16.588785046728972 +34.345794392523366,-21.49532710280374 +32.94392523364486,-28.037383177570092 +24.299065420560748,-5.841121495327103 +45.794392523364486,-22.897196261682243 +18.69158878504673,-30.14018691588785 +14.719626168224298,-29.439252336448597 +32.71028037383178,-11.91588785046729 +46.96261682242991,-27.33644859813084 +17.05607476635514,-26.401869158878505 +27.33644859813084,-3.2710280373831777 +18.22429906542056,-10.514018691588785 +12.616822429906541,-28.50467289719626 +48.13084112149533,-21.962616822429908 +33.64485981308411,-35.2803738317757 +45.5607476635514,-27.102803738317757 +49.299065420560744,-17.757009345794394 +27.33644859813084,-39.48598130841121 +26.401869158878505,-20.093457943925234 +37.38317757009346,-15.654205607476635 +37.149532710280376,-18.69158878504673 +41.12149532710281,-38.55140186915888 +30.14018691588785,-21.72897196261682 +46.26168224299065,-25.233644859813083 +12.616822429906541,-11.91588785046729 +22.429906542056074,-40.18691588785047 +19.1588785046729,-29.205607476635514 +30.373831775700936,-35.51401869158879 +35.046728971962615,-39.953271028037385 +34.81308411214953,-16.35514018691589 +13.317757009345794,-28.97196261682243 +37.149532710280376,-21.261682242990656 +27.80373831775701,-39.7196261682243 +32.00934579439252,-39.953271028037385 +19.626168224299064,-4.4392523364485985 +33.64485981308411,-41.35514018691589 +21.261682242990656,-6.074766355140187 +43.691588785046726,-7.710280373831775 +26.16822429906542,-36.21495327102804 +42.28971962616822,-21.261682242990656 +9.813084112149532,-22.19626168224299 +46.495327102803735,-22.429906542056074 +31.074766355140188,-13.08411214953271 +13.551401869158878,-28.738317757009344 +33.64485981308411,-21.02803738317757 +13.551401869158878,-22.19626168224299 +32.47663551401869,-23.364485981308412 +48.83177570093458,-26.401869158878505 +31.77570093457944,-34.345794392523366 +25.934579439252335,-35.981308411214954 +47.66355140186916,-23.364485981308412 +37.149532710280376,-13.08411214953271 +23.130841121495326,-21.72897196261682 +40.654205607476634,-5.140186915887851 +29.439252336448597,-39.48598130841121 +44.1588785046729,-31.542056074766354 +47.429906542056074,-30.14018691588785 +14.25233644859813,-21.49532710280374 +18.457943925233646,-7.009345794392523 +14.018691588785046,-26.869158878504674 +48.83177570093458,-17.289719626168225 +32.94392523364486,-26.16822429906542 +49.299065420560744,-24.766355140186917 +44.39252336448598,-22.19626168224299 +29.439252336448597,-39.953271028037385 +12.616822429906541,-28.738317757009344 +47.429906542056074,-23.364485981308412 +46.495327102803735,-11.91588785046729 +29.906542056074766,-18.69158878504673 +35.2803738317757,-15.42056074766355 +13.551401869158878,-24.065420560747665 +15.42056074766355,-19.1588785046729 +15.88785046728972,-26.635514018691588 +39.7196261682243,-19.85981308411215 +36.21495327102804,-20.093457943925234 +26.869158878504674,-37.149532710280376 +17.757009345794394,-29.205607476635514 +33.87850467289719,-17.289719626168225 +35.046728971962615,-20.327102803738317 +26.401869158878505,-36.91588785046729 +34.81308411214953,-12.149532710280374 +32.71028037383178,-27.80373831775701 +47.89719626168224,-14.953271028037383 +32.242990654205606,-2.102803738317757 +15.88785046728972,-22.897196261682243 +35.981308411214954,-21.962616822429908 +15.88785046728972,-18.22429906542056 +32.242990654205606,-31.30841121495327 +17.523364485981308,-21.261682242990656 +14.953271028037383,-35.2803738317757 +26.401869158878505,-2.336448598130841 +31.30841121495327,-38.78504672897196 +21.49532710280374,-39.7196261682243 +15.654205607476635,-16.121495327102803 +21.72897196261682,-7.94392523364486 +28.97196261682243,-33.87850467289719 +28.97196261682243,-10.981308411214954 +34.345794392523366,-39.7196261682243 +31.77570093457944,-31.542056074766354 +15.654205607476635,-35.981308411214954 +26.635514018691588,-36.21495327102804 +34.57943925233645,-34.81308411214953 +17.523364485981308,-8.411214953271028 +33.87850467289719,-39.018691588785046 +21.962616822429908,-21.962616822429908 +21.02803738317757,-3.97196261682243 +14.25233644859813,-23.598130841121495 +24.065420560747665,-20.560747663551403 +17.05607476635514,-7.009345794392523 +18.69158878504673,-10.280373831775702 +14.719626168224298,-31.30841121495327 +22.897196261682243,-3.2710280373831777 +11.682242990654206,-29.439252336448597 +28.27102803738318,-15.42056074766355 +30.14018691588785,-39.018691588785046 +31.30841121495327,-32.00934579439252 +32.94392523364486,-34.57943925233645 +13.317757009345794,-11.448598130841122 +14.953271028037383,-21.962616822429908 +47.89719626168224,-28.738317757009344 +34.81308411214953,-34.57943925233645 +16.121495327102803,-20.794392523364486 +49.532710280373834,-25.0 +44.39252336448598,-22.429906542056074 +31.77570093457944,-15.88785046728972 +28.97196261682243,-39.48598130841121 +30.14018691588785,-18.22429906542056 +46.495327102803735,-28.037383177570092 +15.186915887850468,-19.85981308411215 +32.242990654205606,-28.738317757009344 +9.57943925233645,-22.897196261682243 +33.87850467289719,-14.953271028037383 +13.08411214953271,-11.214953271028037 +14.485981308411215,-27.102803738317757 +13.08411214953271,-30.14018691588785 +13.551401869158878,-29.906542056074766 +14.719626168224298,-9.813084112149532 +17.757009345794394,-10.981308411214954 +29.906542056074766,-21.02803738317757 +21.962616822429908,-7.242990654205608 +36.91588785046729,-17.289719626168225 +46.96261682242991,-23.364485981308412 +28.50467289719626,-14.953271028037383 +33.177570093457945,-12.616822429906541 +35.981308411214954,-34.81308411214953 +38.78504672897196,-17.757009345794394 +47.66355140186916,-25.934579439252335 +13.551401869158878,-28.50467289719626 +32.242990654205606,-35.51401869158879 +38.55140186915888,-18.925233644859812 +13.08411214953271,-32.00934579439252 +19.1588785046729,-27.33644859813084 +12.850467289719626,-26.869158878504674 +33.87850467289719,-35.2803738317757 +38.78504672897196,-18.925233644859812 +33.177570093457945,-38.3177570093458 +12.850467289719626,-24.065420560747665 +16.822429906542055,-13.317757009345794 +28.97196261682243,-14.018691588785046 +21.02803738317757,-7.242990654205608 +17.289719626168225,-20.093457943925234 +43.925233644859816,-36.6822429906542 +19.85981308411215,-6.542056074766355 +12.383177570093459,-27.33644859813084 +46.02803738317757,-34.11214953271028 +46.495327102803735,-30.373831775700936 +18.22429906542056,-22.897196261682243 +28.50467289719626,-17.05607476635514 +33.41121495327103,-34.11214953271028 +10.046728971962617,-18.22429906542056 +25.0,-5.607476635514018 +29.205607476635514,-10.981308411214954 +36.6822429906542,-36.91588785046729 +19.626168224299064,-5.607476635514018 +29.439252336448597,-15.88785046728972 +17.289719626168225,-6.074766355140187 +33.41121495327103,-22.66355140186916 +26.869158878504674,-3.2710280373831777 +30.373831775700936,-37.61682242990654 +47.66355140186916,-23.83177570093458 +40.18691588785047,-34.11214953271028 +17.289719626168225,-25.0 +34.345794392523366,-22.897196261682243 +30.14018691588785,-18.69158878504673 +19.85981308411215,-31.074766355140188 +47.66355140186916,-24.53271028037383 +23.364485981308412,-7.009345794392523 +19.626168224299064,-22.429906542056074 +40.18691588785047,-20.327102803738317 +14.485981308411215,-35.046728971962615 +44.85981308411215,-35.046728971962615 +36.21495327102804,-14.953271028037383 +37.38317757009346,-13.785046728971963 +45.32710280373832,-28.50467289719626 +19.1588785046729,-26.401869158878505 +27.33644859813084,-18.457943925233646 +30.14018691588785,-12.149532710280374 +16.588785046728972,-24.766355140186917 +42.28971962616822,-32.94392523364486 +35.981308411214954,-18.457943925233646 +36.91588785046729,-41.12149532710281 +20.327102803738317,-7.710280373831775 +38.55140186915888,-18.457943925233646 +37.61682242990654,-34.81308411214953 +18.22429906542056,-28.738317757009344 +47.89719626168224,-21.72897196261682 +28.50467289719626,-39.7196261682243 +23.83177570093458,-5.140186915887851 +14.018691588785046,-28.27102803738318 +28.97196261682243,-15.186915887850468 +26.401869158878505,-34.81308411214953 +11.682242990654206,-24.065420560747665 +28.037383177570092,-17.990654205607477 +31.542056074766354,-20.560747663551403 +21.72897196261682,-5.841121495327103 +25.934579439252335,-5.140186915887851 +15.654205607476635,-29.672897196261683 +46.728971962616825,-29.672897196261683 +47.429906542056074,-30.8411214953271 +20.560747663551403,-22.897196261682243 +26.635514018691588,-38.78504672897196 +35.046728971962615,-19.39252336448598 +18.925233644859812,-5.841121495327103 +28.50467289719626,-20.560747663551403 +14.018691588785046,-32.242990654205606 +38.08411214953271,-18.457943925233646 +33.41121495327103,-33.41121495327103 +21.72897196261682,-22.429906542056074 +35.51401869158879,-33.87850467289719 +13.551401869158878,-11.448598130841122 +31.074766355140188,-37.149532710280376 +46.728971962616825,-22.897196261682243 +16.822429906542055,-28.037383177570092 +35.046728971962615,-35.74766355140187 +46.495327102803735,-29.672897196261683 +46.495327102803735,-28.27102803738318 +32.94392523364486,-24.065420560747665 +15.42056074766355,-17.289719626168225 +13.785046728971963,-20.794392523364486 +16.35514018691589,-6.775700934579439 +32.242990654205606,-13.08411214953271 +21.49532710280374,-6.074766355140187 +30.8411214953271,-35.2803738317757 +31.77570093457944,-32.47663551401869 +18.925233644859812,-21.962616822429908 +48.83177570093458,-19.1588785046729 +17.990654205607477,-24.299065420560748 +9.813084112149532,-21.261682242990656 +25.0,-2.336448598130841 +36.91588785046729,-16.121495327102803 +29.205607476635514,-35.046728971962615 +47.66355140186916,-30.8411214953271 +21.261682242990656,-39.7196261682243 +39.7196261682243,-4.672897196261682 +34.345794392523366,-2.336448598130841 +35.981308411214954,-14.953271028037383 +39.25233644859813,-8.644859813084112 +26.16822429906542,-41.35514018691589 +31.074766355140188,-35.046728971962615 +35.046728971962615,-15.88785046728972 +32.00934579439252,-22.66355140186916 +48.13084112149533,-14.485981308411215 +33.87850467289719,-19.85981308411215 +25.0,-41.35514018691589 +48.598130841121495,-21.962616822429908 +14.25233644859813,-25.700934579439252 +35.046728971962615,-18.925233644859812 +36.44859813084112,-13.317757009345794 +45.32710280373832,-23.598130841121495 +38.78504672897196,-17.289719626168225 +40.18691588785047,-6.074766355140187 +35.046728971962615,-21.261682242990656 +34.11214953271028,-33.177570093457945 +13.785046728971963,-21.72897196261682 +32.00934579439252,-37.149532710280376 +38.78504672897196,-8.411214953271028 +12.850467289719626,-12.616822429906541 +35.2803738317757,-18.925233644859812 +17.523364485981308,-22.66355140186916 +34.81308411214953,-17.05607476635514 +34.57943925233645,-34.57943925233645 +44.39252336448598,-22.66355140186916 +31.542056074766354,-31.77570093457944 +33.41121495327103,-23.598130841121495 +11.214953271028037,-14.719626168224298 +28.037383177570092,-41.822429906542055 +31.074766355140188,-21.261682242990656 +29.906542056074766,-37.850467289719624 +34.81308411214953,-22.429906542056074 +26.401869158878505,-40.42056074766355 +35.2803738317757,-2.5700934579439254 +18.69158878504673,-25.0 +32.71028037383178,-33.87850467289719 +14.018691588785046,-28.738317757009344 +17.05607476635514,-12.149532710280374 +18.457943925233646,-21.02803738317757 +37.850467289719624,-17.05607476635514 +35.981308411214954,-34.345794392523366 +35.2803738317757,-40.654205607476634 +30.60747663551402,-18.457943925233646 +35.74766355140187,-34.11214953271028 +32.00934579439252,-16.588785046728972 +25.934579439252335,-3.97196261682243 +28.738317757009344,-16.121495327102803 +16.822429906542055,-21.02803738317757 +34.81308411214953,-13.785046728971963 +30.373831775700936,-40.18691588785047 +28.738317757009344,-18.925233644859812 +32.47663551401869,-23.598130841121495 +15.88785046728972,-7.4766355140186915 +13.08411214953271,-27.102803738317757 +18.925233644859812,-7.4766355140186915 +38.08411214953271,-10.046728971962617 +45.5607476635514,-9.813084112149532 +20.327102803738317,-8.411214953271028 +33.64485981308411,-11.91588785046729 +15.654205607476635,-31.074766355140188 +46.96261682242991,-32.47663551401869 +21.02803738317757,-8.411214953271028 +14.018691588785046,-31.074766355140188 +12.850467289719626,-28.97196261682243 +49.06542056074766,-16.822429906542055 +47.66355140186916,-29.439252336448597 +30.373831775700936,-12.383177570093459 +29.439252336448597,-33.87850467289719 +33.41121495327103,-16.35514018691589 +45.5607476635514,-23.598130841121495 +35.046728971962615,-36.6822429906542 +29.906542056074766,-11.448598130841122 +26.635514018691588,-39.25233644859813 +18.925233644859812,-24.766355140186917 +33.64485981308411,-16.822429906542055 +33.41121495327103,-37.149532710280376 +31.77570093457944,-34.11214953271028 +34.11214953271028,-17.990654205607477 +25.46728971962617,-4.4392523364485985 +47.429906542056074,-25.46728971962617 +26.635514018691588,-39.953271028037385 +24.766355140186917,-41.58878504672897 +48.36448598130841,-15.88785046728972 +15.88785046728972,-7.94392523364486 +31.542056074766354,-34.11214953271028 +17.05607476635514,-24.766355140186917 +17.757009345794394,-7.94392523364486 +34.345794392523366,-18.925233644859812 +37.149532710280376,-16.588785046728972 +34.81308411214953,-34.345794392523366 +20.794392523364486,-8.411214953271028 +16.588785046728972,-25.46728971962617 +15.42056074766355,-18.925233644859812 +48.83177570093458,-17.757009345794394 +30.8411214953271,-14.018691588785046 +46.02803738317757,-24.065420560747665 +31.074766355140188,-34.345794392523366 +46.728971962616825,-26.635514018691588 +35.2803738317757,-36.6822429906542 +10.280373831775702,-25.233644859813083 +22.66355140186916,-3.97196261682243 +35.74766355140187,-16.35514018691589 +28.037383177570092,-37.149532710280376 +16.121495327102803,-14.719626168224298 +35.74766355140187,-37.850467289719624 +40.18691588785047,-19.626168224299064 +35.2803738317757,-11.91588785046729 +27.80373831775701,-2.5700934579439254 +19.85981308411215,-39.018691588785046 +14.485981308411215,-29.205607476635514 +34.345794392523366,-17.757009345794394 +32.71028037383178,-28.037383177570092 +14.25233644859813,-27.33644859813084 +36.21495327102804,-19.85981308411215 +12.616822429906541,-22.897196261682243 +31.77570093457944,-18.69158878504673 +31.77570093457944,-20.093457943925234 +46.728971962616825,-25.46728971962617 +40.18691588785047,-33.64485981308411 +21.72897196261682,-39.953271028037385 +46.495327102803735,-22.66355140186916 +16.35514018691589,-14.719626168224298 +18.457943925233646,-25.0 +32.71028037383178,-34.345794392523366 +19.39252336448598,-7.94392523364486 +16.121495327102803,-21.49532710280374 +26.401869158878505,-38.78504672897196 +18.457943925233646,-6.775700934579439 +24.065420560747665,-7.009345794392523 +14.953271028037383,-21.72897196261682 +45.09345794392523,-9.345794392523365 +27.33644859813084,-37.850467289719624 +28.97196261682243,-34.345794392523366 +37.149532710280376,-11.91588785046729 +21.49532710280374,-32.242990654205606 +27.33644859813084,-36.6822429906542 +19.39252336448598,-8.644859813084112 +32.242990654205606,-35.74766355140187 +26.16822429906542,-2.336448598130841 +43.691588785046726,-22.19626168224299 +23.364485981308412,-4.4392523364485985 +16.35514018691589,-23.364485981308412 +36.44859813084112,-20.794392523364486 +18.925233644859812,-4.906542056074766 +38.3177570093458,-3.97196261682243 +38.3177570093458,-3.7383177570093458 +22.66355140186916,-40.18691588785047 +17.990654205607477,-27.33644859813084 +34.57943925233645,-21.72897196261682 +28.037383177570092,-37.38317757009346 +46.495327102803735,-24.766355140186917 +32.242990654205606,-35.046728971962615 +35.74766355140187,-41.12149532710281 +10.514018691588785,-16.35514018691589 +24.065420560747665,-3.7383177570093458 +42.52336448598131,-21.261682242990656 +12.850467289719626,-29.672897196261683 +17.757009345794394,-7.009345794392523 +31.77570093457944,-22.897196261682243 +28.27102803738318,-36.6822429906542 +32.71028037383178,-23.130841121495326 +27.33644859813084,-18.69158878504673 +29.205607476635514,-35.74766355140187 +16.822429906542055,-6.542056074766355 +35.046728971962615,-13.317757009345794 +34.345794392523366,-35.2803738317757 +33.177570093457945,-32.94392523364486 +17.289719626168225,-24.53271028037383 +22.66355140186916,-21.49532710280374 +32.71028037383178,-14.018691588785046 +28.037383177570092,-10.046728971962617 +25.0,-6.775700934579439 +14.018691588785046,-34.57943925233645 +34.345794392523366,-38.3177570093458 +45.32710280373832,-10.046728971962617 +29.906542056074766,-33.41121495327103 +42.52336448598131,-32.47663551401869 +42.28971962616822,-37.61682242990654 +13.551401869158878,-21.72897196261682 +25.700934579439252,-5.140186915887851 +34.81308411214953,-14.018691588785046 +11.682242990654206,-13.08411214953271 +42.28971962616822,-7.009345794392523 +32.71028037383178,-12.616822429906541 +19.39252336448598,-7.242990654205608 +17.757009345794394,-24.766355140186917 +18.69158878504673,-29.439252336448597 +19.1588785046729,-27.570093457943926 +38.55140186915888,-16.35514018691589 +17.990654205607477,-8.411214953271028 +14.25233644859813,-21.72897196261682 +36.21495327102804,-12.850467289719626 +25.700934579439252,-5.373831775700935 +34.57943925233645,-15.186915887850468 +15.654205607476635,-28.97196261682243 +17.289719626168225,-8.177570093457945 +47.89719626168224,-26.16822429906542 +10.046728971962617,-22.897196261682243 +29.906542056074766,-11.682242990654206 +29.205607476635514,-38.55140186915888 +13.08411214953271,-32.71028037383178 +29.439252336448597,-16.822429906542055 +15.654205607476635,-30.60747663551402 +23.364485981308412,-3.97196261682243 +47.19626168224299,-27.570093457943926 +31.542056074766354,-22.19626168224299 +13.08411214953271,-21.02803738317757 +30.14018691588785,-16.822429906542055 +19.1588785046729,-9.813084112149532 +26.635514018691588,-37.850467289719624 +18.22429906542056,-7.94392523364486 +19.626168224299064,-5.140186915887851 +25.0,-4.205607476635514 +33.64485981308411,-2.803738317757009 +36.21495327102804,-37.850467289719624 +39.7196261682243,-6.542056074766355 +40.88785046728972,-6.775700934579439 +39.7196261682243,-7.009345794392523 +37.38317757009346,-35.2803738317757 +38.55140186915888,-17.289719626168225 +13.551401869158878,-23.364485981308412 +26.401869158878505,-39.25233644859813 +32.47663551401869,-20.794392523364486 +46.26168224299065,-28.738317757009344 +25.700934579439252,-40.88785046728972 +31.30841121495327,-34.345794392523366 +18.457943925233646,-28.27102803738318 +35.2803738317757,-39.953271028037385 +18.925233644859812,-25.233644859813083 +15.654205607476635,-17.757009345794394 +20.560747663551403,-39.953271028037385 +47.19626168224299,-31.542056074766354 +20.093457943925234,-22.19626168224299 +15.654205607476635,-18.69158878504673 +38.08411214953271,-17.289719626168225 +24.065420560747665,-34.11214953271028 +48.13084112149533,-15.42056074766355 +30.373831775700936,-17.757009345794394 +37.38317757009346,-14.953271028037383 +37.149532710280376,-35.74766355140187 +31.30841121495327,-13.785046728971963 +30.8411214953271,-41.822429906542055 +20.093457943925234,-4.672897196261682 +11.682242990654206,-22.66355140186916 +33.41121495327103,-13.317757009345794 +14.018691588785046,-33.41121495327103 +15.88785046728972,-17.523364485981308 +30.8411214953271,-15.88785046728972 +33.64485981308411,-14.485981308411215 +19.626168224299064,-6.074766355140187 +14.018691588785046,-27.102803738317757 +39.7196261682243,-6.308411214953271 +31.542056074766354,-18.69158878504673 +29.672897196261683,-38.3177570093458 +17.990654205607477,-24.53271028037383 +33.64485981308411,-21.72897196261682 +32.71028037383178,-39.48598130841121 +34.11214953271028,-11.448598130841122 +12.850467289719626,-11.91588785046729 +33.64485981308411,-39.48598130841121 +46.728971962616825,-28.27102803738318 +29.439252336448597,-34.81308411214953 +47.19626168224299,-29.439252336448597 +28.27102803738318,-34.57943925233645 +32.00934579439252,-15.42056074766355 +39.7196261682243,-5.373831775700935 +50.0,-23.364485981308412 +15.88785046728972,-26.401869158878505 +11.91588785046729,-24.766355140186917 +39.7196261682243,-39.25233644859813 +32.94392523364486,-32.47663551401869 +28.738317757009344,-33.87850467289719 +34.11214953271028,-14.719626168224298 +32.71028037383178,-33.177570093457945 +24.299065420560748,-20.794392523364486 +33.177570093457945,-2.102803738317757 +46.728971962616825,-28.97196261682243 +16.35514018691589,-15.186915887850468 +45.794392523364486,-24.53271028037383 +32.00934579439252,-35.51401869158879 +19.626168224299064,-6.542056074766355 +29.906542056074766,-33.64485981308411 +28.738317757009344,-15.654205607476635 +49.76635514018692,-20.093457943925234 +17.289719626168225,-28.037383177570092 +37.850467289719624,-14.018691588785046 +23.83177570093458,-21.261682242990656 +25.700934579439252,-7.710280373831775 +16.822429906542055,-27.80373831775701 +18.925233644859812,-30.60747663551402 +15.42056074766355,-30.60747663551402 +27.80373831775701,-34.81308411214953 +47.429906542056074,-29.906542056074766 +30.373831775700936,-36.21495327102804 +24.299065420560748,-6.074766355140187 +34.81308411214953,-20.794392523364486 +48.36448598130841,-15.186915887850468 +31.074766355140188,-13.785046728971963 +10.981308411214954,-29.205607476635514 +12.149532710280374,-30.8411214953271 +15.88785046728972,-7.242990654205608 +38.55140186915888,-4.4392523364485985 +17.05607476635514,-19.626168224299064 +38.08411214953271,-17.523364485981308 +30.373831775700936,-19.39252336448598 +16.822429906542055,-20.560747663551403 +19.626168224299064,-6.308411214953271 +27.80373831775701,-35.046728971962615 +26.869158878504674,-35.981308411214954 +23.598130841121495,-7.009345794392523 +18.457943925233646,-9.57943925233645 +47.66355140186916,-21.49532710280374 +35.2803738317757,-14.25233644859813 +48.36448598130841,-24.53271028037383 +47.89719626168224,-14.485981308411215 +24.766355140186917,-4.4392523364485985 +28.97196261682243,-15.42056074766355 +18.457943925233646,-8.411214953271028 +19.1588785046729,-26.869158878504674 +47.66355140186916,-30.14018691588785 +34.11214953271028,-14.25233644859813 +33.41121495327103,-23.83177570093458 +47.66355140186916,-24.299065420560748 +10.514018691588785,-28.27102803738318 +28.037383177570092,-39.48598130841121 +28.50467289719626,-33.87850467289719 +24.065420560747665,-41.35514018691589 +46.96261682242991,-25.700934579439252 +48.36448598130841,-26.635514018691588 +16.35514018691589,-25.700934579439252 +35.2803738317757,-20.327102803738317 +15.654205607476635,-29.439252336448597 +14.485981308411215,-21.962616822429908 +11.448598130841122,-29.439252336448597 +19.39252336448598,-25.700934579439252 +49.299065420560744,-25.0 +18.457943925233646,-28.50467289719626 +25.233644859813083,-5.140186915887851 +18.925233644859812,-21.72897196261682 +44.85981308411215,-35.74766355140187 +38.55140186915888,-18.69158878504673 +34.345794392523366,-19.39252336448598 +21.261682242990656,-32.242990654205606 +32.00934579439252,-18.22429906542056 +36.91588785046729,-20.093457943925234 +45.794392523364486,-25.934579439252335 +29.672897196261683,-12.149532710280374 +33.87850467289719,-17.05607476635514 +35.2803738317757,-35.74766355140187 +29.906542056074766,-34.81308411214953 +33.41121495327103,-11.448598130841122 +12.383177570093459,-28.27102803738318 +28.037383177570092,-36.91588785046729 +31.77570093457944,-39.7196261682243 +16.35514018691589,-26.635514018691588 +28.50467289719626,-18.925233644859812 +17.990654205607477,-10.981308411214954 +15.186915887850468,-35.046728971962615 +45.5607476635514,-34.57943925233645 +32.242990654205606,-16.588785046728972 +31.77570093457944,-22.429906542056074 +34.57943925233645,-12.616822429906541 +32.47663551401869,-16.822429906542055 +48.598130841121495,-15.654205607476635 +48.83177570093458,-16.588785046728972 +19.626168224299064,-9.11214953271028 +25.46728971962617,-4.906542056074766 +29.439252336448597,-40.18691588785047 +33.87850467289719,-18.925233644859812 +34.11214953271028,-23.598130841121495 +49.532710280373834,-18.457943925233646 +35.981308411214954,-14.25233644859813 +38.3177570093458,-17.289719626168225 +17.990654205607477,-8.644859813084112 +23.364485981308412,-40.42056074766355 +26.869158878504674,-41.58878504672897 +14.485981308411215,-32.94392523364486 +25.233644859813083,-20.560747663551403 +15.654205607476635,-17.289719626168225 +43.691588785046726,-36.44859813084112 +44.626168224299064,-30.373831775700936 +17.289719626168225,-23.83177570093458 +35.74766355140187,-15.42056074766355 +46.26168224299065,-25.46728971962617 +18.925233644859812,-25.46728971962617 +43.22429906542056,-21.962616822429908 +18.22429906542056,-23.130841121495326 +49.299065420560744,-22.19626168224299 +35.51401869158879,-21.02803738317757 +11.682242990654206,-30.60747663551402 +19.1588785046729,-5.140186915887851 +42.52336448598131,-21.49532710280374 +17.757009345794394,-27.80373831775701 +33.177570093457945,-36.21495327102804 +33.41121495327103,-13.08411214953271 +31.30841121495327,-39.48598130841121 +31.77570093457944,-32.242990654205606 +43.925233644859816,-8.177570093457945 +12.149532710280374,-23.364485981308412 +37.61682242990654,-10.514018691588785 +48.598130841121495,-20.093457943925234 +48.13084112149533,-20.327102803738317 +26.635514018691588,-35.046728971962615 +27.570093457943926,-9.345794392523365 +11.448598130841122,-29.672897196261683 +29.439252336448597,-39.25233644859813 +14.485981308411215,-20.093457943925234 +16.121495327102803,-21.02803738317757 +33.64485981308411,-11.448598130841122 +18.22429906542056,-7.242990654205608 +30.14018691588785,-38.55140186915888 +28.738317757009344,-17.05607476635514 +35.046728971962615,-39.018691588785046 +10.981308411214954,-29.439252336448597 +47.89719626168224,-27.33644859813084 +34.57943925233645,-19.85981308411215 +33.177570093457945,-33.64485981308411 +31.77570093457944,-16.588785046728972 +32.71028037383178,-13.08411214953271 +49.06542056074766,-21.49532710280374 +10.514018691588785,-22.429906542056074 +32.94392523364486,-36.44859813084112 +35.981308411214954,-12.850467289719626 +25.46728971962617,-3.0373831775700935 +45.09345794392523,-29.439252336448597 +15.654205607476635,-8.177570093457945 +27.80373831775701,-19.39252336448598 +14.25233644859813,-28.50467289719626 +39.25233644859813,-39.7196261682243 +32.94392523364486,-33.41121495327103 +36.6822429906542,-10.981308411214954 +15.88785046728972,-19.626168224299064 +43.691588785046726,-8.411214953271028 +30.8411214953271,-21.261682242990656 +12.383177570093459,-25.934579439252335 +35.981308411214954,-15.186915887850468 +20.093457943925234,-23.598130841121495 +15.88785046728972,-17.289719626168225 +37.38317757009346,-35.981308411214954 +33.177570093457945,-11.682242990654206 +31.542056074766354,-37.38317757009346 +33.177570093457945,-39.25233644859813 +45.794392523364486,-33.87850467289719 +29.205607476635514,-19.626168224299064 +49.532710280373834,-25.233644859813083 +32.94392523364486,-14.485981308411215 +23.364485981308412,-40.88785046728972 +44.85981308411215,-9.345794392523365 +15.186915887850468,-21.72897196261682 +14.485981308411215,-28.97196261682243 +16.35514018691589,-14.018691588785046 +33.177570093457945,-22.66355140186916 +37.149532710280376,-14.953271028037383 +29.205607476635514,-14.719626168224298 +10.046728971962617,-26.401869158878505 +30.373831775700936,-38.55140186915888 +21.261682242990656,-7.94392523364486 +11.448598130841122,-25.0 +34.81308411214953,-21.962616822429908 +33.177570093457945,-21.962616822429908 +18.925233644859812,-8.644859813084112 +28.97196261682243,-10.74766355140187 +22.429906542056074,-4.672897196261682 +36.6822429906542,-20.560747663551403 +48.598130841121495,-29.205607476635514 +15.88785046728972,-22.19626168224299 +38.3177570093458,-17.990654205607477 +47.89719626168224,-24.299065420560748 +26.869158878504674,-37.61682242990654 +33.64485981308411,-36.44859813084112 +26.16822429906542,-35.981308411214954 +34.11214953271028,-15.42056074766355 +31.074766355140188,-38.55140186915888 +17.757009345794394,-21.72897196261682 +18.457943925233646,-23.83177570093458 +46.26168224299065,-28.037383177570092 +14.25233644859813,-34.81308411214953 +26.16822429906542,-39.953271028037385 +31.77570093457944,-33.177570093457945 +46.495327102803735,-28.738317757009344 +33.64485981308411,-32.94392523364486 +33.41121495327103,-16.121495327102803 +34.345794392523366,-17.990654205607477 +32.47663551401869,-40.18691588785047 +21.02803738317757,-39.7196261682243 +22.66355140186916,-33.64485981308411 +49.76635514018692,-25.700934579439252 +23.598130841121495,-34.11214953271028 +15.186915887850468,-19.39252336448598 +33.41121495327103,-19.85981308411215 +20.093457943925234,-31.074766355140188 +32.71028037383178,-26.16822429906542 +31.30841121495327,-11.682242990654206 +35.74766355140187,-21.962616822429908 +22.66355140186916,-7.009345794392523 +17.289719626168225,-30.8411214953271 +18.457943925233646,-23.130841121495326 +37.850467289719624,-10.514018691588785 +31.542056074766354,-19.39252336448598 +28.738317757009344,-19.626168224299064 +15.88785046728972,-21.02803738317757 +28.97196261682243,-18.925233644859812 +30.8411214953271,-2.336448598130841 +48.83177570093458,-25.0 +47.19626168224299,-29.205607476635514 +10.046728971962617,-17.990654205607477 +14.719626168224298,-31.77570093457944 +29.906542056074766,-34.57943925233645 +15.42056074766355,-7.710280373831775 +10.74766355140187,-23.364485981308412 +20.093457943925234,-7.4766355140186915 +46.26168224299065,-24.299065420560748 +30.8411214953271,-12.149532710280374 +36.91588785046729,-12.149532710280374 +18.22429906542056,-21.962616822429908 +37.38317757009346,-19.1588785046729 +36.21495327102804,-13.317757009345794 +31.074766355140188,-32.47663551401869 +14.018691588785046,-22.66355140186916 +34.57943925233645,-13.551401869158878 +33.64485981308411,-2.5700934579439254 +16.822429906542055,-26.401869158878505 +34.11214953271028,-21.962616822429908 +16.121495327102803,-22.66355140186916 +13.317757009345794,-11.91588785046729 +33.87850467289719,-35.74766355140187 +40.42056074766355,-33.87850467289719 +14.719626168224298,-22.897196261682243 +35.2803738317757,-16.121495327102803 +13.551401869158878,-29.439252336448597 +29.906542056074766,-20.327102803738317 +27.102803738317757,-38.08411214953271 +34.81308411214953,-18.22429906542056 +10.280373831775702,-19.626168224299064 +35.2803738317757,-17.523364485981308 +12.616822429906541,-11.448598130841122 +35.981308411214954,-13.317757009345794 +20.093457943925234,-39.25233644859813 +24.065420560747665,-4.205607476635514 +33.41121495327103,-2.5700934579439254 +31.30841121495327,-39.953271028037385 +18.925233644859812,-27.80373831775701 +32.242990654205606,-38.55140186915888 +29.205607476635514,-12.383177570093459 +30.8411214953271,-14.485981308411215 +19.85981308411215,-23.83177570093458 +35.51401869158879,-35.51401869158879 +30.14018691588785,-33.41121495327103 +25.233644859813083,-4.672897196261682 +18.69158878504673,-38.3177570093458 +32.47663551401869,-39.018691588785046 +29.205607476635514,-18.925233644859812 +32.47663551401869,-34.11214953271028 +28.27102803738318,-39.953271028037385 +11.91588785046729,-24.299065420560748 +15.186915887850468,-19.1588785046729 +29.672897196261683,-17.990654205607477 +27.102803738317757,-36.91588785046729 +34.81308411214953,-22.897196261682243 +35.046728971962615,-35.981308411214954 +20.327102803738317,-4.205607476635514 +47.89719626168224,-22.897196261682243 +32.242990654205606,-28.27102803738318 +34.345794392523366,-23.364485981308412 +18.22429906542056,-26.869158878504674 +12.383177570093459,-13.08411214953271 +23.130841121495326,-33.64485981308411 +48.83177570093458,-28.738317757009344 +38.78504672897196,-4.672897196261682 +18.925233644859812,-38.55140186915888 +38.55140186915888,-17.990654205607477 +31.542056074766354,-13.551401869158878 +32.242990654205606,-19.1588785046729 +10.280373831775702,-24.53271028037383 +25.233644859813083,-3.97196261682243 +31.542056074766354,-2.336448598130841 +34.81308411214953,-11.682242990654206 +49.299065420560744,-28.037383177570092 +31.542056074766354,-14.719626168224298 +43.925233644859816,-21.962616822429908 +13.317757009345794,-25.46728971962617 +49.532710280373834,-20.327102803738317 +34.57943925233645,-3.0373831775700935 +11.91588785046729,-13.785046728971963 +35.51401869158879,-15.654205607476635 +19.39252336448598,-6.542056074766355 +17.289719626168225,-20.560747663551403 +41.35514018691589,-6.308411214953271 +38.55140186915888,-3.97196261682243 +20.560747663551403,-22.66355140186916 +17.523364485981308,-29.672897196261683 +29.906542056074766,-38.55140186915888 +33.177570093457945,-26.16822429906542 +18.22429906542056,-24.299065420560748 +22.897196261682243,-33.177570093457945 +16.35514018691589,-27.80373831775701 +41.12149532710281,-33.64485981308411 +13.551401869158878,-20.560747663551403 +30.60747663551402,-39.48598130841121 +48.83177570093458,-29.205607476635514 +32.00934579439252,-42.05607476635514 +32.242990654205606,-39.25233644859813 +45.32710280373832,-9.813084112149532 +48.13084112149533,-30.14018691588785 +14.018691588785046,-30.60747663551402 +25.934579439252335,-3.5046728971962615 +18.925233644859812,-10.046728971962617 +34.57943925233645,-18.69158878504673 +47.19626168224299,-12.850467289719626 +34.57943925233645,-41.822429906542055 +27.80373831775701,-40.42056074766355 +32.00934579439252,-22.897196261682243 +29.205607476635514,-33.87850467289719 +14.485981308411215,-30.14018691588785 +14.485981308411215,-21.72897196261682 +33.177570093457945,-15.654205607476635 +34.57943925233645,-22.429906542056074 +25.233644859813083,-35.2803738317757 +14.018691588785046,-29.205607476635514 +35.981308411214954,-37.61682242990654 +30.8411214953271,-42.05607476635514 +33.64485981308411,-16.35514018691589 +35.981308411214954,-34.57943925233645 +25.934579439252335,-35.2803738317757 +29.906542056074766,-1.8691588785046729 +30.14018691588785,-33.87850467289719 +18.69158878504673,-29.205607476635514 +21.49532710280374,-39.953271028037385 +36.21495327102804,-38.08411214953271 +26.16822429906542,-38.3177570093458 +16.588785046728972,-21.962616822429908 +34.11214953271028,-17.289719626168225 +38.78504672897196,-20.093457943925234 +17.990654205607477,-30.373831775700936 +36.91588785046729,-19.626168224299064 +34.81308411214953,-3.0373831775700935 +32.47663551401869,-32.94392523364486 +30.60747663551402,-39.25233644859813 +10.046728971962617,-18.457943925233646 +29.205607476635514,-1.8691588785046729 +16.121495327102803,-28.037383177570092 +23.83177570093458,-34.57943925233645 +37.61682242990654,-11.214953271028037 +17.523364485981308,-20.560747663551403 +12.850467289719626,-22.19626168224299 +23.364485981308412,-33.64485981308411 +25.233644859813083,-35.046728971962615 +15.654205607476635,-20.327102803738317 +37.149532710280376,-17.289719626168225 +22.897196261682243,-21.261682242990656 +36.44859813084112,-11.448598130841122 +32.242990654205606,-34.345794392523366 +15.186915887850468,-29.205607476635514 +20.794392523364486,-7.94392523364486 +12.149532710280374,-30.60747663551402 +14.018691588785046,-28.50467289719626 +30.8411214953271,-37.38317757009346 +46.96261682242991,-22.897196261682243 +46.96261682242991,-26.869158878504674 +17.289719626168225,-12.383177570093459 +36.44859813084112,-12.383177570093459 +40.88785046728972,-20.093457943925234 +28.738317757009344,-16.35514018691589 +49.532710280373834,-20.560747663551403 +30.14018691588785,-38.3177570093458 +34.57943925233645,-37.61682242990654 +30.14018691588785,-19.1588785046729 +44.39252336448598,-8.644859813084112 +21.72897196261682,-6.074766355140187 +35.046728971962615,-39.7196261682243 +15.654205607476635,-27.33644859813084 +32.71028037383178,-29.439252336448597 +36.44859813084112,-35.74766355140187 +28.738317757009344,-37.149532710280376 +16.121495327102803,-21.72897196261682 +32.71028037383178,-2.5700934579439254 +34.81308411214953,-12.850467289719626 +10.74766355140187,-23.598130841121495 +32.00934579439252,-31.542056074766354 +12.850467289719626,-23.598130841121495 +27.102803738317757,-2.102803738317757 +17.05607476635514,-37.38317757009346 +39.953271028037385,-5.607476635514018 +30.8411214953271,-17.05607476635514 +19.39252336448598,-29.439252336448597 +13.08411214953271,-33.177570093457945 +23.598130841121495,-6.542056074766355 +26.869158878504674,-35.74766355140187 +26.869158878504674,-34.345794392523366 +31.30841121495327,-21.49532710280374 +43.925233644859816,-7.94392523364486 +24.53271028037383,-20.327102803738317 +16.35514018691589,-36.91588785046729 +28.738317757009344,-35.74766355140187 +35.2803738317757,-35.2803738317757 +23.598130841121495,-2.803738317757009 +28.97196261682243,-16.822429906542055 +29.439252336448597,-17.289719626168225 +30.60747663551402,-33.87850467289719 +34.57943925233645,-37.149532710280376 +27.102803738317757,-36.44859813084112 +35.981308411214954,-18.925233644859812 +28.50467289719626,-2.102803738317757 +49.532710280373834,-18.22429906542056 +31.77570093457944,-37.61682242990654 +17.289719626168225,-37.149532710280376 +14.953271028037383,-34.57943925233645 +10.046728971962617,-23.83177570093458 +37.850467289719624,-18.22429906542056 +17.05607476635514,-7.4766355140186915 +15.186915887850468,-21.962616822429908 +17.757009345794394,-28.037383177570092 +36.21495327102804,-17.05607476635514 +35.2803738317757,-37.38317757009346 +30.8411214953271,-39.48598130841121 +18.22429906542056,-6.775700934579439 +21.72897196261682,-32.47663551401869 +33.64485981308411,-22.897196261682243 +19.626168224299064,-31.30841121495327 +35.2803738317757,-21.02803738317757 +32.71028037383178,-19.1588785046729 +17.990654205607477,-7.4766355140186915 +31.542056074766354,-16.588785046728972 +18.69158878504673,-24.299065420560748 +34.81308411214953,-11.214953271028037 +25.233644859813083,-3.2710280373831777 +36.21495327102804,-35.51401869158879 +48.13084112149533,-24.53271028037383 +29.672897196261683,-19.85981308411215 +21.72897196261682,-6.775700934579439 +22.897196261682243,-3.5046728971962615 +48.83177570093458,-18.457943925233646 +21.02803738317757,-8.177570093457945 +29.439252336448597,-15.42056074766355 +16.121495327102803,-30.8411214953271 +21.49532710280374,-4.205607476635514 +35.981308411214954,-36.6822429906542 +32.71028037383178,-28.50467289719626 +37.61682242990654,-13.317757009345794 +33.177570093457945,-22.19626168224299 +46.02803738317757,-26.401869158878505 +28.50467289719626,-34.11214953271028 +33.64485981308411,-38.3177570093458 +25.0,-2.5700934579439254 +26.869158878504674,-3.5046728971962615 +34.11214953271028,-20.093457943925234 +17.523364485981308,-25.934579439252335 +19.85981308411215,-6.308411214953271 +17.05607476635514,-37.149532710280376 +15.42056074766355,-20.093457943925234 +13.317757009345794,-10.981308411214954 +28.50467289719626,-18.457943925233646 +36.21495327102804,-16.35514018691589 +23.130841121495326,-5.841121495327103 +13.08411214953271,-21.72897196261682 +21.962616822429908,-3.2710280373831777 +30.373831775700936,-39.48598130841121 +10.280373831775702,-20.093457943925234 +46.495327102803735,-33.41121495327103 +36.21495327102804,-20.327102803738317 +21.72897196261682,-3.97196261682243 +36.21495327102804,-35.74766355140187 +39.953271028037385,-20.093457943925234 +11.91588785046729,-13.551401869158878 +15.186915887850468,-30.60747663551402 +31.30841121495327,-15.42056074766355 +34.11214953271028,-35.51401869158879 +39.018691588785046,-7.94392523364486 +25.700934579439252,-35.2803738317757 +32.47663551401869,-30.8411214953271 +32.242990654205606,-33.177570093457945 +13.08411214953271,-28.738317757009344 +32.47663551401869,-15.654205607476635 +42.28971962616822,-6.542056074766355 +36.91588785046729,-17.757009345794394 +41.822429906542055,-32.71028037383178 +49.532710280373834,-21.49532710280374 +28.27102803738318,-37.850467289719624 +30.60747663551402,-41.822429906542055 +42.28971962616822,-6.308411214953271 +11.448598130841122,-25.46728971962617 +36.6822429906542,-17.990654205607477 +43.925233644859816,-36.21495327102804 +29.439252336448597,-35.74766355140187 +25.46728971962617,-20.560747663551403 +30.14018691588785,-39.25233644859813 +16.588785046728972,-27.570093457943926 +18.925233644859812,-25.934579439252335 +25.46728971962617,-19.85981308411215 +22.19626168224299,-40.654205607476634 +16.121495327102803,-14.25233644859813 +33.177570093457945,-26.635514018691588 +25.233644859813083,-34.57943925233645 +16.822429906542055,-13.785046728971963 +28.27102803738318,-36.21495327102804 +22.429906542056074,-33.177570093457945 +17.757009345794394,-7.710280373831775 +30.14018691588785,-36.6822429906542 +32.242990654205606,-36.21495327102804 +9.57943925233645,-21.02803738317757 +16.121495327102803,-21.962616822429908 +34.81308411214953,-12.616822429906541 +18.457943925233646,-27.102803738317757 +32.00934579439252,-13.551401869158878 +26.869158878504674,-2.102803738317757 +43.925233644859816,-8.644859813084112 +17.05607476635514,-20.327102803738317 +35.981308411214954,-13.08411214953271 +16.121495327102803,-7.242990654205608 +25.934579439252335,-2.5700934579439254 +34.57943925233645,-17.523364485981308 +12.149532710280374,-31.30841121495327 +13.08411214953271,-28.037383177570092 +25.233644859813083,-20.327102803738317 +14.485981308411215,-25.700934579439252 +12.616822429906541,-24.766355140186917 +16.822429906542055,-23.364485981308412 +33.41121495327103,-14.953271028037383 +20.794392523364486,-6.074766355140187 +32.47663551401869,-15.186915887850468 +37.850467289719624,-39.953271028037385 +14.25233644859813,-23.83177570093458 +39.018691588785046,-19.85981308411215 +30.8411214953271,-13.08411214953271 +17.990654205607477,-23.364485981308412 +47.19626168224299,-25.700934579439252 +26.401869158878505,-3.97196261682243 +17.757009345794394,-27.33644859813084 +26.635514018691588,-40.42056074766355 +32.47663551401869,-14.719626168224298 +36.91588785046729,-40.42056074766355 +25.233644859813083,-3.5046728971962615 +17.990654205607477,-29.906542056074766 +49.532710280373834,-22.429906542056074 +39.7196261682243,-34.345794392523366 +37.850467289719624,-18.925233644859812 +37.149532710280376,-20.794392523364486 +27.570093457943926,-17.289719626168225 +19.39252336448598,-6.775700934579439 +33.64485981308411,-41.58878504672897 +46.495327102803735,-25.700934579439252 +15.42056074766355,-26.869158878504674 +46.02803738317757,-26.16822429906542 +43.45794392523364,-36.91588785046729 +27.80373831775701,-37.38317757009346 +48.83177570093458,-19.626168224299064 +29.205607476635514,-16.822429906542055 +23.598130841121495,-34.345794392523366 +12.850467289719626,-27.33644859813084 +27.570093457943926,-37.38317757009346 +42.757009345794394,-32.00934579439252 +31.074766355140188,-39.953271028037385 +10.514018691588785,-16.588785046728972 +35.74766355140187,-14.953271028037383 +49.06542056074766,-16.588785046728972 +28.27102803738318,-15.654205607476635 +35.51401869158879,-17.523364485981308 +33.177570093457945,-14.485981308411215 +14.953271028037383,-19.85981308411215 +10.280373831775702,-18.22429906542056 +31.074766355140188,-20.560747663551403 +20.560747663551403,-22.429906542056074 +38.78504672897196,-19.1588785046729 +33.64485981308411,-38.08411214953271 +28.50467289719626,-19.626168224299064 +32.00934579439252,-12.616822429906541 +16.121495327102803,-26.869158878504674 +11.214953271028037,-24.299065420560748 +33.177570093457945,-40.18691588785047 +28.50467289719626,-41.822429906542055 +11.448598130841122,-13.785046728971963 +20.327102803738317,-39.7196261682243 +13.08411214953271,-25.934579439252335 +34.81308411214953,-35.2803738317757 +45.794392523364486,-22.66355140186916 +27.570093457943926,-40.18691588785047 +18.22429906542056,-8.411214953271028 +49.76635514018692,-20.327102803738317 +31.542056074766354,-13.317757009345794 +31.30841121495327,-12.616822429906541 +34.81308411214953,-36.6822429906542 +33.177570093457945,-32.47663551401869 +28.97196261682243,-14.719626168224298 +39.48598130841121,-18.457943925233646 +46.96261682242991,-25.233644859813083 +15.88785046728972,-29.906542056074766 +16.121495327102803,-28.738317757009344 +12.616822429906541,-13.08411214953271 +36.21495327102804,-13.785046728971963 +47.429906542056074,-12.850467289719626 +48.83177570093458,-20.093457943925234 +33.41121495327103,-33.177570093457945 +38.08411214953271,-10.280373831775702 +34.81308411214953,-16.121495327102803 +33.64485981308411,-34.81308411214953 +25.934579439252335,-3.0373831775700935 +21.72897196261682,-5.140186915887851 +19.1588785046729,-7.710280373831775 +11.682242990654206,-23.83177570093458 +35.51401869158879,-38.08411214953271 +37.850467289719624,-4.4392523364485985 +31.30841121495327,-16.35514018691589 +47.429906542056074,-26.635514018691588 +35.2803738317757,-21.261682242990656 +39.953271028037385,-5.140186915887851 +15.88785046728972,-31.074766355140188 +16.121495327102803,-16.588785046728972 +36.21495327102804,-15.654205607476635 +16.121495327102803,-7.710280373831775 +18.22429906542056,-22.66355140186916 +31.77570093457944,-31.30841121495327 +32.47663551401869,-2.5700934579439254 +40.654205607476634,-19.85981308411215 +36.6822429906542,-13.785046728971963 +47.19626168224299,-28.50467289719626 +17.05607476635514,-13.785046728971963 +14.953271028037383,-26.869158878504674 +16.121495327102803,-25.934579439252335 +36.44859813084112,-18.925233644859812 +33.41121495327103,-22.19626168224299 +19.1588785046729,-6.542056074766355 +31.542056074766354,-15.186915887850468 +48.83177570093458,-29.439252336448597 +35.2803738317757,-18.69158878504673 +33.64485981308411,-41.822429906542055 +27.80373831775701,-38.55140186915888 +29.439252336448597,-14.953271028037383 +46.96261682242991,-12.149532710280374 +32.71028037383178,-20.327102803738317 +19.1588785046729,-23.598130841121495 +42.05607476635514,-32.71028037383178 +31.542056074766354,-17.990654205607477 +47.429906542056074,-27.33644859813084 +18.457943925233646,-25.233644859813083 +19.85981308411215,-8.411214953271028 +17.523364485981308,-28.27102803738318 +21.962616822429908,-7.4766355140186915 +12.616822429906541,-27.80373831775701 +32.94392523364486,-23.83177570093458 +13.317757009345794,-32.47663551401869 +35.046728971962615,-11.448598130841122 +47.66355140186916,-29.205607476635514 +33.177570093457945,-25.233644859813083 +14.25233644859813,-31.542056074766354 +14.953271028037383,-25.46728971962617 +26.16822429906542,-40.654205607476634 +39.953271028037385,-4.906542056074766 +13.551401869158878,-23.598130841121495 +18.22429906542056,-25.0 +19.626168224299064,-30.14018691588785 +32.94392523364486,-38.55140186915888 +17.523364485981308,-21.49532710280374 +34.345794392523366,-16.35514018691589 +10.74766355140187,-27.570093457943926 +46.495327102803735,-23.83177570093458 +35.981308411214954,-19.39252336448598 +32.47663551401869,-16.588785046728972 +28.738317757009344,-13.551401869158878 +38.08411214953271,-20.560747663551403 +32.47663551401869,-17.757009345794394 +21.261682242990656,-3.97196261682243 +17.990654205607477,-6.308411214953271 +49.299065420560744,-26.401869158878505 +15.88785046728972,-16.121495327102803 +38.3177570093458,-20.560747663551403 +49.532710280373834,-25.46728971962617 +26.635514018691588,-39.018691588785046 +30.8411214953271,-15.42056074766355 +48.598130841121495,-25.46728971962617 +25.0,-41.58878504672897 +32.94392523364486,-12.616822429906541 +29.906542056074766,-35.981308411214954 +39.953271028037385,-33.87850467289719 +16.35514018691589,-36.44859813084112 +14.953271028037383,-23.83177570093458 +42.99065420560748,-7.94392523364486 +47.19626168224299,-24.766355140186917 +29.906542056074766,-17.757009345794394 +10.981308411214954,-15.186915887850468 +34.11214953271028,-13.551401869158878 +34.81308411214953,-37.61682242990654 +40.18691588785047,-6.775700934579439 +18.22429906542056,-8.644859813084112 +29.672897196261683,-19.39252336448598 +14.953271028037383,-30.14018691588785 +28.27102803738318,-19.626168224299064 +30.14018691588785,-35.51401869158879 +11.91588785046729,-30.60747663551402 +9.813084112149532,-21.02803738317757 +28.97196261682243,-35.74766355140187 +38.78504672897196,-20.327102803738317 +28.50467289719626,-15.88785046728972 +22.19626168224299,-33.177570093457945 +48.83177570093458,-16.822429906542055 +12.383177570093459,-12.149532710280374 +28.27102803738318,-2.5700934579439254 +36.21495327102804,-37.38317757009346 +29.205607476635514,-37.61682242990654 +16.121495327102803,-7.94392523364486 +29.672897196261683,-36.91588785046729 +32.47663551401869,-16.121495327102803 +17.289719626168225,-11.91588785046729 +10.280373831775702,-25.934579439252335 +29.205607476635514,-12.149532710280374 +18.925233644859812,-7.009345794392523 +37.61682242990654,-35.981308411214954 +45.5607476635514,-27.33644859813084 +16.35514018691589,-29.672897196261683 +35.2803738317757,-16.822429906542055 +28.97196261682243,-15.654205607476635 +28.97196261682243,-20.327102803738317 +34.57943925233645,-20.327102803738317 +15.654205607476635,-31.30841121495327 +28.738317757009344,-37.850467289719624 +20.093457943925234,-7.94392523364486 +48.36448598130841,-27.33644859813084 +26.869158878504674,-34.57943925233645 +17.990654205607477,-27.80373831775701 +45.32710280373832,-28.97196261682243 +17.05607476635514,-22.429906542056074 +31.542056074766354,-21.72897196261682 +28.738317757009344,-10.514018691588785 +33.177570093457945,-38.08411214953271 +17.289719626168225,-26.16822429906542 +37.850467289719624,-15.42056074766355 +34.11214953271028,-38.78504672897196 +35.74766355140187,-34.345794392523366 +31.30841121495327,-18.69158878504673 +16.588785046728972,-23.130841121495326 +36.21495327102804,-21.02803738317757 +49.76635514018692,-23.83177570093458 +26.16822429906542,-3.97196261682243 +16.822429906542055,-22.66355140186916 +25.46728971962617,-41.35514018691589 +30.60747663551402,-18.925233644859812 +18.69158878504673,-28.97196261682243 +26.869158878504674,-37.850467289719624 +30.60747663551402,-11.91588785046729 +23.598130841121495,-3.7383177570093458 +16.121495327102803,-24.766355140186917 +35.046728971962615,-2.803738317757009 +42.757009345794394,-21.72897196261682 +45.32710280373832,-30.8411214953271 +33.177570093457945,-39.48598130841121 +30.8411214953271,-12.850467289719626 +22.66355140186916,-4.4392523364485985 +49.299065420560744,-27.33644859813084 +31.542056074766354,-37.61682242990654 +16.588785046728972,-13.08411214953271 +44.39252336448598,-31.30841121495327 +46.02803738317757,-30.14018691588785 +31.77570093457944,-35.2803738317757 +32.94392523364486,-25.700934579439252 +14.25233644859813,-30.8411214953271 +45.32710280373832,-34.11214953271028 +16.822429906542055,-30.373831775700936 +19.39252336448598,-24.065420560747665 +30.8411214953271,-21.02803738317757 +32.00934579439252,-12.149532710280374 +39.7196261682243,-7.710280373831775 +15.42056074766355,-17.523364485981308 +23.364485981308412,-40.654205607476634 +38.3177570093458,-16.588785046728972 +29.439252336448597,-41.822429906542055 +32.242990654205606,-39.018691588785046 +31.074766355140188,-36.91588785046729 +13.785046728971963,-20.327102803738317 +18.457943925233646,-9.11214953271028 +31.074766355140188,-19.626168224299064 +32.71028037383178,-38.78504672897196 +15.186915887850468,-28.97196261682243 +37.61682242990654,-20.560747663551403 +20.093457943925234,-22.897196261682243 +20.327102803738317,-4.906542056074766 +49.532710280373834,-19.626168224299064 +28.037383177570092,-18.69158878504673 +15.88785046728972,-23.130841121495326 +28.738317757009344,-14.719626168224298 +14.719626168224298,-34.57943925233645 +34.11214953271028,-19.1588785046729 +32.47663551401869,-18.457943925233646 +48.83177570093458,-28.27102803738318 +16.35514018691589,-21.261682242990656 +25.46728971962617,-35.51401869158879 +21.49532710280374,-40.42056074766355 +36.44859813084112,-17.990654205607477 +22.429906542056074,-32.94392523364486 +28.97196261682243,-2.336448598130841 +22.66355140186916,-3.0373831775700935 +33.64485981308411,-17.289719626168225 +15.186915887850468,-23.364485981308412 +23.598130841121495,-4.205607476635514 +27.33644859813084,-39.018691588785046 +13.785046728971963,-21.261682242990656 +15.654205607476635,-21.962616822429908 +36.21495327102804,-11.214953271028037 +49.532710280373834,-24.53271028037383 +19.39252336448598,-4.672897196261682 +21.02803738317757,-4.672897196261682 +14.018691588785046,-29.439252336448597 +12.850467289719626,-21.261682242990656 +15.88785046728972,-17.05607476635514 +16.121495327102803,-36.6822429906542 +48.598130841121495,-21.49532710280374 +25.46728971962617,-35.2803738317757 +27.102803738317757,-36.21495327102804 +12.383177570093459,-24.299065420560748 +49.532710280373834,-18.69158878504673 +14.953271028037383,-9.345794392523365 +39.7196261682243,-39.7196261682243 +38.55140186915888,-40.18691588785047 +10.046728971962617,-26.635514018691588 +49.299065420560744,-26.869158878504674 +16.822429906542055,-28.738317757009344 +30.14018691588785,-38.08411214953271 +12.149532710280374,-26.401869158878505 +28.037383177570092,-16.588785046728972 +12.383177570093459,-26.635514018691588 +47.19626168224299,-21.72897196261682 +37.61682242990654,-18.925233644859812 +31.542056074766354,-17.289719626168225 +48.36448598130841,-25.46728971962617 +16.121495327102803,-16.121495327102803 +36.6822429906542,-16.35514018691589 +15.654205607476635,-25.233644859813083 +11.448598130841122,-15.654205607476635 +30.60747663551402,-20.560747663551403 +27.570093457943926,-3.2710280373831777 +45.794392523364486,-34.345794392523366 +18.22429906542056,-38.08411214953271 +35.51401869158879,-14.018691588785046 +17.990654205607477,-24.766355140186917 +24.065420560747665,-7.242990654205608 +15.88785046728972,-24.53271028037383 +16.822429906542055,-24.299065420560748 +39.48598130841121,-18.925233644859812 +34.57943925233645,-18.925233644859812 +38.3177570093458,-18.22429906542056 +44.39252336448598,-35.51401869158879 +23.364485981308412,-3.7383177570093458 +47.429906542056074,-23.130841121495326 +47.66355140186916,-26.401869158878505 +39.953271028037385,-5.373831775700935 +18.925233644859812,-22.897196261682243 +27.570093457943926,-19.39252336448598 +14.018691588785046,-33.87850467289719 +16.35514018691589,-20.093457943925234 +34.57943925233645,-18.22429906542056 +13.551401869158878,-25.934579439252335 +38.3177570093458,-4.672897196261682 +26.635514018691588,-35.981308411214954 +47.429906542056074,-26.869158878504674 +48.13084112149533,-27.102803738317757 +20.327102803738317,-7.009345794392523 +27.33644859813084,-41.822429906542055 +38.08411214953271,-14.953271028037383 +14.485981308411215,-21.49532710280374 +31.542056074766354,-33.41121495327103 +28.738317757009344,-18.457943925233646 +40.18691588785047,-39.25233644859813 +10.514018691588785,-19.1588785046729 +13.785046728971963,-34.11214953271028 +26.16822429906542,-37.850467289719624 +11.682242990654206,-24.766355140186917 +18.925233644859812,-24.53271028037383 +33.64485981308411,-33.41121495327103 +23.83177570093458,-6.542056074766355 +10.280373831775702,-21.02803738317757 +37.38317757009346,-12.383177570093459 +14.485981308411215,-25.233644859813083 +34.11214953271028,-36.6822429906542 +24.53271028037383,-34.57943925233645 +25.934579439252335,-20.093457943925234 +17.990654205607477,-21.49532710280374 +22.19626168224299,-7.242990654205608 +30.14018691588785,-11.448598130841122 +28.037383177570092,-34.345794392523366 +31.77570093457944,-13.785046728971963 +35.74766355140187,-12.383177570093459 +47.66355140186916,-13.785046728971963 +35.2803738317757,-38.3177570093458 +29.439252336448597,-35.51401869158879 +28.50467289719626,-19.85981308411215 +34.11214953271028,-35.74766355140187 +31.074766355140188,-16.588785046728972 +29.672897196261683,-37.61682242990654 +25.233644859813083,-4.205607476635514 +33.87850467289719,-13.08411214953271 +48.13084112149533,-23.130841121495326 +33.41121495327103,-37.61682242990654 +34.11214953271028,-2.803738317757009 +28.27102803738318,-20.327102803738317 +25.934579439252335,-41.12149532710281 +22.897196261682243,-5.607476635514018 +32.47663551401869,-22.19626168224299 +38.08411214953271,-14.719626168224298 +17.757009345794394,-8.411214953271028 +33.64485981308411,-13.317757009345794 +32.71028037383178,-22.66355140186916 +29.205607476635514,-35.51401869158879 +15.88785046728972,-24.766355140186917 +32.71028037383178,-29.205607476635514 +17.05607476635514,-29.205607476635514 +40.88785046728972,-6.542056074766355 +25.233644859813083,-40.42056074766355 +48.13084112149533,-25.0 +12.616822429906541,-21.962616822429908 +13.551401869158878,-25.233644859813083 +47.19626168224299,-29.672897196261683 +29.205607476635514,-15.88785046728972 +31.77570093457944,-15.654205607476635 +17.289719626168225,-8.411214953271028 +15.42056074766355,-22.429906542056074 +21.72897196261682,-4.205607476635514 +12.149532710280374,-27.102803738317757 +37.149532710280376,-16.822429906542055 +34.11214953271028,-11.682242990654206 +48.36448598130841,-14.719626168224298 +35.51401869158879,-21.962616822429908 +27.102803738317757,-9.11214953271028 +19.39252336448598,-22.897196261682243 +13.785046728971963,-26.635514018691588 +27.570093457943926,-2.803738317757009 +14.719626168224298,-25.233644859813083 +24.065420560747665,-5.140186915887851 +32.47663551401869,-34.57943925233645 +14.25233644859813,-29.205607476635514 +34.345794392523366,-13.785046728971963 +33.64485981308411,-39.953271028037385 +15.654205607476635,-21.261682242990656 +20.794392523364486,-7.710280373831775 +23.598130841121495,-4.4392523364485985 +29.439252336448597,-19.1588785046729 +32.71028037383178,-16.35514018691589 +25.700934579439252,-36.44859813084112 +37.149532710280376,-36.91588785046729 +25.934579439252335,-40.42056074766355 +17.757009345794394,-21.02803738317757 +16.588785046728972,-36.44859813084112 +19.39252336448598,-26.401869158878505 +46.728971962616825,-28.738317757009344 +34.57943925233645,-22.19626168224299 +15.88785046728972,-25.700934579439252 +32.00934579439252,-15.88785046728972 +36.6822429906542,-21.261682242990656 +48.598130841121495,-24.53271028037383 +36.44859813084112,-36.6822429906542 +41.12149532710281,-5.841121495327103 +14.485981308411215,-32.47663551401869 +13.551401869158878,-30.8411214953271 +34.11214953271028,-41.58878504672897 +26.401869158878505,-39.953271028037385 +37.149532710280376,-13.317757009345794 +21.72897196261682,-3.5046728971962615 +32.94392523364486,-35.74766355140187 +33.87850467289719,-40.42056074766355 +15.88785046728972,-27.80373831775701 +31.30841121495327,-22.19626168224299 +27.33644859813084,-17.757009345794394 +21.261682242990656,-4.205607476635514 +40.654205607476634,-6.542056074766355 +37.149532710280376,-11.682242990654206 +16.121495327102803,-15.42056074766355 +46.495327102803735,-11.682242990654206 +32.242990654205606,-21.261682242990656 +46.495327102803735,-25.0 +29.906542056074766,-33.87850467289719 +33.177570093457945,-41.35514018691589 +39.018691588785046,-39.48598130841121 +32.242990654205606,-20.093457943925234 +28.738317757009344,-19.1588785046729 +49.06542056074766,-17.523364485981308 +48.598130841121495,-29.439252336448597 +44.626168224299064,-9.345794392523365 +13.785046728971963,-10.046728971962617 +19.39252336448598,-23.364485981308412 +28.97196261682243,-14.953271028037383 +41.58878504672897,-6.542056074766355 +19.39252336448598,-7.4766355140186915 +48.598130841121495,-28.037383177570092 +29.906542056074766,-35.046728971962615 +11.448598130841122,-24.53271028037383 +32.00934579439252,-23.364485981308412 +30.8411214953271,-39.018691588785046 +48.13084112149533,-24.766355140186917 +36.6822429906542,-19.85981308411215 +32.94392523364486,-21.962616822429908 +27.570093457943926,-17.757009345794394 +18.457943925233646,-29.672897196261683 +32.71028037383178,-41.58878504672897 +30.373831775700936,-39.25233644859813 +30.14018691588785,-12.616822429906541 +43.45794392523364,-36.21495327102804 +10.74766355140187,-15.88785046728972 +21.49532710280374,-6.542056074766355 +21.962616822429908,-7.710280373831775 +46.02803738317757,-24.299065420560748 +49.299065420560744,-27.570093457943926 +36.44859813084112,-11.91588785046729 +17.757009345794394,-26.869158878504674 +32.94392523364486,-13.317757009345794 +27.102803738317757,-35.2803738317757 +26.635514018691588,-37.149532710280376 +16.121495327102803,-18.457943925233646 +27.102803738317757,-39.7196261682243 +47.429906542056074,-28.037383177570092 +14.719626168224298,-18.69158878504673 +34.11214953271028,-14.953271028037383 +14.25233644859813,-30.14018691588785 +32.47663551401869,-29.672897196261683 +22.19626168224299,-32.94392523364486 +37.38317757009346,-15.186915887850468 +31.30841121495327,-32.94392523364486 +44.39252336448598,-36.21495327102804 +13.317757009345794,-23.130841121495326 +34.345794392523366,-12.149532710280374 +31.30841121495327,-36.44859813084112 +24.53271028037383,-5.373831775700935 +34.81308411214953,-37.850467289719624 +36.6822429906542,-3.7383177570093458 +31.074766355140188,-35.51401869158879 +29.672897196261683,-18.69158878504673 +26.869158878504674,-34.81308411214953 +17.289719626168225,-8.644859813084112 +28.50467289719626,-37.850467289719624 +16.822429906542055,-21.962616822429908 +26.16822429906542,-4.672897196261682 +19.1588785046729,-29.906542056074766 +28.97196261682243,-19.39252336448598 +37.38317757009346,-14.25233644859813 +15.186915887850468,-24.065420560747665 +17.289719626168225,-25.700934579439252 +48.598130841121495,-19.85981308411215 +35.74766355140187,-17.757009345794394 +38.3177570093458,-20.327102803738317 +33.41121495327103,-12.616822429906541 +22.66355140186916,-33.177570093457945 +31.074766355140188,-42.05607476635514 +32.242990654205606,-31.77570093457944 +23.83177570093458,-5.373831775700935 +15.654205607476635,-15.88785046728972 +29.439252336448597,-18.69158878504673 +28.50467289719626,-17.289719626168225 +32.94392523364486,-23.364485981308412 +18.22429906542056,-29.672897196261683 +47.89719626168224,-24.065420560747665 +47.429906542056074,-24.299065420560748 +35.981308411214954,-13.785046728971963 +28.738317757009344,-33.64485981308411 +36.21495327102804,-34.345794392523366 +47.89719626168224,-28.037383177570092 +26.635514018691588,-2.336448598130841 +48.83177570093458,-22.897196261682243 +41.35514018691589,-7.009345794392523 +26.869158878504674,-36.6822429906542 +27.33644859813084,-18.925233644859812 +11.682242990654206,-29.906542056074766 +29.906542056074766,-35.74766355140187 +31.77570093457944,-17.757009345794394 +29.205607476635514,-16.588785046728972 +10.046728971962617,-21.02803738317757 +10.046728971962617,-21.49532710280374 +46.728971962616825,-27.33644859813084 +28.037383177570092,-39.018691588785046 +29.439252336448597,-36.44859813084112 +40.654205607476634,-20.327102803738317 +10.514018691588785,-25.700934579439252 +30.373831775700936,-38.78504672897196 +35.2803738317757,-40.42056074766355 +15.88785046728972,-19.85981308411215 +13.785046728971963,-28.50467289719626 +34.81308411214953,-40.654205607476634 +10.981308411214954,-28.97196261682243 +12.616822429906541,-23.83177570093458 +11.682242990654206,-25.700934579439252 +35.046728971962615,-40.18691588785047 +20.560747663551403,-3.97196261682243 +33.41121495327103,-17.523364485981308 +37.850467289719624,-40.654205607476634 +21.261682242990656,-4.4392523364485985 +32.71028037383178,-17.523364485981308 +32.47663551401869,-30.14018691588785 +22.429906542056074,-3.97196261682243 +13.551401869158878,-26.16822429906542 +24.53271028037383,-40.88785046728972 +14.485981308411215,-21.02803738317757 +13.317757009345794,-26.635514018691588 +37.38317757009346,-35.51401869158879 +27.570093457943926,-19.85981308411215 +36.44859813084112,-14.018691588785046 +34.11214953271028,-20.560747663551403 +45.09345794392523,-22.897196261682243 +27.102803738317757,-38.55140186915888 +17.289719626168225,-26.635514018691588 +35.51401869158879,-17.757009345794394 +18.457943925233646,-29.906542056074766 +39.48598130841121,-34.57943925233645 +17.289719626168225,-11.448598130841122 +48.598130841121495,-27.570093457943926 +28.27102803738318,-10.046728971962617 +46.728971962616825,-12.149532710280374 +29.439252336448597,-42.28971962616822 +19.39252336448598,-21.962616822429908 +31.77570093457944,-32.71028037383178 +19.39252336448598,-29.906542056074766 +12.149532710280374,-23.598130841121495 +31.074766355140188,-32.94392523364486 +29.906542056074766,-36.6822429906542 +17.523364485981308,-27.33644859813084 +35.046728971962615,-20.560747663551403 +26.401869158878505,-37.38317757009346 +15.42056074766355,-29.672897196261683 +33.177570093457945,-37.38317757009346 +20.327102803738317,-7.4766355140186915 +33.87850467289719,-13.317757009345794 +31.77570093457944,-41.58878504672897 +41.12149532710281,-38.78504672897196 +11.91588785046729,-23.83177570093458 +32.71028037383178,-11.682242990654206 +30.14018691588785,-15.42056074766355 +17.523364485981308,-26.16822429906542 +17.523364485981308,-6.775700934579439 +40.88785046728972,-33.41121495327103 +42.52336448598131,-6.775700934579439 +34.11214953271028,-12.850467289719626 +48.83177570093458,-23.598130841121495 +40.654205607476634,-20.093457943925234 +13.785046728971963,-24.766355140186917 +10.280373831775702,-22.66355140186916 +26.635514018691588,-41.35514018691589 +22.429906542056074,-33.41121495327103 +27.33644859813084,-36.44859813084112 +30.60747663551402,-17.757009345794394 +21.49532710280374,-8.177570093457945 +42.99065420560748,-37.61682242990654 +28.27102803738318,-39.7196261682243 +15.186915887850468,-9.11214953271028 +17.289719626168225,-22.429906542056074 +29.906542056074766,-35.51401869158879 +18.22429906542056,-26.401869158878505 +28.50467289719626,-42.28971962616822 +34.57943925233645,-36.91588785046729 +47.66355140186916,-14.25233644859813 +28.27102803738318,-2.803738317757009 +27.80373831775701,-16.588785046728972 +14.953271028037383,-28.27102803738318 +25.46728971962617,-2.336448598130841 +25.934579439252335,-8.177570093457945 +14.719626168224298,-30.373831775700936 +44.626168224299064,-35.74766355140187 +18.925233644859812,-28.97196261682243 +28.738317757009344,-19.85981308411215 +39.018691588785046,-34.81308411214953 +38.3177570093458,-18.925233644859812 +15.42056074766355,-25.934579439252335 +32.00934579439252,-36.44859813084112 +12.850467289719626,-28.50467289719626 +49.76635514018692,-25.0 +17.523364485981308,-26.635514018691588 +12.383177570093459,-26.401869158878505 +26.401869158878505,-41.35514018691589 +35.51401869158879,-34.11214953271028 +48.36448598130841,-28.738317757009344 +47.429906542056074,-27.102803738317757 +14.018691588785046,-32.47663551401869 +29.205607476635514,-19.1588785046729 +23.130841121495326,-7.4766355140186915 +35.2803738317757,-12.149532710280374 +11.91588785046729,-22.66355140186916 +34.345794392523366,-41.12149532710281 +19.85981308411215,-24.065420560747665 +43.45794392523364,-7.94392523364486 +30.373831775700936,-20.560747663551403 +30.373831775700936,-13.08411214953271 +23.364485981308412,-21.261682242990656 +29.439252336448597,-11.448598130841122 +36.6822429906542,-34.81308411214953 +33.64485981308411,-21.962616822429908 +14.719626168224298,-20.794392523364486 +29.439252336448597,-11.91588785046729 +32.47663551401869,-14.018691588785046 +43.45794392523364,-31.542056074766354 +32.94392523364486,-19.85981308411215 +23.83177570093458,-40.654205607476634 +24.53271028037383,-5.841121495327103 +29.205607476635514,-36.21495327102804 +49.299065420560744,-18.457943925233646 +22.897196261682243,-33.87850467289719 +49.76635514018692,-21.49532710280374 +32.94392523364486,-25.934579439252335 +30.14018691588785,-39.7196261682243 +15.186915887850468,-8.878504672897197 +25.233644859813083,-3.7383177570093458 +30.60747663551402,-16.35514018691589 +16.822429906542055,-20.794392523364486 +34.345794392523366,-34.57943925233645 +23.130841121495326,-33.41121495327103 +21.962616822429908,-33.177570093457945 +32.242990654205606,-22.429906542056074 +10.046728971962617,-22.19626168224299 +42.05607476635514,-7.242990654205608 +49.299065420560744,-22.66355140186916 +10.046728971962617,-23.364485981308412 +10.280373831775702,-18.69158878504673 +34.11214953271028,-38.3177570093458 +46.96261682242991,-12.616822429906541 +18.925233644859812,-23.83177570093458 +32.94392523364486,-15.654205607476635 +28.738317757009344,-17.757009345794394 +33.41121495327103,-21.261682242990656 +45.09345794392523,-29.906542056074766 +35.74766355140187,-18.69158878504673 +12.149532710280374,-21.962616822429908 +14.485981308411215,-31.542056074766354 +11.682242990654206,-26.401869158878505 +17.757009345794394,-38.08411214953271 +28.27102803738318,-15.88785046728972 +16.822429906542055,-29.672897196261683 +48.598130841121495,-17.523364485981308 +41.58878504672897,-6.308411214953271 +48.83177570093458,-16.121495327102803 +12.850467289719626,-23.83177570093458 +38.78504672897196,-16.588785046728972 +16.121495327102803,-18.22429906542056 +30.373831775700936,-12.850467289719626 +14.25233644859813,-34.345794392523366 +29.205607476635514,-13.551401869158878 +14.25233644859813,-21.261682242990656 +27.80373831775701,-10.280373831775702 +36.21495327102804,-20.560747663551403 +17.523364485981308,-8.644859813084112 +38.08411214953271,-15.42056074766355 +31.77570093457944,-36.21495327102804 +24.766355140186917,-41.12149532710281 +12.616822429906541,-27.570093457943926 +30.8411214953271,-17.289719626168225 +37.149532710280376,-20.093457943925234 +20.093457943925234,-6.775700934579439 +14.719626168224298,-21.962616822429908 +48.13084112149533,-26.401869158878505 +18.22429906542056,-28.037383177570092 +32.47663551401869,-12.383177570093459 +15.42056074766355,-7.94392523364486 +36.91588785046729,-15.42056074766355 +17.05607476635514,-28.738317757009344 +39.953271028037385,-19.626168224299064 +35.74766355140187,-21.49532710280374 +46.495327102803735,-23.130841121495326 +26.635514018691588,-8.878504672897197 +28.037383177570092,-33.87850467289719 +38.08411214953271,-40.42056074766355 +31.30841121495327,-37.850467289719624 +28.738317757009344,-15.42056074766355 +48.13084112149533,-23.83177570093458 +14.25233644859813,-24.53271028037383 +31.542056074766354,-20.794392523364486 +31.542056074766354,-20.093457943925234 +26.16822429906542,-3.7383177570093458 +34.11214953271028,-21.02803738317757 +36.44859813084112,-15.654205607476635 +46.728971962616825,-25.233644859813083 +37.38317757009346,-3.5046728971962615 +33.177570093457945,-34.345794392523366 +27.33644859813084,-20.093457943925234 +32.47663551401869,-38.55140186915888 +12.616822429906541,-29.205607476635514 +32.47663551401869,-34.81308411214953 +47.66355140186916,-25.700934579439252 +49.299065420560744,-20.794392523364486 +40.42056074766355,-34.11214953271028 +32.71028037383178,-38.3177570093458 +31.074766355140188,-15.88785046728972 +49.06542056074766,-26.635514018691588 +19.1588785046729,-8.177570093457945 +20.560747663551403,-8.644859813084112 +21.962616822429908,-7.009345794392523 +28.27102803738318,-2.336448598130841 +36.44859813084112,-21.261682242990656 +17.289719626168225,-7.94392523364486 +14.485981308411215,-27.80373831775701 +48.36448598130841,-28.037383177570092 +11.214953271028037,-29.439252336448597 +26.401869158878505,-39.48598130841121 +36.44859813084112,-16.121495327102803 +35.51401869158879,-3.0373831775700935 +19.85981308411215,-30.60747663551402 +46.495327102803735,-10.981308411214954 +17.990654205607477,-29.205607476635514 +14.25233644859813,-34.11214953271028 +46.26168224299065,-26.16822429906542 +16.35514018691589,-24.065420560747665 +34.57943925233645,-16.588785046728972 +28.037383177570092,-15.42056074766355 +48.13084112149533,-24.065420560747665 +33.177570093457945,-17.757009345794394 +13.317757009345794,-22.897196261682243 +13.08411214953271,-22.66355140186916 +14.485981308411215,-20.560747663551403 +33.177570093457945,-26.401869158878505 +30.8411214953271,-16.35514018691589 +39.48598130841121,-19.1588785046729 +49.299065420560744,-25.233644859813083 +31.77570093457944,-34.81308411214953 +23.83177570093458,-2.803738317757009 +15.186915887850468,-29.906542056074766 +33.87850467289719,-39.48598130841121 +29.439252336448597,-42.05607476635514 +48.83177570093458,-18.925233644859812 +25.46728971962617,-35.74766355140187 +46.728971962616825,-30.60747663551402 +32.242990654205606,-14.25233644859813 +11.214953271028037,-24.065420560747665 +44.1588785046729,-8.177570093457945 +47.429906542056074,-25.700934579439252 +14.953271028037383,-24.766355140186917 +15.186915887850468,-20.560747663551403 +29.672897196261683,-12.616822429906541 +12.149532710280374,-13.785046728971963 +24.766355140186917,-40.654205607476634 +28.97196261682243,-1.8691588785046729 +47.19626168224299,-23.83177570093458 +13.317757009345794,-26.16822429906542 +34.57943925233645,-12.149532710280374 +33.177570093457945,-25.46728971962617 +28.97196261682243,-12.149532710280374 +15.42056074766355,-25.46728971962617 +32.00934579439252,-31.30841121495327 +38.3177570093458,-9.57943925233645 +20.327102803738317,-7.242990654205608 +35.51401869158879,-13.551401869158878 +12.850467289719626,-10.981308411214954 +33.64485981308411,-13.08411214953271 +23.130841121495326,-40.42056074766355 +32.94392523364486,-35.2803738317757 +46.26168224299065,-25.0 +18.69158878504673,-21.962616822429908 +27.33644859813084,-38.78504672897196 +26.869158878504674,-39.018691588785046 +30.14018691588785,-37.61682242990654 +14.485981308411215,-26.16822429906542 +15.42056074766355,-30.8411214953271 +29.906542056074766,-20.560747663551403 +11.214953271028037,-22.897196261682243 +36.91588785046729,-34.81308411214953 +33.177570093457945,-11.91588785046729 +33.41121495327103,-19.626168224299064 +33.41121495327103,-41.35514018691589 +35.046728971962615,-12.149532710280374 +48.36448598130841,-15.654205607476635 +13.785046728971963,-31.542056074766354 +28.27102803738318,-42.28971962616822 +33.41121495327103,-37.850467289719624 +28.27102803738318,-41.822429906542055 +48.13084112149533,-14.018691588785046 +15.42056074766355,-35.981308411214954 +32.94392523364486,-41.58878504672897 +14.719626168224298,-22.66355140186916 +32.47663551401869,-38.08411214953271 +36.44859813084112,-12.149532710280374 +49.76635514018692,-19.85981308411215 +32.00934579439252,-21.49532710280374 +17.990654205607477,-23.598130841121495 +31.542056074766354,-36.21495327102804 +39.018691588785046,-4.672897196261682 +32.71028037383178,-13.551401869158878 +27.80373831775701,-18.69158878504673 +22.19626168224299,-4.672897196261682 +28.738317757009344,-11.448598130841122 +27.80373831775701,-38.08411214953271 +17.757009345794394,-21.962616822429908 +28.27102803738318,-18.69158878504673 +27.33644859813084,-40.42056074766355 +23.130841121495326,-2.803738317757009 +31.77570093457944,-2.336448598130841 +14.018691588785046,-21.02803738317757 +17.05607476635514,-26.635514018691588 +22.897196261682243,-3.0373831775700935 +30.373831775700936,-33.87850467289719 +16.121495327102803,-19.626168224299064 +21.962616822429908,-22.19626168224299 +17.05607476635514,-8.411214953271028 +14.25233644859813,-32.71028037383178 +45.09345794392523,-10.046728971962617 +48.83177570093458,-22.429906542056074 +31.77570093457944,-14.953271028037383 +27.102803738317757,-38.78504672897196 +16.588785046728972,-36.6822429906542 +27.80373831775701,-35.981308411214954 +32.47663551401869,-11.682242990654206 +47.66355140186916,-14.485981308411215 +35.2803738317757,-20.560747663551403 +49.532710280373834,-26.635514018691588 +33.41121495327103,-17.757009345794394 +17.990654205607477,-22.19626168224299 +31.77570093457944,-21.261682242990656 +36.91588785046729,-18.22429906542056 +21.261682242990656,-7.4766355140186915 +13.317757009345794,-21.72897196261682 +16.588785046728972,-13.785046728971963 +16.35514018691589,-7.710280373831775 +35.51401869158879,-38.55140186915888 +34.11214953271028,-20.794392523364486 +31.542056074766354,-36.44859813084112 +16.822429906542055,-19.1588785046729 +29.205607476635514,-11.448598130841122 +29.672897196261683,-11.448598130841122 +49.299065420560744,-21.02803738317757 +13.551401869158878,-30.373831775700936 +38.08411214953271,-17.990654205607477 +16.822429906542055,-23.83177570093458 +28.27102803738318,-20.560747663551403 +25.233644859813083,-40.18691588785047 +27.102803738317757,-19.85981308411215 +28.27102803738318,-39.48598130841121 +10.514018691588785,-17.523364485981308 +19.39252336448598,-31.074766355140188 +35.51401869158879,-13.317757009345794 +31.074766355140188,-38.08411214953271 +37.61682242990654,-15.42056074766355 +29.439252336448597,-19.85981308411215 +28.27102803738318,-17.757009345794394 +32.94392523364486,-22.429906542056074 +26.635514018691588,-39.7196261682243 +20.794392523364486,-6.775700934579439 +30.14018691588785,-16.121495327102803 +29.672897196261683,-37.149532710280376 +32.242990654205606,-40.18691588785047 +35.981308411214954,-36.44859813084112 +41.58878504672897,-33.177570093457945 +28.50467289719626,-36.21495327102804 +29.672897196261683,-16.822429906542055 +38.55140186915888,-39.7196261682243 +17.990654205607477,-9.57943925233645 +47.66355140186916,-22.429906542056074 +12.383177570093459,-32.242990654205606 +47.89719626168224,-28.27102803738318 +32.94392523364486,-19.1588785046729 +32.47663551401869,-41.822429906542055 +34.57943925233645,-22.897196261682243 +17.289719626168225,-29.439252336448597 +26.401869158878505,-40.654205607476634 +10.514018691588785,-28.037383177570092 +32.94392523364486,-13.785046728971963 +11.91588785046729,-31.30841121495327 +15.186915887850468,-22.19626168224299 +25.0,-5.140186915887851 +35.2803738317757,-36.91588785046729 +18.69158878504673,-29.906542056074766 +30.60747663551402,-21.261682242990656 +40.18691588785047,-34.345794392523366 +33.87850467289719,-33.64485981308411 +44.1588785046729,-22.19626168224299 +19.39252336448598,-29.672897196261683 +31.77570093457944,-33.41121495327103 +23.364485981308412,-3.5046728971962615 +14.719626168224298,-27.80373831775701 +32.71028037383178,-25.0 +22.19626168224299,-4.4392523364485985 +28.97196261682243,-17.05607476635514 +17.523364485981308,-24.299065420560748 +46.02803738317757,-25.233644859813083 +21.962616822429908,-3.97196261682243 +27.102803738317757,-37.149532710280376 +16.588785046728972,-26.635514018691588 +25.934579439252335,-35.74766355140187 +34.57943925233645,-35.981308411214954 +38.3177570093458,-15.88785046728972 +32.71028037383178,-17.990654205607477 +47.19626168224299,-22.429906542056074 +28.50467289719626,-39.48598130841121 +29.205607476635514,-37.850467289719624 +31.77570093457944,-14.018691588785046 +30.60747663551402,-36.91588785046729 +39.953271028037385,-39.25233644859813 +27.33644859813084,-3.5046728971962615 +23.130841121495326,-5.140186915887851 +11.682242990654206,-25.0 +29.906542056074766,-39.7196261682243 diff --git a/src/data_morph/shapes/bases/line_collection.py b/src/data_morph/shapes/bases/line_collection.py index ee155387..2f91fc2f 100644 --- a/src/data_morph/shapes/bases/line_collection.py +++ b/src/data_morph/shapes/bases/line_collection.py @@ -4,6 +4,7 @@ from typing import Iterable import matplotlib.pyplot as plt +import numpy as np from matplotlib.axes import Axes from ...plotting.style import plot_with_custom_style @@ -22,7 +23,13 @@ class LineCollection(Shape): """ def __init__(self, *lines: Iterable[Iterable[Number]]) -> None: - self.lines = lines + # check that lines with the same starting and ending points raise an error + for line in lines: + start, end = line + if np.allclose(start, end): + raise ValueError(f'Line {line} has the same start and end point') + + self.lines = np.array(lines) """Iterable[Iterable[numbers.Number]]: An iterable of two (x, y) pairs representing the endpoints of a line.""" @@ -44,65 +51,48 @@ def distance(self, x: Number, y: Number) -> float: float The minimum distance from the lines of this shape to the point (x, y). - """ - return min( - self._distance_point_to_line(point=(x, y), line=line) for line in self.lines - ) - def _distance_point_to_line( - self, - point: Iterable[Number], - line: Iterable[Iterable[Number]], - ) -> float: + Notes + ----- + Implementation based on `this Stack Overflow answer`_. + + .. _this Stack Overflow answer: https://stackoverflow.com/a/58781995 """ - Calculate the minimum distance between a point and a line. + point = np.array([x, y]) - Parameters - ---------- - point : Iterable[numbers.Number] - Coordinates of a point in 2D space. - line : Iterable[Iterable[numbers.Number]] - Coordinates of the endpoints of a line in 2D space. + start_points = self.lines[:, 0, :] + end_points = self.lines[:, 1, :] - Returns - ------- - float - The minimum distance between the point and the line. + tangent_vector = end_points - start_points + normalized_tangent_vectors = np.divide( + tangent_vector, + np.hypot(tangent_vector[:, 0], tangent_vector[:, 1]).reshape(-1, 1), + ) - Notes - ----- - Implementation based on `this VBA code`_. + # row-wise dot products of 2D vectors + signed_parallel_distance_start = np.multiply( + start_points - point, normalized_tangent_vectors + ).sum(axis=1) + signed_parallel_distance_end = np.multiply( + point - end_points, normalized_tangent_vectors + ).sum(axis=1) + + clamped_parallel_distance = np.maximum.reduce( + [ + signed_parallel_distance_start, + signed_parallel_distance_end, + np.zeros(signed_parallel_distance_start.shape[0]), + ] + ) - .. _this VBA code: http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/source.vba - """ - start, end = line - line_mag = self._euclidean_distance(start, end) - - if line_mag < 0.00000001: - # Arbitrarily large value - return 9999 - - px, py = point - x1, y1 = start - x2, y2 = end - - u1 = ((px - x1) * (x2 - x1)) + ((py - y1) * (y2 - y1)) - u = u1 / (line_mag * line_mag) - - if (u < 0.00001) or (u > 1): - # closest point does not fall within the line segment, take the shorter - # distance to an endpoint - distance = min( - self._euclidean_distance(point, start), - self._euclidean_distance(point, end), - ) - else: - # Intersecting point is on the line, use the formula - ix = x1 + u * (x2 - x1) - iy = y1 + u * (y2 - y1) - distance = self._euclidean_distance(point, (ix, iy)) - - return distance + # row-wise cross products of 2D vectors + perpendicular_distance_component = np.cross( + point - start_points, normalized_tangent_vectors + ) + + return np.min( + np.hypot(clamped_parallel_distance, perpendicular_distance_component) + ) @plot_with_custom_style def plot(self, ax: Axes = None) -> Axes: diff --git a/src/data_morph/shapes/bases/shape.py b/src/data_morph/shapes/bases/shape.py index 3ca20c09..75f47695 100644 --- a/src/data_morph/shapes/bases/shape.py +++ b/src/data_morph/shapes/bases/shape.py @@ -4,8 +4,8 @@ from numbers import Number from typing import Iterable, Optional +import numpy as np from matplotlib.axes import Axes -from scipy.spatial import distance class Shape(ABC): @@ -67,9 +67,9 @@ def _euclidean_distance(a: Iterable[Number], b: Iterable[Number]) -> float: See Also -------- - scipy.spatial.distance.euclidean : Euclidean distance calculation. + numpy.linalg.norm : Euclidean distance calculation. """ - return distance.euclidean(a, b) + return np.linalg.norm(a - b) def _recursive_repr(self, attr: Optional[str] = None) -> str: """ diff --git a/src/data_morph/shapes/circles.py b/src/data_morph/shapes/circles.py index 0896afc0..12bdb46b 100644 --- a/src/data_morph/shapes/circles.py +++ b/src/data_morph/shapes/circles.py @@ -29,22 +29,20 @@ class Circle(Shape): ---------- dataset : Dataset The starting dataset to morph into other shapes. - r : numbers.Number, optional + radius : numbers.Number, optional The radius of the circle. """ - def __init__(self, dataset: Dataset, r: Number = None) -> None: - self.cx: Number = dataset.df.x.mean() - """numbers.Number: The x coordinate of the circle's center.""" + def __init__(self, dataset: Dataset, radius: Number = None) -> None: + self.center: np.ndarray = dataset.df[['x', 'y']].mean().to_numpy() + """numpy.ndarray: The (x, y) coordinates of the circle's center.""" - self.cy: Number = dataset.df.y.mean() - """numbers.Number: The y coordinate of the circle's center.""" - - self.r: Number = r or dataset.df.std().mean() * 1.5 + self.radius: Number = radius or dataset.df[['x', 'y']].std().mean() * 1.5 """numbers.Number: The radius of the circle.""" def __repr__(self) -> str: - return f'<{self.__class__.__name__} cx={self.cx} cy={self.cy} r={self.r}>' + x, y = self.center + return f'<{self.__class__.__name__} center={(float(x), float(y))} radius={self.radius}>' def distance(self, x: Number, y: Number) -> float: """ @@ -60,7 +58,9 @@ def distance(self, x: Number, y: Number) -> float: float The absolute distance between this circle's edge and the point (x, y). """ - return abs(self._euclidean_distance((self.cx, self.cy), (x, y)) - self.r) + return abs( + self._euclidean_distance(self.center, np.array([x, y])) - self.radius + ) @plot_with_custom_style def plot(self, ax: Axes = None) -> Axes: @@ -81,7 +81,7 @@ def plot(self, ax: Axes = None) -> Axes: fig, ax = plt.subplots(layout='constrained') fig.get_layout_engine().set(w_pad=0.2, h_pad=0.2) _ = ax.axis('equal') - _ = ax.add_patch(plt.Circle((self.cx, self.cy), self.r, ec='k', fill=False)) + _ = ax.add_patch(plt.Circle(self.center, self.radius, ec='k', fill=False)) _ = ax.autoscale() return ax @@ -125,6 +125,9 @@ def __init__(self, dataset: Dataset, num_rings: int = 4) -> None: ] """list[Circle]: The individual rings represented by :class:`Circle` objects.""" + self._centers = np.array([circle.center for circle in self.circles]) + self._radii = np.array([circle.radius for circle in self.circles]) + def __repr__(self) -> str: return self._recursive_repr('circles') @@ -150,7 +153,10 @@ def distance(self, x: Number, y: Number) -> float: Rings consists of multiple circles, so we use the minimum distance to one of the circles. """ - return min(circle.distance(x, y) for circle in self.circles) + point = np.array([x, y]) + return np.min( + np.abs(np.linalg.norm(self._centers - point, axis=1) - self._radii) + ) @plot_with_custom_style def plot(self, ax: Axes = None) -> Axes: diff --git a/tests/shapes/bases/test_line_collection.py b/tests/shapes/bases/test_line_collection.py index 6b356552..dbda853d 100644 --- a/tests/shapes/bases/test_line_collection.py +++ b/tests/shapes/bases/test_line_collection.py @@ -35,20 +35,16 @@ def test_distance_nonzero(self, line_collection, point, expected_distance): assert pytest.approx(line_collection.distance(*point)) == expected_distance @pytest.mark.parametrize('line', [[(0, 0), (0, 0)], [(-1, -1), (-1, -1)]], ids=str) - def test_distance_to_small_line_magnitude(self, line_collection, line): - """Test _distance_point_to_line() for small line magnitudes.""" - distance = line_collection._distance_point_to_line((30, 50), line) - assert distance == 9999 + def test_line_as_point(self, line): + """Test LineCollection raises a ValueError for small line magnitudes.""" + with pytest.raises(ValueError): + LineCollection(line) def test_repr(self, line_collection): """Test that the __repr__() method is working.""" - lines = r'\n '.join( - [r'\[\[\d+\.*\d*, \d+\.*\d*\], \[\d+\.*\d*, \d+\.*\d*\]\]'] - * len(line_collection.lines) - ) assert ( re.match( - (r'^\n lines=\n ' + lines), + r"""\n lines=\n {8}array\(\[\[\d+""", repr(line_collection), ) is not None diff --git a/tests/shapes/test_circles.py b/tests/shapes/test_circles.py index 1b6c906d..87b6e80c 100644 --- a/tests/shapes/test_circles.py +++ b/tests/shapes/test_circles.py @@ -9,6 +9,8 @@ pytestmark = [pytest.mark.shapes, pytest.mark.circles] +CIRCLE_REPR = r'' + class CirclesModuleTestBase: """Base for testing circle shapes.""" @@ -42,8 +44,8 @@ class TestBullseye(CirclesModuleTestBase): repr_regex = ( r'^\n' r' circles=\n' - r' \n' - r' $' + r' ' + CIRCLE_REPR + '\n' + r' ' + CIRCLE_REPR + '$' ) def test_init(self, shape): @@ -51,9 +53,8 @@ def test_init(self, shape): assert len(shape.circles) == 2 a, b = shape.circles - assert a.cx == b.cx - assert a.cy == b.cy - assert a.r != b.r + assert np.array_equal(a.center, b.center) + assert a.radius != b.radius class TestCircle(CirclesModuleTestBase): @@ -61,14 +62,15 @@ class TestCircle(CirclesModuleTestBase): shape_name = 'circle' distance_test_cases = [[(20, 50), 10.490381], [(10, 25), 15.910168]] - repr_regex = r'^$' + repr_regex = '^' + CIRCLE_REPR + '$' def test_is_circle(self, shape): """Test that the Circle is a valid circle (mathematically).""" angles = np.arange(0, 361, 45) + cx, cy = shape.center for x, y in zip( - shape.cx + shape.r * np.cos(angles), - shape.cy + shape.r * np.sin(angles), + cx + shape.radius * np.cos(angles), + cy + shape.radius * np.sin(angles), ): assert pytest.approx(shape.distance(x, y)) == 0 @@ -81,8 +83,10 @@ class TestRings(CirclesModuleTestBase): repr_regex = ( r'^\n' r' circles=\n' - r' \n' - r' ' + r' ' + CIRCLE_REPR + '\n' + r' ' + CIRCLE_REPR + '\n' + r' ' + CIRCLE_REPR + '\n' + r' ' + CIRCLE_REPR + '$' ) @pytest.mark.parametrize('num_rings', [3, 5]) @@ -92,11 +96,10 @@ def test_init(self, shape_factory, num_rings): assert len(shape.circles) == num_rings assert all( - getattr(circle, center_coord) == getattr(shape.circles[0], center_coord) + np.array_equal(circle.center, shape.circles[0].center) for circle in shape.circles[1:] - for center_coord in ['cx', 'cy'] ) - assert len({circle.r for circle in shape.circles}) == num_rings + assert len({circle.radius for circle in shape.circles}) == num_rings @pytest.mark.parametrize('num_rings', ['3', -5, 1, True]) def test_num_rings_is_valid(self, shape_factory, num_rings): diff --git a/tests/shapes/test_points.py b/tests/shapes/test_points.py index 842d831d..feb38a47 100644 --- a/tests/shapes/test_points.py +++ b/tests/shapes/test_points.py @@ -25,7 +25,8 @@ def test_distance(self, shape, test_point, expected_distance): Test the distance() method parametrized by distance_test_cases (see conftest.py). """ - assert pytest.approx(shape.distance(*test_point), abs=1e-5) == expected_distance + actual_distance = shape.distance(*test_point) + assert pytest.approx(actual_distance, abs=1e-5) == expected_distance class TestDotsGrid(PointsModuleTestBase): diff --git a/tests/test_cli.py b/tests/test_cli.py index 4cc289fd..3b0a337f 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -30,12 +30,6 @@ def test_cli_version(capsys): assert f'data-morph {__version__}' == capsys.readouterr().out.strip() -def test_cli_usage_wrap_for_docs(): - """Confirm that the usage wrapping for the docs is working.""" - usage_text = cli._generate_parser_for_docs().format_usage() - assert all(len(line) <= cli.USAGE_WIDTH_FOR_DOCS for line in usage_text.split('\n')) - - def test_cli_bad_shape(): """Test that invalid target shapes raise a ValueError.""" with pytest.raises(ValueError, match='No valid target shapes were provided.'):