Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Update study area to account for exclusion zones #5

Closed
wants to merge 1 commit into from

Conversation

brynpickering
Copy link
Member

@brynpickering brynpickering commented Oct 23, 2020

Reopening PR from old repo: timtroendle/possibility-for-electricity-autarky#7

Fix for #1

For completeness, original PR text is below.

--

Now an arbitrary number of exclusion zones can be defined in the model scope. These can only be rectangular.

I can't test this on the whole workflow, since I have eternal package conflicts, but the defined 'atlantic islands' exclusion zone leads to the following study area:

image

If defining a polygon that fits within the exclusion zone (e.g. small_hole = shapely.geometry.Polygon(((-25, 35), (-24, 35), (-24, 40), (-25, 40)))), then study_area.intersects(small_hole) and study_area.contains(small_hole) are both False.

If defining a polygon that partially fits within the exclusion zone (e.g. big_hole = shapely.geometry.Polygon(((-25, 35), (-10, 35), (-10, 45), (-25, 45)))), then study_area.intersects(big_hole) == True and study_area.contains(big_hole) == False.

MWE

import shapely.geometry
import shapely.errors
import geopandas as gpd

def _study_area(config):
    """
    Create a bounding box for the study area, and cut out holes for all defined
    exclusion zones. For plotting purposes, exclusion zones and the bounding box are
    defined in opposite orientations, see https://github.com/geopandas/geopandas/issues/951
    """
    if config["scope"].get("exclusion_zones", {}) and isinstance(config["scope"]["exclusion_zones"], dict):
        holes = [
            (
                (exclusion_zone["x_max"], exclusion_zone["y_min"]),
                (exclusion_zone["x_max"], exclusion_zone["y_max"]),
                (exclusion_zone["x_min"], exclusion_zone["y_max"]),
                (exclusion_zone["x_min"], exclusion_zone["y_min"])
            )
            for exclusion_zone in config["scope"]["exclusion_zones"].values()
        ]
    else:
        holes = []

    study_area = shapely.geometry.Polygon(
        ((config["scope"]["bounds"]["x_min"], config["scope"]["bounds"]["y_min"]),
         (config["scope"]["bounds"]["x_min"], config["scope"]["bounds"]["y_max"]),
         (config["scope"]["bounds"]["x_max"], config["scope"]["bounds"]["y_max"]),
         (config["scope"]["bounds"]["x_max"], config["scope"]["bounds"]["y_min"])),
        holes=holes
    )

    if study_area.is_valid is False:
        raise shapely.errors.TopologicalError(
            "Invalid study area geometry. "
            "Ensure that exclusion zones do not share a border with the study bounds."
        )
    else:
        return study_area

config = {'scope':{
    'bounds': {
        'x_min': -30, # in degrees east
        'x_max': 37,  # in degrees east
        'y_min': 30,  # in degrees north
        'y_max': 75,  # in degrees north
    },
    'exclusion_zones':{
        'atlantic_islands':{
            'x_min': -29.5, # in degrees east
            'x_max': -15,  # in degrees east
            'y_min': 31,  # in degrees north
            'y_max': 41  # in degrees north
        }
    }}
}

study_area = _study_area(config)

small_hole = shapely.geometry.Polygon(((-25, 35), (-24, 35), (-24, 40), (-25, 40)))
big_hole = shapely.geometry.Polygon(((-25, 35), (-10, 35), (-10, 45), (-25, 45)))

print(
    "contains small hole:", foo.contains(small_hole)
    "intersects small hole:", foo.intersects(small_hole)
    "contains big hole:", foo.contains(big_hole)
    "intersects big hole:", foo.intersects(big_hole)
)

Copy link
Member

@timtroendle timtroendle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, but some more tests would be good.

@@ -31,12 +32,7 @@
@click.argument("path_to_output")
@click.argument("config", type=Config())
def retrieve_administrative_borders(path_to_countries, max_layer_depths, path_to_output, config):
Copy link
Member

@timtroendle timtroendle Oct 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not convinced by this configuration mechanism (which I introduced and) which we are extending here. But I guess it may not the right moment to fix that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I didn't edit the config mechanism. Best to fix it universally in a different PR

@@ -78,6 +74,41 @@ def _country_features(path_to_file, layer_id, study_area):
yield new_feature


def _study_area(config):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this maybe move to utils.py and have a few unit tests (especially including the error that you are catching)?

In that case I'd not hand the config over to it, but rather a bounds object (probably a dict) and a exclusion zones object (probably list of dicts).

@@ -38,10 +38,17 @@ scope:
- "Serbia"
- "Switzerland"
bounds:
x_min: -15.8 # in degrees east
x_min: -30 # in degrees east
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you change the default.yaml, I think you should test whether the entire workflow still works. Alternatively, create another config file. Even then, it would be good to test the workflow.

The package conflicts should be fixed at the moment.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure thing. I haven't checked the functioning of the workflow in this repo on any of these PRs, I have to admit...

@brynpickering
Copy link
Member Author

@timtroendle I'm closing this in preference of #9, which also includes exclusion zone schema and tests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants