From ec32e573cf7f5a4d624c57475116d5a6be263f41 Mon Sep 17 00:00:00 2001 From: BuczynskiRafal Date: Wed, 18 Sep 2024 17:15:40 +0200 Subject: [PATCH] release v-0.1.0 --- .gitattributes | 1 + .gitignore | 10 + README.md | 516 +++++++++-------- gui/app.py | 46 +- gui/help_content.txt | 352 +++++++++++ gui/icon.ico | Bin 0 -> 92032 bytes rcg/fuzzy/categories.py | 169 +++--- rcg/fuzzy/engine.py | 9 - rcg/fuzzy/memberships.py | 96 +-- rcg/fuzzy/rules.py | 707 +++++++++++------------ rcg/fuzzy/test_fuzzy/test_categories.py | 58 +- rcg/fuzzy/test_fuzzy/test_memberships.py | 2 +- rcg/inp_manage/inp.py | 151 ++--- rcg/runner.py | 1 - setup.py | 2 +- 15 files changed, 1252 insertions(+), 868 deletions(-) create mode 100644 .gitattributes create mode 100644 gui/help_content.txt create mode 100644 gui/icon.ico diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d5e334a --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +RCG.exe filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index b6e4761..0604ad4 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,13 @@ dmypy.json # Pyre type checker .pyre/ +.idea/dbnavigator.xml +.gitignore +.gitignore +.gitignore +.idea/inspectionProfiles/Project_Default.xml +.idea/inspectionProfiles/profiles_settings.xml +.idea/rapid-catchment-generator.iml +.idea/modules.xml +.idea/misc.xml +.idea/vcs.xml diff --git a/README.md b/README.md index 0436f6e..8dd5c24 100644 --- a/README.md +++ b/README.md @@ -53,248 +53,286 @@ The diagram below shows the construction of the Rapid Catchment Generator. The m ## About -For the construction of the catchment generator, the type of land use was divided according to Table 1, +For the construction of the catchment generator, the type of landform was divided according to Table 1, the land cover according to Table 2. The categories were determined on the basis of the data presented by (Dołęga, Rogala, 1973), given below in Table 3. -

Table 1. Land use categories

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NumberLand Use
1marshes and lowlands
2flats and plateaus
3flats and plateaus in combination with hills
4hills with gentle slopes
5steeper hills and foothills
6hills and outcrops of mountain ranges
7higher hills
8mountains
9highest mountains
- -

Table 2. Land cover categories

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NumberLand cover
1medium conditions
2permeable areas
3permeable terrain on plains
4hilly
5mountains
6bare rocky slopes
7urban
8suburban
9rural
10forests
11meadows
12arable
13marshes
- -

Table 3. Runoff coefficients o according to Iszkowski

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NumberTopographic terrain definitionDrainage coefficient ϕ
1marshes and lowlands0.20
2flats and plateaus0.25
3flats and plateaus in combination with hills0.30
4hills with gentle slopes0.35
5steeper hills and foothills0.40
6hills and outcrops of mountain ranges0.45
7higher hills0.50
8mountains0.55
9highest mountains0.60-0.70
- -## -Table 4 shows what and how feature values are generated. -

Table 4. SWMM catchment data.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Parameter nameExplanation
NameCatchment names (ID) are generated by adding a number.
RaingageWhen "raingage exists in the uploaded file, it will be assigned to the catchment area being built. If it does not exist, it will be added to the file along with the "timeseries" and assigned to the catchment area being generated.
OutletIf there are receivers in the transferred file, the program will automatically assign it to the catchment area, if there are none, the name of the generated catchment area will be assigned.
AreaA parameter passed by the user.
Percent ImpervParameter calculated as described above and assigned to the catchment area.
WidthThe generated catchment areas are square in shape therefore the length of the side of the catchment area is assigned.
Percent SlopeParameter calculated as described above and assigned to the catchment area.
N-ImpervThe value taken based on the linguistic variables passed to the fuzzy logic controller which were previously mapped with Manning coefficients.
N-PervThe value taken based on the linguistic variables passed to the fuzzy logic controller which were previously mapped with Manning coefficients.
Dstore-ImpervThe value taken based on the linguistic variables passed to the fuzzy logic controller which were previously mapped with typical storage values.
Dstore-PervThe value taken based on the linguistic variables passed to the fuzzy logic controller which were previously mapped with typical storage values.
Percent Zero ImpervThe value taken based on the linguistic variables passed to the fuzzy logic controller which were previously mapped with typical storage values.
RouteToOdpływ z obszarów imperv i perv spływa bezpośrednio do wylotu
CoordinateSquare-shaped catchments are generated, located so that one side is the edge of the contact.
+ +### Table 1. LandForm Categories: +| No. | LandForm Category | +|-----|--------------------------------------------------| +| 1 | Marshes and lowlands | +| 2 | Flats and plateaus | +| 3 | Flats and plateaus in combination with hills | +| 4 | Hills with gentle slopes | +| 5 | Steeper hills and foothills | +| 6 | Hills and outcrops of mountain ranges | +| 7 | Higher hills | +| 8 | Mountains | +| 9 | Highest mountains | + + +### Marshes and Lowlands +**"Marshes and lowlands"** refer to areas that are typically flat or gently sloped and are often water-saturated. These regions are characterized by standing or slow-moving water, making them prone to flooding, with a high capacity for water retention. The waterlogged conditions create unique ecosystems dominated by wetland plants such as reeds, grasses, and shrubs. + +#### Characteristics: +* **High water retention**: Marshes and lowlands have a high capacity to hold water due to their low elevation and flat topography, leading to slow drainage. +* **Low runoff**: The saturated soil and presence of water limit the amount of runoff, as much of the precipitation remains within the area. However, excessive rainfall can lead to overflow and potential flooding. +* **Common features**: Swamps, bogs, floodplains, and low-lying coastal areas. Vegetation in these regions is adapted to waterlogged conditions, and the soil is often rich in organic matter. + +#### When to choose this category: +* Select this category when modeling catchments in areas that are prone to frequent water saturation, such as wetlands, floodplains, or other low-lying areas near bodies of water. This category is ideal for regions where water retention is a significant factor, and where drainage is slow due to flat terrain or a high water table. It is suitable for modeling environments that are vulnerable to flooding or are part of a natural water retention system. +*** + +### Flats and Plateaus +**"Flats and plateaus"** refer to large, flat or gently elevated areas that have minimal slope. These regions are generally stable and well-suited for human habitation and agriculture, as they offer a consistent and level surface. Water flow in such areas is slow, and the overall drainage is often uniform across the terrain. + +#### Characteristics: +* **Low slope**: These areas are either flat or have a very gentle slope, leading to slow-moving water and moderate drainage. +* **Moderate runoff**: Due to the relatively even surface, water tends to spread out rather than quickly running off, though it can accumulate in low spots during heavy rainfall. +* **Common features**: Plains, plateaus, agricultural fields, and urban developments on flat land. + +#### When to choose this category: +* Select this category when modeling catchments in flat or gently elevated areas where water flow is relatively slow and spread out. This category is ideal for agricultural regions, urban developments on flat terrain, or natural plateaus with minimal slope. It's suitable for areas where runoff is moderate and drainage is even across the landscape. +* * * + + +### Flats and Plateaus in Combination with Hills +**"Flats and plateaus in combination with hills"** describes areas where flat or plateau-like terrain is interspersed with hills or gently rolling elevations. These regions feature a mix of flat surfaces and more pronounced slopes, leading to varied drainage and water flow characteristics. + +#### Characteristics: +* **Mixed topography**: A combination of flat areas and gentle hills creates diverse water flow patterns. Flat areas may retain water, while the hills encourage faster runoff. +* **Moderate to high runoff**: Depending on the balance between flat and hilly areas, runoff can vary significantly, with water flowing more rapidly off hills and collecting in the flatter sections. +* **Common features**: Regions with a mix of flat plains and rolling hills, such as transitional landscapes between mountain ranges and valleys. + +#### When to choose this category: +* Use this category when modeling catchments that have a combination of flat terrain and rolling hills, creating varying water flow dynamics. It is suitable for transitional landscapes or areas where flat and hilly features are mixed, causing both retention and runoff within the same region. This category fits well for areas where water behavior is affected by both flat and sloped sections. +* * * + +### Hills with Gentle Slopes +**"Hills with gentle slopes"** refer to areas with low-gradient hills, where the elevation changes are not steep, and the land has a more gradual slope. Water flows more freely than on flat land but at a slower pace than on steeper hills, allowing some infiltration while still generating runoff. + +#### Characteristics: +* **Gentle slope**: The hills have a slight incline, which allows for moderate water flow without the risk of significant erosion or fast-moving runoff. +* **Moderate runoff**: The gentle slope generates runoff, but the land’s permeability and the slow flow allow for some water absorption and moderate drainage. +* **Common features**: Rolling hills, gently sloping highlands, and transitional areas between flatlands and mountain ranges. + +#### When to choose this category: +* Select this category when modeling catchments in areas with gently sloping hills, where the terrain is not flat but also not steep. This category is ideal for regions where water flows more freely than in flat areas, but the risk of erosion and rapid runoff is low. It's suitable for rolling countryside or gently elevated regions with consistent but slow water movement. + +* * * + +### Steeper Hills and Foothills +**"Steeper hills and foothills"** refer to areas where the terrain becomes more pronounced, with moderate to steep slopes leading to faster-moving water and a higher potential for runoff. These regions are often found at the base of mountain ranges or as part of hilly landscapes where elevation changes are significant. + +#### Characteristics: +* **Steep slopes**: The steeper incline of these hills leads to faster water flow, which can result in increased runoff and potential erosion. +* **High runoff**: Water moves quickly down these slopes, reducing the potential for infiltration and increasing the risk of erosion, particularly during heavy rainfall. +* **Common features**: The foothills of mountain ranges, steep hillsides, and areas with rapid elevation changes. + +#### When to choose this category: +* Choose this category when modeling catchments in areas with steep hills or foothills, where water flows rapidly and there is little opportunity for infiltration. This category is suitable for regions where runoff is significant due to steep slopes, and where erosion control and water management are necessary to handle the fast-moving water. It is ideal for foothills, mountainous areas, or regions with pronounced slopes.” + + +### Hills and Outcrops of Mountain Ranges +**"Hills and outcrops of mountain ranges"** refer to areas that lie at the foothills of mountains or where smaller hills and rocky outcrops are present. These areas have moderate slopes and often feature rocky or uneven terrain, but they are less steep compared to full mountain ranges. +#### Characteristics: +* **Moderate permeability**: The presence of rocky outcrops and soil allows for some water infiltration, but runoff can occur due to the slopes and rocky surfaces. +* **Moderate runoff**: Water flows relatively quickly down the slopes, but the presence of vegetation and soil can help retain some moisture. Rocky outcrops may limit infiltration and increase runoff in certain areas. +* **Common features**: Rolling hills, rocky foothills, and areas where small outcrops break through the terrain. These regions often have a mix of soil, vegetation, and exposed rock. + +#### When to choose this category: +* Use this category when modeling catchments in areas near mountain ranges, where the terrain is characterized by a combination of hills and rocky outcrops. This category is suitable for regions with moderate slopes and mixed terrain, where water infiltration and runoff both play significant roles. + +* * * + +### Higher Hills +**"Higher hills"** refer to more elevated hill regions with steeper slopes compared to regular hills. These areas are characterized by their higher elevation and more pronounced slopes, which influence the flow of water and runoff dynamics. + +#### Characteristics: +* **Moderate to low permeability**: The steeper slopes mean that water tends to run off quickly, limiting infiltration. Vegetation may help retain some water, but the terrain generally promotes runoff. +* **Higher runoff**: Water flows more quickly downhill, resulting in greater surface runoff. Steep terrain and less permeable soil contribute to this. +* **Common features**: Large hills, highland areas, and elevated plateaus. Vegetation may still be present, but the steep slopes dominate the water flow. + +#### When to choose this category: +* Choose this category when modeling areas with large hills that are significantly elevated and have steep slopes. These areas are often prone to quick runoff and are less likely to retain water for long periods. This category is suitable for regions where the elevation and slope greatly influence the hydrological behavior of the land. + +* * * + +### Mountains +**"Mountains"** refer to areas with steep slopes and significant elevation above the surrounding terrain. These regions are characterized by dramatic changes in elevation and are often the source of rivers and streams due to their runoff potential. + +#### Characteristics: +* **Low permeability**: The steep slopes and rocky terrain mean that water runs off quickly, with little opportunity for infiltration. Any precipitation in these areas typically flows into streams and rivers. +* **High runoff**: Due to the steep gradients, water travels quickly downhill, leading to a significant amount of surface runoff. The lack of permeable soil in some areas also contributes to this. +* **Common features**: High-altitude mountain ranges, steep valleys, and rugged terrain. These areas often feature sparse vegetation and exposed rock. + +#### When to choose this category: +* Select this category when modeling catchments in mountainous regions where steep slopes and high elevation play a major role in water movement. These areas typically see high levels of runoff, which may lead to the formation of streams and rivers. This category is suitable for regions where rapid water flow and erosion are common concerns. +* * * + +### Highest Mountains +**"Highest mountains"** refer to the most elevated and rugged areas of mountain ranges, often featuring steep cliffs, rocky terrain, and sometimes permanent snow or ice. These areas are typically at altitudes where vegetation is sparse or nonexistent, and the hydrological processes are dominated by fast runoff and extreme conditions. + +#### Characteristics: +* **Very low permeability**: The extremely steep slopes, rocky surfaces, and potential snow cover mean that almost no water infiltrates the ground. Instead, precipitation and melting snow or ice quickly turn into runoff. +* **Very high runoff**: Due to the steep terrain and minimal vegetation, water runs off almost immediately after precipitation or snowmelt. These areas are prone to erosion and rapid water movement. +* **Common features**: High-altitude peaks, glaciers, and steep cliffs. The environment is often harsh, with limited plant life and challenging terrain. + +#### When to choose this category: +* Choose this category when modeling the highest parts of mountain ranges where elevation, steep slopes, and minimal vegetation lead to extreme runoff conditions. This category is ideal for regions that are prone to rapid snowmelt, landslides, or fast-moving rivers and streams originating from the high-altitude peaks. + + + + +*** + +### Table 2. LandCover Categories: +| No. | LandCover Category | +|-----|--------------------------------------------------| +| 1 | Permeable areas | +| 2 | Permeable terrain on plains | +| 3 | Vegetated mountains | +| 4 | Rocky hilly mountains | +| 5 | Urban weakly impervious | +| 6 | Urban moderately impervious | +| 7 | Urban highly impervious | +| 8 | Suburban weakly impervious | +| 9 | Suburban highly impervious | + + +### Permeable Areas +**"Permeable areas"** refer to regions where the majority of the surface is highly permeable, allowing water to easily infiltrate into the ground. These areas are characterized by natural or semi-natural surfaces that absorb rainfall efficiently, reducing surface runoff and enhancing groundwater recharge. +#### Characteristics: +* **High permeability**: The soil and surface materials are naturally absorbent, allowing most of the water to infiltrate rather than flow overland. This greatly reduces the need for stormwater management. +* **Low runoff**: Due to the high infiltration rates, surface runoff is minimal, and flooding risks are generally lower unless the soil becomes saturated. +* **Common features**: Natural landscapes like grasslands, forests, meadows, agricultural lands without heavy compaction, and areas with green infrastructure designed to manage stormwater naturally. + +#### When to choose this category: +* Use this category when modeling rural or undeveloped regions where the surface consists mostly of natural vegetation, soil, or other permeable materials. This category is ideal for areas with minimal urban development, where the natural environment is the primary landscape feature and water infiltration is the dominant process. +* * * + +### Permeable Terrain on Plains +**"Permeable terrain on plains"** refers to flat or gently sloping areas with permeable surfaces, often characterized by open landscapes that allow for good water infiltration due to the permeability of the soil or vegetation cover. These areas are typically large and open, with little obstruction to water movement. + +#### Characteristics: +* **Moderate to high permeability**: The terrain allows water to infiltrate easily, particularly because of the flat or gently sloping nature of the plains, which prevents rapid runoff. +* **Minimal surface runoff**: Due to the low slope and permeable surface, most rainfall infiltrates into the ground, making runoff rare unless the area becomes saturated or experiences heavy rain. +* **Common features**: Agricultural fields, meadows, prairies, and other large expanses of flat, open land with permeable soils or vegetation. These areas often support farming, grazing, or natural ecosystems that benefit from steady water absorption. + +#### When to choose this category: +* Choose this category when modeling flat or gently sloped areas with high water infiltration potential, particularly in rural or agricultural settings. This category is ideal for plains regions where surface water flows slowly and infiltration plays a key role in hydrological processes. It's suitable for areas with minimal development and strong natural water management. +*** + +### Vegetated Mountains +**"Vegetated mountains"** refers to mountainous regions covered with significant vegetation, including forests, shrubs, and other plant life. These areas typically have well-established ecosystems that can absorb water and reduce surface runoff, despite the steep terrain. +#### Characteristics: +* **High permeability**: The vegetation, along with the soil, allows for good water infiltration, reducing the amount of direct runoff. Root systems help anchor the soil and absorb water, stabilizing the slopes. +* **Moderate runoff**: Although the mountainous terrain can cause water to flow quickly downhill, the presence of vegetation mitigates erosion and helps manage runoff more effectively. +* **Common features**: Mountainous forests, wooded highlands, and natural areas with dense plant cover. These regions often have a mix of tree species, shrubs, and grasses that enhance the land’s ability to handle rainfall. + +#### When to choose this category: +* Select this category when modeling catchments in mountainous areas with dense plant life, where vegetation plays a key role in water absorption and erosion control. This is ideal for natural, less-developed highland regions with forests or protected areas where human intervention is minimal. It’s suitable for mountain regions with significant greenery, where infiltration is higher despite the slope. +* * * +### Rocky Hilly Mountains +**"Rocky hilly mountains"** refers to mountainous or hilly areas where the landscape is dominated by rocky terrain with sparse vegetation. These regions tend to have less capacity for water absorption, leading to higher surface runoff and potential erosion. #### Characteristics: +* **Low permeability**: The rocky surfaces, combined with the limited presence of soil and vegetation, make it difficult for water to infiltrate into the ground. As a result, much of the rainfall turns into runoff. +* **High runoff**: The lack of permeable surfaces and vegetation means that water flows quickly downhill, often leading to erosion and the rapid movement of surface water. +* **Common features**: Barren rocky mountains, cliffs, and hilly landscapes with exposed rock formations. Vegetation, if present, is often sparse, consisting of small shrubs or grasses that provide minimal infiltration capability. + +#### When to choose this category: +* Use this category when modeling mountainous or hilly areas that have limited vegetation and are dominated by rock. These environments are typically found in arid or semi-arid regions, or at higher altitudes where plant growth is limited. It's suitable for areas where rainfall results in immediate runoff due to the rocky, impermeable nature of the terrain” +*** + + +### Urban Weakly Impervious +**"Urban weakly impervious"** refers to areas in urban environments where the majority of the surface is permeable or semi-permeable, allowing some degree of water infiltration into the ground. In such areas, only a small portion of the land is covered by impervious materials like concrete, asphalt, or rooftops, which prevent water absorption and contribute to runoff. +#### Characteristics: +* **Low imperviousness (30-60%)**: These areas typically include residential zones with scattered buildings, green spaces, or areas where natural or permeable surfaces (like gardens, parks, or permeable pavements) dominate. +* **Moderate water infiltration**: The ability of the ground to absorb water reduces the amount of surface runoff, though some impermeable surfaces still contribute to runoff, especially during intense rainfall. +* **Common features**: Small residential areas, suburban parks, low-density developments, and areas with green infrastructure designed to manage stormwater. +#### When to choose this category: +* Use this category when modeling catchments for urban areas that prioritize green infrastructure, have a mix of built and natural environments, or where urban developments have relatively low coverage of impermeable surfaces. This category is suitable for residential neighborhoods with gardens, small green spaces, and fewer paved areas.” +* * * + +### Urban Moderately Impervious +**"Urban moderately impervious"** refers to areas where the proportion of impermeable surfaces is higher, but there is still a balance with permeable areas. These regions tend to have more extensive urban development compared to weakly impervious areas, but some green spaces or permeable zones still allow for limited water infiltration. +#### Characteristics: +* **Moderate imperviousness (50-80%)**: Impervious surfaces like roads, buildings, and parking lots cover a significant portion of the area, reducing the ability of water to infiltrate into the ground. +* **Increased runoff**: The higher coverage of impervious surfaces results in greater surface runoff, which may require stormwater management systems to handle excess water, particularly during heavy rainfall events. +* **Common features**: Medium-density urban developments, commercial areas with parking lots, residential neighborhoods with larger buildings, and some streets or sidewalks. +#### When to choose this category: +* Select this category when modeling urban areas with moderate development, where impervious surfaces make up the majority but there are still patches of permeable ground, such as lawns, small parks, or green infrastructure. It is suitable for areas like commercial districts, medium-density housing areas, or developments where runoff management is a concern, but some infiltration is still possible. +* * * + +### Urban Highly Impervious +**"Urban highly impervious"** refers to areas where almost all surfaces are impermeable, meaning little to no water can infiltrate into the ground. These are typically densely developed urban environments, where surfaces like concrete, asphalt, and rooftops dominate, resulting in significant amounts of runoff. +#### Characteristics: +* **High imperviousness (75-100%)**: Nearly all of the area is covered by buildings, roads, sidewalks, and other impermeable surfaces, leaving little room for natural water infiltration. +* **Very high runoff**: Due to the lack of permeable surfaces, most precipitation becomes surface runoff, which can lead to challenges with stormwater management and increased risk of flooding in poorly managed systems. +* **Common features**: Dense urban cores, industrial zones, large commercial complexes, and areas dominated by high-rise buildings, parking structures, and heavily trafficked streets. +#### When to choose this category: +* Choose this category when modeling highly developed urban environments where permeable surfaces are rare. This includes city centers, industrial areas, or large commercial hubs with little vegetation. In such areas, nearly all rainfall becomes runoff, necessitating robust drainage and stormwater management systems. +* * * + +### Suburban Weakly Impervious +**"Suburban weakly impervious"** refers to suburban areas where the majority of the land remains permeable, allowing significant water infiltration. These areas typically consist of low-density residential developments with open spaces, gardens, and unpaved areas. #### Characteristics: +* **Low imperviousness (10-40%)**: A large portion of the surface area remains natural or permeable, such as lawns, gardens, and parks, with only small sections covered by impervious materials like roads or driveways. +* **High water infiltration**: Due to the predominance of permeable surfaces, these areas experience limited surface runoff and are often effective in absorbing rainwater, reducing the strain on stormwater management systems. +* **Common features**: Low-density suburban neighborhoods, single-family homes with large yards, and semi-rural areas with scattered development. +#### When to choose this category: +* Select this category when modeling suburban environments that are characterized by large open spaces and minimal impervious surfaces. It is suitable for areas with single-family homes, small streets, and large permeable surfaces where runoff is minimal and water infiltration is high. +* * * + +### Suburban Highly Impervious +**"Suburban highly impervious"** refers to suburban areas where impervious surfaces are more dominant, typically found in higher-density suburban developments. These areas still retain some green spaces but have more substantial coverage of paved surfaces, roads, and buildings. +#### Characteristics: +* **Moderate-to-high imperviousness (35-65%)**: A significant portion of the land is covered by impermeable surfaces, such as streets, driveways, and buildings, though permeable areas like lawns or small parks are still present. +* **Increased runoff**: The presence of impervious surfaces leads to moderate surface runoff, requiring some level of stormwater management, particularly during heavy rainfall. +* **Common features**: Higher-density suburban neighborhoods, multi-family housing developments, shopping centers, and areas with large roads and parking lots. +#### When to choose this category: +* Use this category when modeling suburban areas that have a higher density of development, where impervious surfaces are more prevalent but not completely dominant. It is suitable for neighborhoods with multi-family homes, small commercial areas, and suburban developments with notable impervious surfaces but still some room for water infiltration. + +*** + + + +### Table 3. Runoff Coefficients According to Iszkowski + +| Number | Topographic Terrain Definition | Drainage Coefficient ϕ | +|--------|------------------------------------------------|------------------------| +| 1 | Marshes and lowlands | 0.20 | +| 2 | Flats and plateaus | 0.25 | +| 3 | Flats and plateaus in combination with hills | 0.30 | +| 4 | Hills with gentle slopes | 0.35 | +| 5 | Steeper hills and foothills | 0.40 | +| 6 | Hills and outcrops of mountain ranges | 0.45 | +| 7 | Higher hills | 0.50 | +| 8 | Mountains | 0.55 | +| 9 | Highest mountains | 0.60-0.70 | + + +*** +### Table 4. SWMM Catchment Data + +| Parameter Name | Explanation | +|---------------------|-----------------------------------------------------------------------------------------------------| +| Name | Catchment names (ID) are generated by adding a number. | +| Raingage | When "raingage" exists in the uploaded file, it will be assigned to the catchment area being built. If it does not exist, it will be added to the file along with the "timeseries" and assigned to the catchment area being generated. | +| Outlet | If there are receivers in the transferred file, the program will automatically assign it to the catchment area. If there are none, the name of the generated catchment area will be assigned. | +| Area | A parameter passed by the user. | +| Percent Imperv | Parameter calculated as described above and assigned to the catchment area. | +| Width | The generated catchment areas are square in shape; therefore, the length of the side of the catchment area is assigned. | +| Percent Slope | Parameter calculated as described above and assigned to the catchment area. | +| N-Imperv | The value taken based on the linguistic variables passed to the fuzzy logic controller, which were previously mapped with Manning coefficients. | +| N-Perv | The value taken based on the linguistic variables passed to the fuzzy logic controller, which were previously mapped with Manning coefficients. | +| Dstore-Imperv | The value taken based on the linguistic variables passed to the fuzzy logic controller, which were previously mapped with typical storage values. | +| Dstore-Perv | The value taken based on the linguistic variables passed to the fuzzy logic controller, which were previously mapped with typical storage values. | +| Percent Zero Imperv | The value taken based on the linguistic variables passed to the fuzzy logic controller, which were previously mapped with typical storage values. | +| RouteTo | Odpływ z obszarów imperv i perv spływa bezpośrednio do wylotu. | +| Coordinate | Square-shaped catchments are generated, located so that one side is the edge of the contact. | + + # Bugs diff --git a/gui/app.py b/gui/app.py index 4da49ca..2bab232 100644 --- a/gui/app.py +++ b/gui/app.py @@ -10,19 +10,26 @@ from rcg.runner import generate_subcatchment +def get_help_file_path(): + if getattr(sys, 'frozen', False): + base_path = sys._MEIPASS + else: + base_path = os.path.abspath(".") + return os.path.join(base_path, "gui", "help_content.txt") + + class RcgApp: def __init__(self, root): self.root = root self.file_path = None self.root.title("Rapid Catchment Generator") - self.set_window_size(width=340, height=270) + self.set_window_size(width=340, height=300) self.create_widgets() def set_window_size(self, width, height): self.root.geometry(f"{width}x{height}") def create_widgets(self): - # Tworzenie rozwijanej listy LandCover land_cover_label = tk.Label(self.root, text="Select land cover type:") land_cover_label.grid(row=0, column=0, padx=10, pady=10, sticky="w") @@ -46,10 +53,9 @@ def create_widgets(self): "marshes", ], width=25 - ) + ) land_cover_combobox.grid(row=0, column=1, padx=10, pady=10) - # Tworzenie rozwijanej listy LandType land_form_label = tk.Label(self.root, text="Select land form type:") land_form_label.grid(row=1, column=0, padx=10, pady=10, sticky="w") @@ -69,10 +75,9 @@ def create_widgets(self): "highest mountains", ], width=25 - ) + ) land_form_combobox.grid(row=1, column=1, padx=10, pady=10) - # Tworzenie pola do wprowadzenia parametru Area area_label = tk.Label(self.root, text="Area [ha]:") area_label.grid(row=2, column=0, padx=10, pady=10, sticky="w") @@ -80,37 +85,52 @@ def create_widgets(self): area_entry = tk.Entry(self.root, textvariable=self.area_var, width=28) area_entry.grid(row=2, column=1, padx=10, pady=10) - # Selecting file file_label = tk.Label(self.root, text="Select file:") file_label.grid(row=3, column=0, padx=10, pady=10, sticky="w") choose_file_button = tk.Button(self.root, text="Select file", command=self.choose_file, width=23) choose_file_button.grid(row=3, column=1, padx=10, pady=10) - # Selected file selected_label = tk.Label(self.root, text="Selected file:") selected_label.grid(row=4, column=0, padx=10, pady=10, sticky="w") self.selected_file_label = tk.Entry(self.root, width=28, state='readonly') self.selected_file_label.grid(row=4, column=1, padx=10, pady=10, sticky="w") - # Tworzenie przycisku Run run_label = tk.Label(self.root, text="Run simulation:") run_label.grid(row=5, column=0, padx=10, pady=10, sticky="w") run_button = tk.Button(self.root, text="Run", command=self.run_simulation, width=23, bg="#36D7B7") run_button.grid(row=5, column=1, padx=10, pady=10, sticky="w") + help_button = tk.Button(self.root, text="Help", command=self.show_help, width=23, bg="#a5d8ff") + help_button.grid(row=6, column=1, padx=10, pady=10, sticky="w") + + def show_help(self): + help_window = tk.Toplevel(self.root) + help_window.title("Help - Categories and Instructions") + help_window.geometry("400x400") + + try: + with open(get_help_file_path(), "r") as file: + text = file.read() + except FileNotFoundError: + messagebox.showerror("Error", "Help file not found.") + return + + help_text = tk.Text(help_window, wrap='word', width=50, height=20) + help_text.insert(tk.END, text) + help_text.config(state="disabled") + help_text.pack(padx=10, pady=10, expand=True, fill=tk.BOTH) + def choose_file(self): - file_path = filedialog.askopenfilename(title="Wybierz plik", filetypes=(("Pliki tekstowe", "*.txt"), ("Wszystkie pliki", "*.*"))) + file_path = filedialog.askopenfilename(title="Select file", filetypes=(("Text files", "*.txt"), ("All files", "*.*"))) if file_path: - # Sprawdź, czy plik ma rozszerzenie ".inp" file_extension = os.path.splitext(file_path)[1] if file_extension.lower() != ".inp": messagebox.showerror("Error", "Please select a file with the '.inp' extension.") return - # Aktualizowanie pola tekstowego z nazwą pliku self.selected_file_label.config(state='normal') self.selected_file_label.delete(0, tk.END) self.selected_file_label.insert(0, file_path) @@ -150,8 +170,8 @@ def run_simulation(self): messagebox.showinfo("Information", f"Simulation has been executed successfully for values: \n\nArea: {area}\nLand cover type: {land_cover}\nLand form type: {land_form}\nFile: {self.file_path}") + if __name__ == "__main__": root = tk.Tk() - root.iconbitmap("gui\icon.png") app = RcgApp(root) root.mainloop() diff --git a/gui/help_content.txt b/gui/help_content.txt new file mode 100644 index 0000000..85b0c73 --- /dev/null +++ b/gui/help_content.txt @@ -0,0 +1,352 @@ +Rapid catchment generator +Tool for rapid prototyping of a hydraulic model that can be read and edited with SWMM. The generator was created using feature analysis and surface runoff research from the literature. Fuzzy logic controller rules were developed using parameterized categories of soil, slope, and permeability. The catchment configuration procedure was simplified by mapping typical storage and Manning's coefficients. The use of fuzzy logic rules allows the system to be modified to adjust the categories to certain situations. The use of membership functions allows us to increase computation accuracy and customize the tool to diverse applications. Following alteration of the catchment in the SWMM GUI allows for accurate portrayal of the real condition of the catchment. +Using the Graphical User Interface (GUI) +One of the most user-friendly ways to utilize the capabilities of RCG is through its intuitive graphical user interface (GUI) application. The GUI provides an easy-to-navigate environment where users can input parameters, manipulate data, and visualize results, all without the need for complex coding or scripting. +Getting Started +To get started with the RCG GUI, simply follow these steps: + +Click the file "RCG.exe", gitchub will take you to "https://github.com/BuczynskiRafal/rapid-catchment-generator/blob/main/RCG.exe". On the right side of the window is the "download" button, download the file. +Double-click on the file downloaded to the desired location. After installation, the RCG window will appear. +Fill in the data and generate the catchment with the "Run" button. + + + + +Requirements + +Python 3 + +Usage from terminal +Create a virtual environment: +python3 -m venv venv + +Download and install the required dependencies: +python3 pip install -r requirements + + +To run the script, use the following command: +python3 rcg.runner file path + + +where file path is the path to the SWMM model file. +Enter data into the terminal according to the instructions it displays. +The file is automatically saved in the same directory. +How it is built +The diagram below shows the construction of the Rapid Catchment Generator. The modular form of the system allows easy adaptation to specific user needs and tuning to achieve greater accuracy. + + + +About +For the construction of the catchment generator, the type of landform was divided according to Table 1, +the land cover according to Table 2. +The categories were determined on the basis of the data presented by (Dołęga, Rogala, 1973), given below in Table 3. +Table 1. LandForm Categories: +| No. | LandForm Category | +|-----|--------------------------------------------------| +| 1 | Marshes and lowlands | +| 2 | Flats and plateaus | +| 3 | Flats and plateaus in combination with hills | +| 4 | Hills with gentle slopes | +| 5 | Steeper hills and foothills | +| 6 | Hills and outcrops of mountain ranges | +| 7 | Higher hills | +| 8 | Mountains | +| 9 | Highest mountains | +Marshes and Lowlands +"Marshes and lowlands" refer to areas that are typically flat or gently sloped and are often water-saturated. These regions are characterized by standing or slow-moving water, making them prone to flooding, with a high capacity for water retention. The waterlogged conditions create unique ecosystems dominated by wetland plants such as reeds, grasses, and shrubs. +Characteristics: + +High water retention: Marshes and lowlands have a high capacity to hold water due to their low elevation and flat topography, leading to slow drainage. +Low runoff: The saturated soil and presence of water limit the amount of runoff, as much of the precipitation remains within the area. However, excessive rainfall can lead to overflow and potential flooding. +Common features: Swamps, bogs, floodplains, and low-lying coastal areas. Vegetation in these regions is adapted to waterlogged conditions, and the soil is often rich in organic matter. + +When to choose this category: + +Select this category when modeling catchments in areas that are prone to frequent water saturation, such as wetlands, floodplains, or other low-lying areas near bodies of water. This category is ideal for regions where water retention is a significant factor, and where drainage is slow due to flat terrain or a high water table. It is suitable for modeling environments that are vulnerable to flooding or are part of a natural water retention system. + + +Flats and Plateaus +"Flats and plateaus" refer to large, flat or gently elevated areas that have minimal slope. These regions are generally stable and well-suited for human habitation and agriculture, as they offer a consistent and level surface. Water flow in such areas is slow, and the overall drainage is often uniform across the terrain. +Characteristics: + +Low slope: These areas are either flat or have a very gentle slope, leading to slow-moving water and moderate drainage. +Moderate runoff: Due to the relatively even surface, water tends to spread out rather than quickly running off, though it can accumulate in low spots during heavy rainfall. +Common features: Plains, plateaus, agricultural fields, and urban developments on flat land. + +When to choose this category: + +Select this category when modeling catchments in flat or gently elevated areas where water flow is relatively slow and spread out. This category is ideal for agricultural regions, urban developments on flat terrain, or natural plateaus with minimal slope. It's suitable for areas where runoff is moderate and drainage is even across the landscape. + + +Flats and Plateaus in Combination with Hills +"Flats and plateaus in combination with hills" describes areas where flat or plateau-like terrain is interspersed with hills or gently rolling elevations. These regions feature a mix of flat surfaces and more pronounced slopes, leading to varied drainage and water flow characteristics. +Characteristics: + +Mixed topography: A combination of flat areas and gentle hills creates diverse water flow patterns. Flat areas may retain water, while the hills encourage faster runoff. +Moderate to high runoff: Depending on the balance between flat and hilly areas, runoff can vary significantly, with water flowing more rapidly off hills and collecting in the flatter sections. +Common features: Regions with a mix of flat plains and rolling hills, such as transitional landscapes between mountain ranges and valleys. + +When to choose this category: + +Use this category when modeling catchments that have a combination of flat terrain and rolling hills, creating varying water flow dynamics. It is suitable for transitional landscapes or areas where flat and hilly features are mixed, causing both retention and runoff within the same region. This category fits well for areas where water behavior is affected by both flat and sloped sections. + + +Hills with Gentle Slopes +"Hills with gentle slopes" refer to areas with low-gradient hills, where the elevation changes are not steep, and the land has a more gradual slope. Water flows more freely than on flat land but at a slower pace than on steeper hills, allowing some infiltration while still generating runoff. +Characteristics: + +Gentle slope: The hills have a slight incline, which allows for moderate water flow without the risk of significant erosion or fast-moving runoff. +Moderate runoff: The gentle slope generates runoff, but the land’s permeability and the slow flow allow for some water absorption and moderate drainage. +Common features: Rolling hills, gently sloping highlands, and transitional areas between flatlands and mountain ranges. + +When to choose this category: + +Select this category when modeling catchments in areas with gently sloping hills, where the terrain is not flat but also not steep. This category is ideal for regions where water flows more freely than in flat areas, but the risk of erosion and rapid runoff is low. It's suitable for rolling countryside or gently elevated regions with consistent but slow water movement. + + +Steeper Hills and Foothills +"Steeper hills and foothills" refer to areas where the terrain becomes more pronounced, with moderate to steep slopes leading to faster-moving water and a higher potential for runoff. These regions are often found at the base of mountain ranges or as part of hilly landscapes where elevation changes are significant. +Characteristics: + +Steep slopes: The steeper incline of these hills leads to faster water flow, which can result in increased runoff and potential erosion. +High runoff: Water moves quickly down these slopes, reducing the potential for infiltration and increasing the risk of erosion, particularly during heavy rainfall. +Common features: The foothills of mountain ranges, steep hillsides, and areas with rapid elevation changes. + +When to choose this category: + +Choose this category when modeling catchments in areas with steep hills or foothills, where water flows rapidly and there is little opportunity for infiltration. This category is suitable for regions where runoff is significant due to steep slopes, and where erosion control and water management are necessary to handle the fast-moving water. It is ideal for foothills, mountainous areas, or regions with pronounced slopes.” + +Hills and Outcrops of Mountain Ranges +"Hills and outcrops of mountain ranges" refer to areas that lie at the foothills of mountains or where smaller hills and rocky outcrops are present. These areas have moderate slopes and often feature rocky or uneven terrain, but they are less steep compared to full mountain ranges. +Characteristics: + +Moderate permeability: The presence of rocky outcrops and soil allows for some water infiltration, but runoff can occur due to the slopes and rocky surfaces. +Moderate runoff: Water flows relatively quickly down the slopes, but the presence of vegetation and soil can help retain some moisture. Rocky outcrops may limit infiltration and increase runoff in certain areas. +Common features: Rolling hills, rocky foothills, and areas where small outcrops break through the terrain. These regions often have a mix of soil, vegetation, and exposed rock. + +When to choose this category: + +Use this category when modeling catchments in areas near mountain ranges, where the terrain is characterized by a combination of hills and rocky outcrops. This category is suitable for regions with moderate slopes and mixed terrain, where water infiltration and runoff both play significant roles. + + +Higher Hills +"Higher hills" refer to more elevated hill regions with steeper slopes compared to regular hills. These areas are characterized by their higher elevation and more pronounced slopes, which influence the flow of water and runoff dynamics. +Characteristics: + +Moderate to low permeability: The steeper slopes mean that water tends to run off quickly, limiting infiltration. Vegetation may help retain some water, but the terrain generally promotes runoff. +Higher runoff: Water flows more quickly downhill, resulting in greater surface runoff. Steep terrain and less permeable soil contribute to this. +Common features: Large hills, highland areas, and elevated plateaus. Vegetation may still be present, but the steep slopes dominate the water flow. + +When to choose this category: + +Choose this category when modeling areas with large hills that are significantly elevated and have steep slopes. These areas are often prone to quick runoff and are less likely to retain water for long periods. This category is suitable for regions where the elevation and slope greatly influence the hydrological behavior of the land. + + +Mountains +"Mountains" refer to areas with steep slopes and significant elevation above the surrounding terrain. These regions are characterized by dramatic changes in elevation and are often the source of rivers and streams due to their runoff potential. +Characteristics: + +Low permeability: The steep slopes and rocky terrain mean that water runs off quickly, with little opportunity for infiltration. Any precipitation in these areas typically flows into streams and rivers. +High runoff: Due to the steep gradients, water travels quickly downhill, leading to a significant amount of surface runoff. The lack of permeable soil in some areas also contributes to this. +Common features: High-altitude mountain ranges, steep valleys, and rugged terrain. These areas often feature sparse vegetation and exposed rock. + +When to choose this category: + +Select this category when modeling catchments in mountainous regions where steep slopes and high elevation play a major role in water movement. These areas typically see high levels of runoff, which may lead to the formation of streams and rivers. This category is suitable for regions where rapid water flow and erosion are common concerns. + + +Highest Mountains +"Highest mountains" refer to the most elevated and rugged areas of mountain ranges, often featuring steep cliffs, rocky terrain, and sometimes permanent snow or ice. These areas are typically at altitudes where vegetation is sparse or nonexistent, and the hydrological processes are dominated by fast runoff and extreme conditions. +Characteristics: + +Very low permeability: The extremely steep slopes, rocky surfaces, and potential snow cover mean that almost no water infiltrates the ground. Instead, precipitation and melting snow or ice quickly turn into runoff. +Very high runoff: Due to the steep terrain and minimal vegetation, water runs off almost immediately after precipitation or snowmelt. These areas are prone to erosion and rapid water movement. +Common features: High-altitude peaks, glaciers, and steep cliffs. The environment is often harsh, with limited plant life and challenging terrain. + +When to choose this category: + +Choose this category when modeling the highest parts of mountain ranges where elevation, steep slopes, and minimal vegetation lead to extreme runoff conditions. This category is ideal for regions that are prone to rapid snowmelt, landslides, or fast-moving rivers and streams originating from the high-altitude peaks. + + +Table 2. LandCover Categories: +| No. | LandCover Category | +|-----|--------------------------------------------------| +| 1 | Permeable areas | +| 2 | Permeable terrain on plains | +| 3 | Vegetated mountains | +| 4 | Rocky hilly mountains | +| 5 | Urban weakly impervious | +| 6 | Urban moderately impervious | +| 7 | Urban highly impervious | +| 8 | Suburban weakly impervious | +| 9 | Suburban highly impervious | +Permeable Areas +"Permeable areas" refer to regions where the majority of the surface is highly permeable, allowing water to easily infiltrate into the ground. These areas are characterized by natural or semi-natural surfaces that absorb rainfall efficiently, reducing surface runoff and enhancing groundwater recharge. +Characteristics: + +High permeability: The soil and surface materials are naturally absorbent, allowing most of the water to infiltrate rather than flow overland. This greatly reduces the need for stormwater management. +Low runoff: Due to the high infiltration rates, surface runoff is minimal, and flooding risks are generally lower unless the soil becomes saturated. +Common features: Natural landscapes like grasslands, forests, meadows, agricultural lands without heavy compaction, and areas with green infrastructure designed to manage stormwater naturally. + +When to choose this category: + +Use this category when modeling rural or undeveloped regions where the surface consists mostly of natural vegetation, soil, or other permeable materials. This category is ideal for areas with minimal urban development, where the natural environment is the primary landscape feature and water infiltration is the dominant process. + + +Permeable Terrain on Plains +"Permeable terrain on plains" refers to flat or gently sloping areas with permeable surfaces, often characterized by open landscapes that allow for good water infiltration due to the permeability of the soil or vegetation cover. These areas are typically large and open, with little obstruction to water movement. +Characteristics: + +Moderate to high permeability: The terrain allows water to infiltrate easily, particularly because of the flat or gently sloping nature of the plains, which prevents rapid runoff. +Minimal surface runoff: Due to the low slope and permeable surface, most rainfall infiltrates into the ground, making runoff rare unless the area becomes saturated or experiences heavy rain. +Common features: Agricultural fields, meadows, prairies, and other large expanses of flat, open land with permeable soils or vegetation. These areas often support farming, grazing, or natural ecosystems that benefit from steady water absorption. + +When to choose this category: + +Choose this category when modeling flat or gently sloped areas with high water infiltration potential, particularly in rural or agricultural settings. This category is ideal for plains regions where surface water flows slowly and infiltration plays a key role in hydrological processes. It's suitable for areas with minimal development and strong natural water management. + + +Vegetated Mountains +"Vegetated mountains" refers to mountainous regions covered with significant vegetation, including forests, shrubs, and other plant life. These areas typically have well-established ecosystems that can absorb water and reduce surface runoff, despite the steep terrain. +Characteristics: + +High permeability: The vegetation, along with the soil, allows for good water infiltration, reducing the amount of direct runoff. Root systems help anchor the soil and absorb water, stabilizing the slopes. +Moderate runoff: Although the mountainous terrain can cause water to flow quickly downhill, the presence of vegetation mitigates erosion and helps manage runoff more effectively. +Common features: Mountainous forests, wooded highlands, and natural areas with dense plant cover. These regions often have a mix of tree species, shrubs, and grasses that enhance the land’s ability to handle rainfall. + +When to choose this category: + +Select this category when modeling catchments in mountainous areas with dense plant life, where vegetation plays a key role in water absorption and erosion control. This is ideal for natural, less-developed highland regions with forests or protected areas where human intervention is minimal. It’s suitable for mountain regions with significant greenery, where infiltration is higher despite the slope. + + +Rocky Hilly Mountains +"Rocky hilly mountains" refers to mountainous or hilly areas where the landscape is dominated by rocky terrain with sparse vegetation. These regions tend to have less capacity for water absorption, leading to higher surface runoff and potential erosion. #### Characteristics: + +Low permeability: The rocky surfaces, combined with the limited presence of soil and vegetation, make it difficult for water to infiltrate into the ground. As a result, much of the rainfall turns into runoff. +High runoff: The lack of permeable surfaces and vegetation means that water flows quickly downhill, often leading to erosion and the rapid movement of surface water. +Common features: Barren rocky mountains, cliffs, and hilly landscapes with exposed rock formations. Vegetation, if present, is often sparse, consisting of small shrubs or grasses that provide minimal infiltration capability. + +When to choose this category: + +Use this category when modeling mountainous or hilly areas that have limited vegetation and are dominated by rock. These environments are typically found in arid or semi-arid regions, or at higher altitudes where plant growth is limited. It's suitable for areas where rainfall results in immediate runoff due to the rocky, impermeable nature of the terrain” + + +Urban Weakly Impervious +"Urban weakly impervious" refers to areas in urban environments where the majority of the surface is permeable or semi-permeable, allowing some degree of water infiltration into the ground. In such areas, only a small portion of the land is covered by impervious materials like concrete, asphalt, or rooftops, which prevent water absorption and contribute to runoff. +Characteristics: + +Low imperviousness (30-60%): These areas typically include residential zones with scattered buildings, green spaces, or areas where natural or permeable surfaces (like gardens, parks, or permeable pavements) dominate. +Moderate water infiltration: The ability of the ground to absorb water reduces the amount of surface runoff, though some impermeable surfaces still contribute to runoff, especially during intense rainfall. +Common features: Small residential areas, suburban parks, low-density developments, and areas with green infrastructure designed to manage stormwater. + +When to choose this category: + +Use this category when modeling catchments for urban areas that prioritize green infrastructure, have a mix of built and natural environments, or where urban developments have relatively low coverage of impermeable surfaces. This category is suitable for residential neighborhoods with gardens, small green spaces, and fewer paved areas.” + + +Urban Moderately Impervious +"Urban moderately impervious" refers to areas where the proportion of impermeable surfaces is higher, but there is still a balance with permeable areas. These regions tend to have more extensive urban development compared to weakly impervious areas, but some green spaces or permeable zones still allow for limited water infiltration. +Characteristics: + +Moderate imperviousness (50-80%): Impervious surfaces like roads, buildings, and parking lots cover a significant portion of the area, reducing the ability of water to infiltrate into the ground. +Increased runoff: The higher coverage of impervious surfaces results in greater surface runoff, which may require stormwater management systems to handle excess water, particularly during heavy rainfall events. +Common features: Medium-density urban developments, commercial areas with parking lots, residential neighborhoods with larger buildings, and some streets or sidewalks. + +When to choose this category: + +Select this category when modeling urban areas with moderate development, where impervious surfaces make up the majority but there are still patches of permeable ground, such as lawns, small parks, or green infrastructure. It is suitable for areas like commercial districts, medium-density housing areas, or developments where runoff management is a concern, but some infiltration is still possible. + + +Urban Highly Impervious +"Urban highly impervious" refers to areas where almost all surfaces are impermeable, meaning little to no water can infiltrate into the ground. These are typically densely developed urban environments, where surfaces like concrete, asphalt, and rooftops dominate, resulting in significant amounts of runoff. +Characteristics: + +High imperviousness (75-100%): Nearly all of the area is covered by buildings, roads, sidewalks, and other impermeable surfaces, leaving little room for natural water infiltration. +Very high runoff: Due to the lack of permeable surfaces, most precipitation becomes surface runoff, which can lead to challenges with stormwater management and increased risk of flooding in poorly managed systems. +Common features: Dense urban cores, industrial zones, large commercial complexes, and areas dominated by high-rise buildings, parking structures, and heavily trafficked streets. + +When to choose this category: + +Choose this category when modeling highly developed urban environments where permeable surfaces are rare. This includes city centers, industrial areas, or large commercial hubs with little vegetation. In such areas, nearly all rainfall becomes runoff, necessitating robust drainage and stormwater management systems. + + +Suburban Weakly Impervious +"Suburban weakly impervious" refers to suburban areas where the majority of the land remains permeable, allowing significant water infiltration. These areas typically consist of low-density residential developments with open spaces, gardens, and unpaved areas. #### Characteristics: + +Low imperviousness (10-40%): A large portion of the surface area remains natural or permeable, such as lawns, gardens, and parks, with only small sections covered by impervious materials like roads or driveways. +High water infiltration: Due to the predominance of permeable surfaces, these areas experience limited surface runoff and are often effective in absorbing rainwater, reducing the strain on stormwater management systems. +Common features: Low-density suburban neighborhoods, single-family homes with large yards, and semi-rural areas with scattered development. + +When to choose this category: + +Select this category when modeling suburban environments that are characterized by large open spaces and minimal impervious surfaces. It is suitable for areas with single-family homes, small streets, and large permeable surfaces where runoff is minimal and water infiltration is high. + + +Suburban Highly Impervious +"Suburban highly impervious" refers to suburban areas where impervious surfaces are more dominant, typically found in higher-density suburban developments. These areas still retain some green spaces but have more substantial coverage of paved surfaces, roads, and buildings. +Characteristics: + +Moderate-to-high imperviousness (35-65%): A significant portion of the land is covered by impermeable surfaces, such as streets, driveways, and buildings, though permeable areas like lawns or small parks are still present. +Increased runoff: The presence of impervious surfaces leads to moderate surface runoff, requiring some level of stormwater management, particularly during heavy rainfall. +Common features: Higher-density suburban neighborhoods, multi-family housing developments, shopping centers, and areas with large roads and parking lots. + +When to choose this category: + +Use this category when modeling suburban areas that have a higher density of development, where impervious surfaces are more prevalent but not completely dominant. It is suitable for neighborhoods with multi-family homes, small commercial areas, and suburban developments with notable impervious surfaces but still some room for water infiltration. + + +Table 3. Runoff Coefficients According to Iszkowski +| Number | Topographic Terrain Definition | Drainage Coefficient ϕ | +|--------|------------------------------------------------|------------------------| +| 1 | Marshes and lowlands | 0.20 | +| 2 | Flats and plateaus | 0.25 | +| 3 | Flats and plateaus in combination with hills | 0.30 | +| 4 | Hills with gentle slopes | 0.35 | +| 5 | Steeper hills and foothills | 0.40 | +| 6 | Hills and outcrops of mountain ranges | 0.45 | +| 7 | Higher hills | 0.50 | +| 8 | Mountains | 0.55 | +| 9 | Highest mountains | 0.60-0.70 | + +Table 4. SWMM Catchment Data +| Parameter Name | Explanation | +|---------------------|-----------------------------------------------------------------------------------------------------| +| Name | Catchment names (ID) are generated by adding a number. | +| Raingage | When "raingage" exists in the uploaded file, it will be assigned to the catchment area being built. If it does not exist, it will be added to the file along with the "timeseries" and assigned to the catchment area being generated. | +| Outlet | If there are receivers in the transferred file, the program will automatically assign it to the catchment area. If there are none, the name of the generated catchment area will be assigned. | +| Area | A parameter passed by the user. | +| Percent Imperv | Parameter calculated as described above and assigned to the catchment area. | +| Width | The generated catchment areas are square in shape; therefore, the length of the side of the catchment area is assigned. | +| Percent Slope | Parameter calculated as described above and assigned to the catchment area. | +| N-Imperv | The value taken based on the linguistic variables passed to the fuzzy logic controller, which were previously mapped with Manning coefficients. | +| N-Perv | The value taken based on the linguistic variables passed to the fuzzy logic controller, which were previously mapped with Manning coefficients. | +| Dstore-Imperv | The value taken based on the linguistic variables passed to the fuzzy logic controller, which were previously mapped with typical storage values. | +| Dstore-Perv | The value taken based on the linguistic variables passed to the fuzzy logic controller, which were previously mapped with typical storage values. | +| Percent Zero Imperv | The value taken based on the linguistic variables passed to the fuzzy logic controller, which were previously mapped with typical storage values. | +| RouteTo | Odpływ z obszarów imperv i perv spływa bezpośrednio do wylotu. | +| Coordinate | Square-shaped catchments are generated, located so that one side is the edge of the contact. | +Bugs +If you encounter any bugs or issues while using our software, please feel free to report them on the project's issue tracker. When reporting a bug, please provide as much information as possible to help us reproduce and resolve the issue, including: + +A clear and concise description of the issue +Steps to reproduce the problem +Expected behavior and actual behavior +Any error messages or logs that may be relevant + +Your feedback is invaluable and will help us improve the software for all users. +Contributing +We welcome and appreciate contributions from the community! If you're interested in contributing to this project, please follow these steps: + +Fork the repository on GitHub. +Create a new branch for your changes. +Make your changes, including updates to documentation if needed. +Write tests to ensure your changes are working as expected. +Ensure all tests pass and there are no linting or code style issues. +Commit your changes and create a pull request, providing a detailed description of your changes. + +We will review your pull request as soon as possible and provide feedback. Once your contribution is approved, it will be merged into the main branch. +For more information about contributing to the project, please see our contributing guide. + + +License +This project is licensed under the MIT License. By using, distributing, or contributing to this project, you agree to the terms and conditions of the license. Please refer to the LICENSE.md file for the full text of the license. \ No newline at end of file diff --git a/gui/icon.ico b/gui/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..3f5a8a986b62327eba74bb4fc553c25758746821 GIT binary patch literal 92032 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�aW}XsFy{L5ql4RHOO%=QDlS0%&`%W|!4(gGjYeG!`64 zgJ%rygHiZyYc02#F2=fQz6;!*tGc5B&_I2`F!=}1V(Rn1LBF^X=0= z!cDC%U)a#0hsD($(*#;Rqa}f&rzjbhOd7WIGAs~GoG}0zP!Zg;r{0R_w@zjHN8iK_ zjRHz&_ZT{SDemOwFj0#bUlkr7`_JYoT{fzR)E!aj*><4M9_dDY#rl+~rJ(qIcFHd5@f?*&cGH(Q6ehW}R2_YiJ z^4iz^7uQ|6irVNfF{WmnTCMz&YIDJO>hF6Rog)^Y(>>Hk73`bcUoSPiz$#*^T!GD5 zWj!ZOujG@hmE2}LhAhR>BGsyY2VyZXA`6<-e|00>lP@G69!{=*x8!d7jPUV~{yW_e z`S#`>>tc=4IL5uwjzT+v#td;e(a3;CptA)GV8AbkIdnCUJ?@#*54bn6(*{-SP)6s< zwPYt=KqIfu6et-TC5W-{^KhY(2dNY5oUf+*-s7h7sn0gf&V~`Lb zD8@rkH`HG7aNJ%)Xt$gx$g1iTeDvg_BV2pcdS3pj_ag|gub_07M#^H%8vwxjh;un7 zeEREr>zhBLwrmlh*ijKjKrFH7F!Jw@qkQba#EAmj&MmD)gct)clxSHH%ObJ-RWS(8 z)pEZzT36`RvD!W8Z!E;%8f1+)xURL?kMsr5$ow z2yOLrHPVxahh6Wv0aS_BVT`4qPUDz=K=OK06AEBPnru7m4=gx)6>hjb5ET7m&=}Dw zH!s#ybq9BVR}ezt1Q8Av0%^LcdbLd^S1%@)j7|JbM zdHFv)k!L;Y30%|aa@IsaBeOJeL(fFE1lSY<<0#vMFcE|ab;)OuG!W*>m>80uF2o_1 zjK%(kG4kkR2)$Oy&sVCz(;q*JMeDn0-ONhi?Z5z(l(MtK8E$w8oK)w(1M^g8wobVSWNw>`x2ANLUsHmRVzv)_#STYLO$@3Z}E-OevWhb zyb<6oZUACbtj)OPmUX=Na+h;bbpsnh!H#5r1^_$X`g-tW~6~26x}Wd z96ZWLKKMpjA#(bLE>k9>T}HO}Kn-}@u<+O+ArFBLLa;(BrUspVtZASskXf>!Iz_LC zedIH!A97z}y9EfY-eCHsNz7+1Vnk}(o)J?Zw~oeFt|5E(uc%L^jz{t*u0cQ5iI0d1 zrtL8ZQieocGT>~6pXi`R?$5|$jzGJ;x!MeabBWlYF<$ffk8su1H)3r@yaN@McV^sW z4S-e>SDkLbKm6nIY}`DFYt|~Q%$dt#j6qvn@_Q}E9sf+)4blLVq@&kgv4pgJ9nqY( zExQMGpwI$O#B0e*hWFu#^(AOX1Ne3iYNzM=-=R>?Xl!iJKH+EBC|CnlGql&{)1N$! z#mg4)osDg73YLP5o;VJ7j(6|dx(f#lQ!UJovq)wjHSO=g&R(EUa2$*q1v`sA)EH_sf#fupGWqG zjZn}2NO4hBU8#wpXw;-er$I5jh(tlKIFq4~)K?G>54$_Hd+$%|^%5ncIv3TDL(lyb*fawEiEi7aW*c<(c4 zEy@u;Tcd6U0-%xQS(Cl9VPaL&?<#jRqTH)C2HKYa+l3Lhy8^e|pf5oCrC1hrBeWWjs79+)|!@lL=)c)x) z#0M-x+6A}YKp;UCToJHdk*Q7!cKSf2bCOBwj`=0Ha*-gvfkm17p=98w3b9w*S>+zcB_DN1W_B!3*L z5E_Dv)l$l`)xzBS;pB(h7ipJ>3a*(kdDA91=^~nWbEhI4K`BGY^s$FAef*=CUg(nd zF+1(IW9K0S6Jp|EbW4o2#G)!+^Z_jb#u!WxG$9&pz*0(UtB5TjzmuxaBqUj2rTLYD1XOU@W#yF>G(|BvEP2coTR z>S!BNN)Jfstm>hX+H+4y!G||pOIMX#a-2AfSWeouinF^LF;)G4A3N#g?O5&$P|s*? zY1940ud23NDY9?X^Mw=N&d}%xUtK%RDm64RWvVb7mN}laS5|cql1gnzC=gEB7VsuV z?qCK&0d+#GTNte)MkoqP_Nc!jj4XoCBZLw&++f?cf6wRzYca$5p!dM+4+^brdflMbo~F=we!Q4LLy=v)(EsEofYU*Q*kM5mBvfV!PF9L=DaZ(VjR{BrknH$>xJM>`2*9hJQ;uT zxnydIMTvNil_U~sRnRgCgIWyIDKJMIKz_(RP?3y)By>U~MzD@69I}LpJ(v{C9V?5#2R26}OTo@?2acs* z)fl4^Y6jED(VD|#8KLmldq0Buf%iv>v=V2HI_-1MY9J62;4LjEu#1L_d7V+)V z&f}9OejR7CyIkeqUB&>!n6&tAy>%nUz4Z&&;gQ7s7c=uP1``FFDZ{URB(0$w)Afi4 z8&XxC^=W`nL`*c8B4BzRJ-f7i)edDRUmn{XEr5xL74fPR~+e|gg< z*tBUYwwCXdbrWL=Q=X;&@F;qRFDFcNF(ufphhBtEK`cC4Bm#;QJ{741#Y}f`U61rU zMuq4jTBIm7y?_(}6Evw_1xVu+%+g;p|7-}*bf`;# zH_?=j*X4Joe&5GX76m~JG;8=9H^bT2le--4`m~aJ2CKRWV7-zR0oyLIWyJWz@pom! zs9*}8jAExp`Jg4Zw?C2co{NaB0&v7a@jVYwh#sO>1WWXq*qsrfS}((V-e>zh8AdOGT^GqQI^+$9V^iuzl=e9BMw!4J<%uD=hUhA8PF)=^BgS@xJi zsULj=Q=7M8BE+swdKraByMe3|km(Y-9@8o@(>#MvPZ%2~tGrQYW5q8GfRRT1QYr=9CY-mP3drCdrO{|4N3ob`!s#>oL8Ma`;8Sv`VBKkWvw^NVi0MD)cek5<0!~ zx=x9-OZ;|EcdAS5l;9QZ#q?PLq~kFqNGD}V^R$jneBP(#HHvoz4XIZ&o9dCK(7cfK3w08$)r{hB5X)YRJ`Kuqp ztsB-+?}GLslqu&dZI5&V+Ac6lMU{Fng#gA<0@P}9@&r-Qks<}y9gA@b2quBr@W+1suBBx?7)}XzD zII@K7&<7IBBxRGu^{=0@j;kAi4qRMPdmWKnf)bf^;Ib1XpTG9meFT_-JSzejiL$OYwjm$4Q8o z;UV7no>RE>*7aEH?h<-}cWMKm^as1}x_|!^Tepm3Yo^-o#;V*5kR&&C_&-0DHKt44 zo?LzFVIF=a7=^H&mAr_MN01@OG~70;rp%9)!>Mu#nhEm+qn`NzF8 zj%Zk-Myymti(kspk>4c(pGGEl%Yco1@GCBgAr6QnPuaK zt-SHLk0N(kEy1}wlbuW1?nHap>{YBwX9}$^{pFu+k3u@9T`SLL}#6{F@{(~>Pzb!`l?5B-ZiVpvKpaR zVzZ3sBVr@9${ROha+h=kx*Z;I|NByF4)fDhZ8m#L9z4rX-g-ctMrDKyhNxf# zKD(*I*_|3$Rl#feOMPXCfPwmPKk{{Z`+u!m28lr3;qxvU6<4c6w?HY7e28rMzW5E- zCpvIuC^u{;TeB5&w+7nlfpHjX5$_4Ah_Pr)!NC*&tr$(j4X;$A5vNp8Qaj zEnQHJrTn8A9r!iTU(q{^febkXC@Q3LFGMI{ir!LI6-%wsf z`;(@i5xBZj@cxY@HqTQAgq`ab{`VIb)S0Bk7nN8UxiTucY+tOYY5|O-*vYzaOsUvT zKsp6+x*OMNw63Eoa@TZM^TlPC<;h-4yAa0e4CR5JFP*J@35B zIqkG_a0^EWJ`Qs3kOr@x?y~$*2Q&8g!&!O78fwF}wCBw%A!-u-lnA*+oI|WhoVlt@ zz+^cf5K?4ci%2X2!AD~BBM#w6rVV~y^J*H+-fs<;A8B~VT7?a8j45$?Xf!aL|B^z5FVi!9u=x z^7py$_g7T{B6m(BaHrb>q?JFp(QkeCNdyeeR4c(wuk{@A^apU$m8;1o%j7Cc%15gK z<0PpBTfwQMfFd!`YOP?683fYA7_2eG(pP$O5wQkqTrx;`HE;`$brzGQD6`SjgFr+G zF(NTy8yWtljmh;Sb?L1&EqB{%jBDB@lTi>gG^}#BtnZtrbF!j9;DyVZ>^H2ud3BqM z%LZAs-;L5|_oegsvm9Hg5(Yj+=3@+)JcFrj+ERpw)`rpA2(-5VmS7;R-A=Z8g4il> z;!uRrNJ@dastbzJgO-ZU?Rjquu;&M z+?|3|6QwT1GiJm5zN*Kt#cK%AVnEZ~YAC{Ngi%m;7&=BJHlY(;0wN=9_WS zmwv$czqyiZ=@P=6sBg058V#n|jRn7h2>;`Kkj~X(30;vUBi8BTY zC6-`p4xJ9s8ho<>c@C`r>TtD;;=)@gf4`Rg!jGhSh`wts{ktO7p1d*$pooOZeRpWp zc?d`~74%b{GpUNc2PCA)+9y|v%D?~)*pUWIW*EpE&w@rJ8yn-?UtPi1zwt9(^5UZ_ z&vH+<0CUgtPGta6u*R}^^H$#bfv;djhiB#dcR)4q{6t6|1F0d!tlAaO{_lh2Ix{7! zmTJUSZ>XZ$1Gk^*A5&!-?chnBeE?F)gY;LIYJ{uKeiHJmMxJG?Z3niNiu%YQHDRRR zC==#X_X$E7BZuUczh4%3?|P5sHf0LTY{T?tKm@u0+m6Ie4_hh`NuG_7)cDin7}J;| z%_k~Q#7ZxI4Qj)OKK?D%tr^GVwLvbo zpWilsH5RZS7UOcT4r7zRhPd>w##BNRGFVO00CGT$zt5STb;cO1q=%SPo@yjVsAD9< zn5_C-hmawXr{^WZ;c${;txZvJo2uZ9q=)pI_EjsJ_Yq&D{d+G$Kv3~mlh)5&DeR@L z!@~UH5tbCA5v|z9M(9DOCq5C->pqsE@vIqrr*U1Vp9hpoJ(~G z)}}rV)k7?ZnR%#KRP3ObYX%05f^9aq`KHZ$>crD3vDf}p-cvDGtMN|b0i<4jmg{a< z#Yta06Sr_|r`6vWLyU@_+>W(XR4h$%`g!p-%}R|Q5m4K})SHRYT6NUvcDu-MPOF3wBWw%GmQXTe(?q|$`hFi`<+(^T z`%QAk&8+Xc0~rEAFjHl9y(^z1#=%y{h(Ti{#{O&4s;|)#eJg!PRPeq!j{X_R3Mo?prsKkK>0aDA7moW=5 zc+A)$PW-|dy!;i%u-`s=S7Yr@U#~p@cNznrC`N=2o^UGD<85k-NAV%hALae=nmgWc#Lty~d*s>o3i0qq~D zrtg8BL^DDN)a!MAbj}r=@WF3mM;l4IuwAls>m=Pqni$SGR`ir#jt5?`xQQ5`(3TV2YB^>rnYmlp*j`=Xk~4 z7c%90^ju*7cf1C-d>Lq}#!Ne(N)Mp2L@`!^>3ssioFmmrDLADe#W-YYitOrdkQWAsV(SENf5Q{`$Cn=s-GZ6f z8momS&Ui;vFmABI4}xU7xbgkfd%%`qW@VQdV=;HO=??F$p7tK{CKDUB^WjR)${MBY z3TtoM!tfyuH8HLC&T_(8#IpV!2?d%Qv?~q~TJ051oR`PfQhmO3D`0OE|)!KT@PN3@*TG;F>n< zHw?Le@z8AWyKN(UYWp~n&#e@VK42Cu;4A-g7O(rKzvJ+G9YTzL=dI2jh&zP==+CkD zf8|zks=YLkqM)l0*$89)kr3QS8l#_JIiZ8 z8R=ONXSktP=egJRIPu_||2LG)x+;<1PIP$X^&Q4-Y8t8l9<*=F`k{ArrBq@DpM!vS zCEMb`N1`Rw?;D2}B{3)}Rpfng8_{=DUp^9pt`P#)u3Ae=g{3uR$vfUfY{nFPr~)i% z;=of9Ndd*#8>-oU(!f3lRg5OZU#!W=x(!l^VBhOXu^s#*^d=LDs3*u+1``uZx0THT zU7h3+Uir2$te2#KJbMH<3!SZ9{`-@s^W~G?xd)dE&t(Gc@dhANeE;8^cPVH7_(=l@0i>ibbxH-}ESIz! zJo)M#Pg&68fkVkHzI;0HlX1tiEW}!=dK{*eUV~OK4rcOXw+jWaWZO#Yrd}#(?v+TD zUyqe00s*w15x1{Ly+`t#KxCMVy{Vy+h(1Nxq0ucc(&>{DDow??I*BuPum4|VRt|NC z9f6s7y*R+k^1Q0oIufZXq+lTR6dRFIA?rp84~Nz3yts^M zj(g(^IqdL55)Wbz4+iY<2B1G!K6c_a@jfG084L(PuqM#!pk}$?lAAeZszuXglt@w`j6!B$ng-t;SH8Q=SMGfSs{L$)s{UUp zS)$p;Z)UbTJEGbqxUOQ>w-Sj-61j#zlHEeU2gKQwX;{BPcunyInH z1Rh_@IkP*>bumqjATtKvT0?tVmrs56JAC%jZzV=yj;FK-VvjaoAw-OET=IwO`N>(A zl8rUyME;eCC!?lYdCv>l2u1B`NWk)WkC__{kklk2vf9vsRs5JX95Mk1BimQ7{9wwB?X z87Knj?C9ih$3-w6_wr-Hc|{unKUX%NUYC*@=wR z4AxY6m;N4q@N@Px))_~PzI}?=%#bx4W^R=3kKU&klb&JHKL{9GBF^_s6vitx6>2sj z#YwzWc5m#_2B7cdedY__p`7+ODp#_y z+SUl69eLgn6Fm6A8NCY{xam6N4vLug`4QAntww)-WH*w+1En#1b47<=tP@ODC*o0w z^jmNP-;)_m(jwCg1os;xcu$f_E%QT<<{I{zt=DY#;o1oA7Te;#^ zzJKQLa3e!=6kU^ZX&JTSyy=+{9j{Sbmt%(VBsMuKus4&sn~j+O(CtC1m-WQ*lHW}7 z!t<1;{BoL)-B4od_5ZcFqzGsTJa~!Yh*2f_6ls@{Cg1=5Z`pU#7V>(Y>T!Z5v5VQq zHOu+C+(5$3We_rFhrk)T`0_LXY)^5wm1OICDaF_I zn4X6ai2-6zq>RLFNLwIVCvII$*l-=j)ew{zlp~E}u|$g4W3-XNO?GhN+CRV)02QhL zX~ohc-42`OTs0nf<#`i~+d7Tmj9+gN-t@cesa7J!9aiDFo`B9UhN|Cf;&e#BH6R<7 zqy{Y(1LQ`@bH^iUH4d{5)qelH*%x{WWM^1W(%vgdjMd!zwSRg85^j8F0P z@12JX4JFl=0d+?yB%>a>yz)s3g@c z5WNyR1yZI7o*H~)G@KBw{TaUOB%x5idExQ-Fg8Snnl&?%8NOJqt5R5@;E_G0{7HbV< z^ehy^lT9O4IPT~LsKU?!PCflB+O0O$+CR(5+MTdR8h{W3f^gy|I3LK>y zWsDHpj>jG}&7&S+>0X&3Q$t9tBK^kHK}IV|tZG0tCa)sKlqNH9{S^!N&5xJR8h2m< z+OOzS5EmGqv>f;Q7DZN1?==|_L+&g$OxO93OUDuCh}DYJU;NVekB~cxtd_X{vEmU_ zE0`z*4+oA3FFs(5P!u2$m(`e9KhBC1Pi8^GP^Q8F)MQ9I;<_H=HAMxazrWdevh|yp z7!hm<*+fZwO@XW_uu}}Q3r#m*i%9gO`u$!^%~UiM%h;?zSn(UmHJ4zr)c?S%axW`9 z)YS=MsMk^qfK{Y_>38lw53!?Z4VjY?b55{spnKB1vKt8Hs z0gVPLZ&=4qetPa;Ozw%;BMpExmKY-^oq8r%n{MO)sUwUjCwbjdJXV$Rx(v6VhN?^T z#Ru(Rc+~dB6`cN@Yn1Q4TU~GdAT+!poPDXCiissvGd|41xu=R{n zSHbsh_1G*Y`Wc$BYNV@%SN*Qb6Mj6+b)6d6okLh61}%HM;fOkWH$6TmCU~;pCey$E zIp#ZOQX8!kiV~SBldM)NAk#jn<91SzFfH(sik~p|2?tY)OSnCrJNF}+7S;mE{3@5JYV1{Z0lMyB-IQpeeV!!(ygzxl{h(&FPcVAgB z-pLTEKJ_smM%mO+{^y2*^VTU>ju)w7O*NIts+nQNmXZBO1Mfb3B(=mgDZu9H8QV_& z9JOCxMy=5#dXIz@7&S#CZ!2~pkWG}>i4wQ9i`iOWw-;pVI@C7w$T#$GTLamYAU%h6 zJTl$GbdsD|==6{x61_sZM9UO>)u@OaA}`zYF8T)A8%M1Jgb+Lr*Nn$ChMA6$%z83w z$!m@*FGxi`v%Ql3p1q$9Naf09X?g3#9a^$1lgfPO&jYp0)$-=V$yfNI<8@Uq9Y$TFe4 zb^%r_zFVYW7m|=<5(p*jk#A=Mr#?wq_5Z9WSWgieyzS}kGQ zJ@y}x@~sNaS+1UTeEga=rk)yh7yee23s9@*sG4;bQU%Ux=9U43uW94MWW7(%z? z6^9M+wB?3gw}+9CN?4_2^28HpUbKR|)8W=50{&{2x zqv9FLg=Ur`K)nuWhw#T|NzjO?J5Ij7&9Aozwq}STXm^UY9{)=2_Rcq>``nA@dx%ve zuk+rkr`X)h5o=~;1q5tKZFvi^N%LV)ETs&@TaBdh;ci!nTBGYdHYgG!Sj&Mn=kSoW z0V1=fONtq^wwL_RX+KHN?16!RJ<0&6MvS#=-#*PxfB6S!)`?Z>udi=kmck(;9iH{b zNW3W{tQ90Ph&ZB6EH)cW(w(c4QmST#li%N?$3)5*)S{T=8JN7rN!Lws?zVu-^Yq?M zn~!|(HQZ(mE01_E&4)i3-3BVm@J9}A*0PFA3nmtS)#2PQp`bn8=3g@yBRv~O6uAmbbU$<_9C_wkd`7ngW2kF z;~vwBP(4g561R0>s*8+w(3Zz`W0KoyCvi?91GiseMBEU$l$5_a6~FEhv{p}KBwM1s z#el^7c;Z?&vBcRDzdf0+HuPVuIeIaHqJATc_a@# z=5d@=Dx=SPHNh;ztEXly*R~y>xv7oR>Zls%cOF(j%N=4MRc}U7gO)<4o4i5M3?E`5 zaw(Lhr-*_L!jYPj2O;9-L{nl6Fg(IHzxOMqCa0=k;O=e&=4MIuC#7MjEB9}N*bN+Yzk+3V%jsO&Ah&f?Dn^)3ZK3-azv#n&ivK6STa%l3+qR4kU(>?W z8dzf}$0s@Z8IR=6LqdJ1DKSUhll*>9BXrYHw#^!!S=Hr+$%t_fXH&R^o%67&6q8DC+#@QLaB8A~ z#}IKYbw7?#DK&6UE9_5Mm4wTzkBOK@os}y$bN1Q4n>9xF9PCjBpznfx^SkFjbXB1b zgBp>bLY)rBK0;wjjj*bL$rUXlMh(&p)Bt>vjy6&;>EbLp$qvMsBwdxtPQ^Grbxnuc zd`^~If>-Ke8K3&_Yx()K=cn5&nHb8wmQs88^Uy*Fro_~9&fXljU?WH^ABce%5v=LU zktBVU5E`^{XJC_B`}b-@_QSA~Fe@4Pyo3jX$S#&d2ea zt_4?5X+soW6ges&aiTG!IxIGkfBt? z7q@TcxHr6jMThLimu~LDXoK#;67$HX;uao^FAJQpZ0TA)bA1oVYSrzNicw`!u~&4Y zGDd?&i^L3x;EYgugK7pUSZnEok}@hm4ec0l(Qu@%(>GwV?TM&Dtnu9+{Bl>PwcBHl zF#vt$->Ox&a?WqA#0(G3VPF}f!~!02w+;_EBG9=p!$}iyCN=r!mE^TLlip%%9gQKW z!az#<{gFGTnyT(rj|Wb@wTm?B7z}=Ui`T#6*&KZ6{`|*HZPe8uhcas+|L+l)y&iz} zN-R*C247lHf>?H?+>?xeP_x1(9x=?P9zMc-mXxTEg9x*+Lg@B*_T3F%dDIy7ZkJwE zjL{?|Hq@ZJ?RQLm<((9#ewNy*o2hjz+5)avLj&N|9okGmbVoa6rWA2@Mi$$HIG%x6ep(FplNo<)qdqUbg`ZB5CF zsgxzsKyvqIugD5S=(G*(P9RE7*)tS9L$6m56-poI`AEx03Q@cfdIE=pIwRf?nX4|X zA;3_Bv(CGmO&d0)66xK38!&@C#{g7pk28LBA=@@ilVt`p3{rVwfo{Qb9@N1#Bkil} zN%%r6r7Br7$gJOrGqZkV&MoeuOp`?5DnM{5C-T2c#gt_eJxOKOX z_Y_lF@}5Ps22w+)gVa$|$GTx`mZP!2jo(bX`iFGR{V1*TPo&&(HIn5REQ9Vt-5xK` zmhjev%czGgYLwh6&M3`>qvlGQV;-q##k-)Y)7XK!Osd-$-yZqST1Dy&L>xLj!P{Q{ zTy%7l&#q}h(;*&0-GLU^gYS>q>o8*3CAXF>UBh=)b-+la>oEIEQZh7pN7=C{;*a2C zMyulq7z(en%ZzR%_)~-u0eUgAP~nhL5}&{-V$uT>akY$1YbN;F&n`$c8GA-L0N8`d zfo&D;Kl7&-LM@SZW_&TBRL7F6%`p#;u)dDl+5n)O3}^!|O}@Nh9XC!-aMP`pS0B6| z_sx=Wt^oLm%Vow(9gLqOH5Nfct0M+NtIM68FIfwM-Zua*Phw5b4nzX~9rua4~O> z{}(j{J}=lBSuQu9%Ak}X64lTNCDA6q6EPO=4Tp&1 zir7VWxGzEk=Q#6c=kb!4J!_BR1ndz8U{LsT^Q~Nb$x6)7FoBAnPx3B9C-SHRI~;gV zL+9rs*y$SDFi0auwM0}o`hYQh)9&%;0~T}eNKWi@lW4aQ2o^0BvJ|E6%S!jF{Wi2c z*GvV}+EfQF$}^t)P%bG;&hHwemIOyl1k+CY>5$j4%MQV>zY3dymz>{k?eUUBn`ttc zSq7*7AMID5C1%iUwc6RW17ZTc7ddd)@q;HX9Fq}2y_0b-!$1CN%Bmy9mwaI3^vP&1aSCe)ik ztyV*#XJojHiNFEZ_^jpFkR8iD+aD zlnOp|Jd#BRAo&o)F6tcDw}h>&$g(_n0Y(Ki7A*unRVQ>T)(Evc(wkPS$!T?|KE)Nza;h$ z0}v5oIqR(7GqrV!eCYy$pNS|(5u_V1%%<$ad>vl`cKj0cJVqZmRKrAI{ed-b)J6EQoelc6eq8l z=H_Xeq_MM%%#c!4#-(5*AR18B8G$G5{Qc^>2}FBsa3ht}$t!qfAf zdd=YsG#WyqZm_0DbF7CO>q0H0eucX_wK=mRedqd41e;YJS;>75-;eX6aswkq7%m_V{OQ#K3hpIphc1w@AzQlzp3T;d(w5ciz z%TP~5lxR|sNnfFdxn;2FDM_myApHg-5(jnu{Yt+`Bu*FHErWMHxXG&yAK`l|+kAU% zkITlpY>!n%qs!9%Rl`8zbil{cep!-Mk@I9=7fIkDw#n0mM!A2jPCY=c^hEL0>q4Fj z^<1cDFq8|8W{-Sf2eMc-45@g3l}eLVT75>7E=MAQg%=h!!ttca0l+}t8AqY#9jopl~ZJ?$}f$ejOekxc(?Zva$NoA1q=w{!6y zu0tC2G_d+)4+FFV57}#)y$?_}{iH_hS#%_ou3DR?p7D92eD;DQ?Z4m=liE!o0S!sU zT8&TXr;wUnAYgLKuro9aOhhGfLa){3>RUE4exLoplvtM&w*v;q@YrjdcrIVhPLG7&~qSe{u9X*9V{Ugy4Uh<%K~`I5qy zD8fi4Sm&@dkmV7VS+bg^Fly0^kQ#+pAF)@o(Um1c2qxls(`YeGB6Pv9C_|bPCn8#=A|VNq{vVWX5iog< zSceZV?bFJyChf|$mmQ24I;E!<16@_hs1yc#^el;nW)!AP(l+Q%^biBoYW(v2%PES2 zJkPkpB$K}(c6$R5W5im^1;4wBb+>KDEf`A^N`1uxu+Z!A#QOy>hVF`-W-Z57syJRk za+Qlj@l&fv6eP=%dXN>gR`s1LJ)#csfN_pcf)deSSYiwZ)EyhA1&dGy_|EsvVbOgb z0bAPG?TWdsO&pJq8G^Bx>FxM6S0c7XR0F=`kg+TYQ-_$TbLyB%Ec>?F z3=%7Xp|1&GOhuuMh9gFD&<-I8)NB0mylZ&ss;#)mA@XeySN4dvbwR;XB3g&_H=~_N zq~^c}4$SK;4UV$s@MVsP8D-%K1WI*8b%dVKDkHrzQW}HT3||B~p`;MS#7G&y;85`x zYfv8uQ5g}#($X^RLZwq$c{Zf%#mbeNxc=%D-1hH@F z^i?h^1cedfdF*{aw;0-68VqNSo~B;$)|#~MjfSWJYa%fy-dnP!A~s?Qn_9 zOH3gcGC=XY#AZ8c%yH3#M}xzSj&k;oE?}>rpD}vhr%`NaBQZm3N}e@fY8(FY9}u^# z#^f0uB@3P>jVyv5Xc-Wf437fZOS*ok&?ZBC6(h*{dUn-%V-&nkm5o$^M`IvV!6>na z6-?E`uAD5Z8is&Hx%(2!!6S}x8af@J+jI1K4lfY8sT8_YU$OF1_nZciM$M*HUdCgc zkk=hq9;h{X3=aj=Lg*#o%;*ECA!k6*Yw3fyYT2GLo9aFYzu)b zZ*cvJ?QD7fX^cMQRdgo`LaVF_roc)av*AX%SDZO$-JsF)@UbSY4O4|p#;{1L%4IOL zf}p`tlqJC!R1Lli_^1Q{r>SzWD(|v=7*WIJ#1mftfMR} znMTC)$TLR_2Ai3*mkt4GyQJYMN)&?>)gIUiRGNMwhS*YGw%0;V-msNhHDaux)QmN! zpTzKrb<_@eFtND=)9Yb2ucmv`*_5k)4>gP6(Vp<+#md7*4Zd9hj#wz_3=}36-{g{X z1q#qAdM#*@pVs8&Da@B-K3v0;5g#p?L`+m9I*KZ=FTIGh0h8HO4J-y@ zrP3CNXqh_cC;<&ZT403(R5^a1g}nTht>Ef74AFF$zTjl!vLE8=OCWgs^m-JJlbdWzo~jL_sq*0g>l|nx z6#Y`?s@O%f>WQaSt$S*xr6Gc*;vVM&+YoB?h{aIS!#hVWD)pwLX41ruHIEe~uUV`F zj7zk-5C!pRE2bW-IIMH{o?>tqNCi+fhe$f#XwVW|O$pIsCLEeO!gR^o@3EM3HmqZ9 zaO9$BUeLYbyO`_G!VWD$5n^XMp*@kP)LDjz(Cc>im%AM`^*(ykPiqnc!Zlkx+Bh_AMs0xXF_rb>4QWuq7))S6s%&{s%{{|C`iqu6}Ahht8GRMsdk`S23|T+=e8vyynFK`WnLo} zkj4lxv;XqRmh|u#V<>wCvFc^mje#*^InHm24MG12kzDepugH*df82AQ6Mrj2)p)E^hSF;>`)6^ zl+G4M!+`0zjvG%@+N4Dn6=Q{H3^69= zpLm185HzH$LR(NuFb17cBoicq=nX2GsLTZjsb8Sx6$jqhl*#h}#Q_Svh%u?EP@I7n z3EmK03NmUp{XNE#+(k6AP;(GGp$EpNTKlAEl$vj9-63U3ECX7SDrnMljkY?qY$EpqGXhk zf4SVYLG{CJ@1@s~WPblpwbDu^Y-fcHusQ$dX{iWIky9_?}ENzGx- zz1P8)6oUTc=QA< zz-0KCDjmJT_UJL1QiUB!{D59mrbB^|j8Y?QEwExJV+z-n8WO~qWHYqX~ zKbKy81IO~Tzw(UvbCBlNZfgKklL2UVI$U$ztx(VBv}I1(`NGoSg2VQYO#ftvrP(l& zMPf`6+P)%>47+C9+xx(X%YoEQSYyPB!B|uAbrRi11TteV4#3i1L{U?TOByIV2RGo| z2kynYa?9qPr&aba3Zt1};cz2$zikVYo}uP&#a^qj@6ILJA6HGneqBS6Qh&Hgh_tOp zZlNZLoTF7f-8Is2sFJ&!zPuHXLe>2)yVj;E)>!?XIVh_jroK_#htIh&X^ww})0R)4b*Y}%rw1_EhfrrwFwgpruqg;`17R8Vl1 zCB2e2?7f6H?7J5ey@E|WPgw+pvW!t^N?tLRRC7D{lta?b(34 zu>nZY_uJNPWZk+g7?;iA!l%X*foW;-A1Bw@etnZk-^NTpqT2OPO%hXi_1+?Q8m>2J znN_t1K~%AZl#{OlJ65acdBs`@8ga%FL&RndG^Q2Xb*MQ@WIVOE0PSDAyIwRxPKMKJgf= z1&w6PDnYHzKLvGCTFu$iu~`x=0x^kq5sfA>p`iFEbbV3|?r0)6D@s|#6jZf}`=l`< z3OO07N+~JguRet+gC@;pk5pnoX}i~@JCmXn#Z%FyRT1?}TEU9J?@&-1QOGmatX{{| z)HEX_!>EScFc7$#GXYcM56g-bYnhtpVn>Fn=c>Px2K)Lj#7EEaP#3Um2BIyqO>@z$ z-jr73zPmP4l#<-W0dp&HZL2rS%#Z$SOYI*(T0s?Ny4LE<=L%qoYQ>v%KvG!*)hccP z%<7Iy<+=|(7y8t>nb+Me!KANX+y7~1U6AUrYWN_b1|LI*X)&J*866sBxXrQ zC(LGw&pIZl4ANl2QjIP<_x(FKATz%Y>i5zy58l@Q{2d0H?%)yZs_%CH^X@2&VGv`` z+2;szW{dQ{|4(6-H?WhCN8cM6JTdEeW?kZ49Do15*gBZJ%0tfbZ2Ir(JA^=$7GTaj z+Z9wHQ^f9SatoBgt+#Ey{rB%C*li6!zYc!wy3HVh4p!&t@1#`Hq5F@pXi+TewS$-}Jo3>twXM2&;_VEQXy-{nhRGH{$z5J(66ZIPrJEJo%G#$*=~!Kc5P4AxYY zYAx-z?1re-3M45Y7H7GgR7V7}4yOPA{-;(akh&t%535ma$tzLJ{&&orziNuf4Z7Y) z7;HHPLZC6N{b^38r89)oRmso(uECX)8M*ChQum)rRg?^j!=TB^>}`V1JTC*IQ0X#6 z9ef9($UtCJFc_Ss*oPQTYFb+LVX#o!egj#1J8BYpQc11N(xp~h35co0Vv|Nfq_1Hp z1EZO4RqA7ysj_mi!Rolr9PdE;+ZS)S@rJeRzSzxU0DbX@_3O7mCUf@xA`nAH<~p4A zwRiKtBkywu-Sm0xSYYJtSUybnGkOc$Yzge2$~yQ z-Jw~;*x50O8LuV?*t+nUlNa*(uP?!l4W;^q*|3(|)~-))*-iKVu$vlyJ_Wce3pQ_^ z1Xr8$cOh{A_Fk+U6n;in^9Ot{fW|~o)d>A!*Z!(I*el2Xuf6jQm*l9<{_j+E_k}8H6j2VFw0Uow z)ZG=nKdO3W?%fq&KHIFMd!A>cy)!*M)!kL6PI%9IKnW6e%&N=qz*jf%ecLx-rT>21 z&7vh8HioLqwetC^L6%{6_Hk!dLL>$VN4c0=l%U-Dy$Qn&=$Lq(rGsVQcAD|q9(x6;W1%RXvgJb%JN}TwWMYvz+;WxyE z`Z2R>%m81bC z9$TCXKDb*YB#FZ>SGcyrx5I_`p2v1PiPDtDMeam}Hx63}E)iCA3mOei*DZ{ocswKt zd-qM#?RIH2nuD0hK_&;K0;uLsr`=_4z7zekLHAz-u$C1~Ly{;8-z3!@yv3(FM5h(h z>K5lxz~Wtrm=sh%uXza=%_^2F5oPFO!|p11w%^!d3?RfBhK%6@@LpA)_RZqRQ6TvK zf)pC8~rGHkBGU{#31DTl9(@r=J(&37-@NV=+q65-n0R`8_P z*7^3kGY(z92V1zfe}{V-q#BPK{LX>y&!VC1H|d8~uy+JRVT>7w44t9cHGdn1-TwVk zki|4_FB!T;p&ZKVU~wq~N}^HcF;-BCLp4L8lrI7Ukgh9SSA|MU<#8R4M~9Z<1spQ! zSynIUl(B7?A6OuWVs5@*zSRy2;2@QQQUO$IeXiA_)#;#Qlo-_m2#eM$R!@|mdjZlK zw5(>y`#Ty|T`w*VedY&j{ooqu@$a{l>CYe|;X0t$}==N-lg>3EHwUbUX* zo#*+~o3`SZ3-O0VSG@b?rFU+}L2{nBmBcT@E{>M30tGme{UE zPzU_I{WHQrF}t$C_V=AY=b}{{^4FIzcGfM>QZYQ^P^pjHkRdDKW7iDk%QQ)1=?8^i$Y+CI?r692YLFa^QGkO|0aTk{ zr`x5pB|2U3X{_FZkw&Pz6VZ`e9#rNjezpPm(J?d=i`Bu@&-RT`e&tA#qxV5N6YA-` z0&HJ9m)#og{`^L+*jnQ`Pu{`tclWqX2y{u}D0fe?>z~hneGOJV`x5H+*oN=zKOvos zu=(*Cdv~XN?VDqK>HHCXa?MI+_N@s4y0iw}c@pzW-uwC~e9_x)et0jSJ>7hizkd5# zKKGMRN~d}38ExMF;@ymnPb~nr9Z0P4B+C-M{GXG&=Dq9Clj9&RC^la*lGW%I4kwT< ztMi#3uIK0vl)T}kC1$pZRtHvai=S8J0VyTFc0A638c}@r$74LZ_-)MGSn}z`8UlWn~^N(1J<{Wv`^& zYX91M_xqNEQUO#_hH@6^*$johz49zxTEU z0WYGtuP0Jwca8JDkEeX-Ta#?svJUhF)Kk*Quu?>oSl#5-oejw4fF+~Ody+=RdGBAr zyFPzBEE@v_ANp#GwaYYbcv%lyv~Rz`hyy+1x#^~Hp7Z9_NPRV0IlN0y-5I|4#n#G|v6_kUmm@BQL9@BXWK9{QM@amFJ_#9inZ+tCKIX$`$gj>GvT zR^_A#%+0RjnxC~f_1qSvFVKX>BAGTU%Y1Mlqy+qcZCNP_Z|UXvqWiWemhyX+gIWQU zMHy!Cemjr{WvOQ#IweX%rx>q1K8dV8-cuM$X*|YT(nQm>hK3Ea+7eCC{?rtpEP3#Q zZVqK&9jH=>qQDo>JZ_rtN8Ny#Nf3QP|_AxoS7jL2xkcCP>6ks&%UWOzl z0CJosOF)f*jMSJa$N0#1mh+hlrg-KbB)tAl=UKI39y4zbs00QVEqGsse;eon_?2Sk zlw#%;_5`7pTd_6ck&4IQJrCO0MXZHK#UIVP{6k{o9R@eb`Q<4Fmz zu#82q7p$i!%fXyI2<0GE0O4j?8@kmj&Jp>TAFBbRf^7u`Tcs^Y&0<&Wqg@=1OTom3 z61|q7R&}XO^sqKOT;+Z6*&_Y67jWHx5YtuOtQwC8)tG1Hvws=iG9i*I9&bUVf?b1n79 z-k)TTXZ}A+)R>?%*gdz3KYv?~@4dH6lFUR*z{2O?JS6p$_kDJZfB(s`AddBGEr0#I z8O%&ck_-j?@e!E`Sj|*BQf|4xgj`H_LK*Q$gGM8vwn9_bmAwBOlYIZ8-Mr^7_VLhj z_F{VZ?YJe2cGP%3lmg0<*Zk!k{?B6@9Cd6Dx+O`P;>r-r^We2`6p`y?bcM?!FVZJxQQ(iv7KYrn8)a0RLjWEG`Q`0yz!=oR2Dqs7`dA#8* zkHMKPNd)~v@ zxNaHU-TUaYb5bSLmT9hUZQ$I$JDd-G>uA*HIO?Qbz$_SrD=p3h zOVI_R0Ne94kD8@bn_+$$^0s1rD#2%S9Q%N+_%si|2IoN?mv!I6I zK$h(9Uk*|WAjn_?j`;MrUR5kY@y0{fLkhkOJB;sn^hArbFT8^0RjX;=I!QJFUqGP0 znX~-#J=71I!IuG^T2RJ{3~6hX%wTroVEv;x;H`J+eeV8wWi zPk(nF@roxtbc#owxgFp3{oQZ?tWih?d$&&TqIaypH`d{$1gA;N9RK?Mx6mBBiYu;u z1I%n~S+uL~CTff51 zH*Tf5e1b-5D3b}E|1Sxxp78SLZlUb8N%Z~?D6F}Px@?fe^q#=JEYNQo?g1O>190Ga zo@~VoNB!B=Z2J0IX7@B$xuV69r|)Lf`YC!Xhqyo?X1xzk;r^tE_oSZ-6p(;PIhf_( zRsj92=0G3D0+Cb&8tn`uk${&mLel85>Ooss^`MH5_fR0Nz0fTwx&_WNvu`)cfAQCh&+X(nXFZL_ zo%38?{5L<~GynE|>dPk381zJwSA29WD@Jpk`HZcYR*4>(!7eU%tvr$e8L@J)ib(2! z#3~#Z1wH8X90>uAtpvQ&G>_WJ-JiRcqLotj4%h8rdZ1E|m_T}J#pAQkt@uFG#K+4` zk|hQ3JBzA_J3REuXoqU{dxe4`0ArHGBgP|HWXrKaIqOiiz$XexL)6#TQ?M45Skwp+ zBkKlFEbv6*yr)7HA234iK#$jW=Leg7{0AFIS2b}M%uJUvPdkkN`20UIy4Lg5uRV*; zZo2?R#?j5?)b=Sl-VdsQRfR=>P-VVQ#D|4udJgD@u12Cz%Eg@8;8^P*8PWXg7vp^7 zo9oe&DN3N2ndJfJ9M9`s`#kne{{vHV7a-{f9>bUtlG0Hc!FF=|nWIeHxSpA>>_m^I zWa9~6yKFVz{LvJTdRPxL?}h@8A%@~CxVZwPg8mNgjFoi~@39uFu@q&fxlnkF5lo@T zT#C765BY8HV)-GT;ZuM45>|~qjQ4!xeCo{xc#B@v)$ z>KU{XHto!~ZfldxJ7H?CBDW5$gwdK|Z4(Y#1;-p}SvA>)p2fD40N8Q@J+OsAD@!pC z(orZM@FXr;4q`x3P0#2B*Xz6s;3wzN%G0Q)L-*p~lY>+Ngqu~b*ZSOYvft;9yi-66 zl-5YshJYX*NkYm{w1z~Zy~TNlL*sgetOnLJx$Lr(xmGX>>)J6g=2l8kbbSjC|LhC7YL ztha%D(+#dsL@jKaeL$2|vTnwEW_^DN%!jW_{TLbBz?dMyyM5eqF%3)Xh%ye=SAEdrc=@CEu_l`j z?bN&r0naLEryPk^SQ7)QfN=pT?h4CXnfCXn;pgSNBTY4<9N&N{Bk!_Vw+uM;|`EXTGT-UkS&_ci|ZV-w7ktH_!jYaAmD z%jZ7!W_ry%JmM=a;F@AD+1LbyH3+dNshfaJLIoO*S`vz!e%qjySN4GKI}f@6mtT|e zjZ4>(jA;~#a(WLh`s>GY;@yts+HDWRs68keI4^XI1Z^G5GGS}Skt819w#Y=u*aJ2& z{kfa4HBU0CxZv8=TzK&w&boga)AqNUeq1nC4lq{dNt7Z>G~z-m#+8=&xgI3VU=1mY z&J>OH1?HOX@W#e{nLBJ9AKJc?hSV8p3ct91IUoH>m*>5(%Wc=yc;}~<^U?1uV|scl z+=vvCOrWk&Dv2c)3gbP_c}lO?G^2Ru=O;LNS(oRZJ0GCPP7oi7QOgj`w_I7GfI7#BkvX*diW%- zddYK`Yk!=5)8D67Z&H>8UQ&#$N_)W}SmRNyhV2RZm{DZaIDUQxoeG7o^SK|7an=K7 zfZp#q%06CVG>g3u1Smbb_V!?1GXOd(0uxmtYxm%{?cuz!Nq(8t`K9qRMUeUk|N7nK zjIC1q^#@ZnZ(9o^nshQ)7v2$4aNQhi7feXYktWcrp)n+hV6v1>D~Y&)2vc?fg~9iN z<%g2c?KoN}-i5eDTPyb3kj+_;!6C#rMw%l-7Qm7M_%F-Yc#}r6Mt9y1sIMR*y%hVp z2^1Q<1s6KdxH1%ygd!3LNrKk_spYhU%z;zXYb9cOhLpK+0pCCe?E)X4l?-h zH6wIq8r18(J_UCl1Q|`{ipQXeyY>>1y65s8lRWLb8e~msD+8*p%pJ@I%*Be;V;p+STGp*u zO|zchGsX06(_C`xb~>fteCYU7&q4}Wj0q6&!h+W*Z9)0aDh`Dy43cWdL3_*md`i1e z;h5oxD+1QCY`n?D*a!!+9Fz*6QkIhwlT40{GBrELaJVlC|Mb@FbCb~#c{!h_1f z0x=n(stiHqERqSLHIfKvGeIdGh#R!Si|6nS6ZE<*cFxRUXI+@VBO_#4f{b~LTfw(3 zUd{zq@8k{7+{=%y97AeJFyC8qy!e?FvKVo5{*&{Zd=HIBMz)# z!|Ko|)D$JDQlVjp_X8458|lNe3Ok#MoqS?(q!+lmbxkH@BnH1G?s8th4mf~!QBnda~&bWDt z;>2ST$|6SF#UtVdF;trbv%dc@!;bE3SVYc%P8^4?v2j-hvW%;)x(Oay>z6uv@e!sW zxH&_+>$tzoI9_L5V-$(DAq&%Npki>}?44%K>Pa5=M-SuN$DPB;ryS1eaYJUdLT`%J zg>5!}@mDDWX0s}C$kHx#;|$c-TzYx1~r@6Wrh`Uo=^YN5~Pg6k0t*>pyG zOA3j?y3kYLu+14DSS23dz_A(#Wvw9y-yq z+auhxxz35l&4zrp4bV656q4w`+2DdwsuGleN6#Z($IBA%!reAlL~@MDkb1^1F5SW2 zy{lQ#O#0=t-UsET(R8(=Sy)aY z#g6l_Q&Y@bcrr7WtfuYibc;ZfWD!uFWs<|bK>p!-Dm;&f)bu6`yuuzZp@Q4t5$l<= zP1?2|h(Y7?c0!nk2G*}#8Q$|g)UOrfQnEh2fMqP9jy>W zm(rn(kI7&a02>fYNfB%r&Z)hl;Vom)QV=cBE0~$f8EO8U-m-77{$Ufm`~5HDhRZ&} zr#}8_?smj-?4Bu{BZ~PgmF%l+-R1jSSPRJS>f0e`T z1C{1D<5@l4;pC%w&~;FHoB>}bYzKS~d?7d+?dVd6_j8ZxX($xV3f2Vj3P{+o&4Z08 z!qRfc`V};qQ%ucWi;!ZyrR$*U6s_DdTME~k2?|{!Aq$her%`!GqauP%Gw=}x>@xOF z)dI0esqiKZT&zCC51a*@54{4d2kVhs;d_ED!h7Nbx+SL{)n%fYlb4oS0$a8heDi{7 zWO5Q?B9l`u!R}j2Ig?Quk2QuvZD_P2A}Fo#Mi?zizVOM{@tzO9nsx4cWCVZrVitgL_e2aRN3OWxH`q8$bmdNi*@R9#c?wybUy9I8dd7BBuVl4>?vB3&R9R z;Wn4i^FmgEu!bkGFWInRZJe(M9|EwF$QXC<>4Ay};?VVzU`hlXe8aGTpu{t88f>1< zz<69CVZwRvg+-ShWn94PcLcn`^#b}&YtQ7E0W?~Lon-5dDSYuAcHj1LKK8w*aNNhA z#*41}SB`r01}?eay}aZv9)p{i2B-S~B@qS2vTFHqE^FPw*6wZ+rTY_9Byi_RE$sfC z?cHh?tfI54HG@eX^%1*RVuI!(F$KCVeDmSu<5K1R!k{)YXb7I2Vyj~1++Z5 z^#e4M6GrMyESo5qK~#!yo~~8Q_5xyXkE?NgdpWw7VoD8Vg6lO9t0@bM*FBVW^m)Y) z=4MmqrT8MW1(yQoDabXd(72w0LgRY^c~FqP0D2PIoE45@9*$V=IQ!T+TwWj;d=uXD zwJ!PGvQY4ZU}p}axaRJ-yiVRzIIn4G2^4WUq7vs@eBvX2%j2GWf9AjYOv;Zxma*F| zrB>JIdPaWL+2pq#&%Bhhdxm|vp(Q@HEf*SDj}>EiDC{cI5DUnGUI3K&(xGg~`zwMq z<_(YtTW1v5(Cf&dLP@cY@ec7Ed(_7N68%b>=8^#r{yw410t+|qa zz49B}=S#2P@jrSC&w2Bs`S3se1-3I6^Thpd9f>4dJ#`&O$%4ih9-Ma6lN#pJU~g+8 zY3NR?T#Khacpu`sp$l*q=nB$P&{HU<0s~Q@@bP_Lf)onZQ%FznIY?JSOQ37XA}Y;! z3pk20XTH0MZr9Q-p5#&N3P9t7;wl863z*1yRCuKD__l*QL8~5rdcPLpa;#I->M56OO?k@~ z>Lkq(idIT7yBclBfdp}yYn}|Rvzh96Q+qY=ETJGOoCKC*96URHoR~&3WwJHlo}ZsvJYN>!|rAwp+vIHEc18>tunM zM>IGEN?1K^z?SHqh7M?}@VUabRbbNX0X+vD3tflg!BQYs_^w73f-WrOKFg!-)8fI$ z&r-BQDlHj>H(#=xPkd`5jqC11al?_cW) zpnml?Nk&t=SVWiDEk{vYbuYY6=yV*&%Uxx*U*N1zFs+q*#KD7m1V#`iR|EGHdWqO`$SLFy^j+;AJW zZl1u@SCJbiyCu)sa4Tolx1!V}WMe%0s~=(08IF6Ob1Y@66MW<>LR}lW z7URGhnD@~XwSnN$0X@ye)q+lQjMGT1#^x>a+`4%Rks5i|GoNd&Hxqnz`Y^7TKZ*uT zd~VTs6N(YAO?;W+iwrGwWPTpo>mjjeMwU1>jO8#7ejZdu1(amVAx2O6X zhQmr@XSzK1&!5T|=TrXpeWXo|!{JhoogJn3i<7b4Ix~66RJ*`gPZzk>7y?DP15!R89n@~j`tsn_Ol){rDHKkNDES7y1s zx0Va`jPsdYhwlK@<4cz}CK!?*0nZuS2muqa-;4K&v(Sk_`!deTyv|;=f zY^u3+uECCW4bhQLpz=ONLuokj@KubAGzMU&gH#UEG+@-E-ZD9{jN^_yge_ZcK-EV3 z908s{*R?yZbKPvpF^6apV*|^p0Chzw7E;1hdI{>T5q8x>$6Gw%eU8%Dv)6Up+{H*V~K(^zOR!IW2q5g ziV~iUapAhWCo>*qN}l(K?d+Y_oOhvuS{-qotk&S`?Sh}R+x&SAt|z4@DK!!Dc9)ax zaX2S0zYKozbCTsTHJk#QmyzCl6l1lNj(D0$Fu{%0{J1yAtg@selqU3*c=YilT0G4W z9jXVNiyfqi=~Y5bS4n^e8wX9z%~K6}u8LAzcNgo ze$a`vjwI2Xa^i7(_nW_p7Qny?PzXjh_|SE0*fKXk=>k=gNPuUH1Pv~PsC^RUP0m^8%b%-TDpxM#(_>tl%$9^c&i`< zcY{#ArAhBLgPjM}5Tqt}6F?x=dJdcPjH!%&{_F_GjbdZU$;Hd^KBPXw#Pj_kMXf6E zqmJOFkje0gUW+aOP-$-NEaA?RGPgAOPCE|VX-7^&^r=4E_=XJOc1LzH!S`M}uz*_b%co>wK_CNFS8i+em zq%_g!>Ia1S^Wn&T#P$Kgh`^ z-wo%=$QpETN^-EVlnzT(CVR?BM^GD0DQpEh4e$HvdDRk>iXC4T)S?e$@w<`jKhTQ_ z{ZMUf*=Yaxz||SH=fFg+s8)dVOO$O%(*H52q|{Tqf$ZpEVxw&h>FZLgS+AOn+I{_#GZ=c z6iK7jR}ikMq43|HuRxPRhI7OWwUZq`tv_3BB{ z@5(bwBI5^pLG_Z@_po2RHy*rT=XL*>#Uk;Zh361Zf8GhW*lkIC$jk^}dTcm!HAh7Z zz`+~$)%j>hxmNUSag$we8M1gIS!5aP_fI+S=f6>g zpU==Bye5=dR)S*YOqhiZ_-FH%TBloD*7bGPF+FfuX{+=+uZ z0>}adaPV0IB7$>8{K{%Y`x->c3%a7QE-yi~_j(@lob zN`efTB{s8+id0EZc-4ds zop6T~VBg}dlu0};cFmFao{#ruu(*6w!;x4!Zs1-#gZ_xp=i008xAs1AK7{i)XF>(1 z-a%n*Li;+(g?ngI(THfYb|4;K6<+}b8CzsDut`OxVDCKMrO};>iX>IoU4ofU zKoh$+SGP`}G~D-|!T)#Gp$_5zAROct07O(3@Sq2t%BTPBd=~E#N^8vYG*3DAp}g%a zFZ;cWaY+s)*~yk&e~Gmhk*E+HGpcb!F2#Dmi{O-{CZp_q;~2$E=r%p9?5zW7)VSZ7CjlJvyFc6k6o886_gVKnk+Ee> zTBTi(SM|<9*5D@>UCz$APh#B;g3oTTKA72_A>z%d%dQWB9f`*b3~%3$1v&3f(bsnb z63bAE=VNcVu%ip1O_an4Qiv~9=}KaZOojc=hY`8BarEott2+8>2k+YiZV+>emIyNN zV)`EY(D(N59bWDF5-lRZc@_Wmea|8U{GyNUtNRhh7w^;lq$Saf@@B!imBpkan$}XC ze=w}U6-Z>#kvLT8yEg-#fk?bjSx0gAcY!Pc;{@j%iB9R19cH_8c(0MvW0Yn)DNYyY z#A97Rah6^O429RsdqvBn%=Kz$EjUxdDjzHaC3sarCiqPw^fnjxq`9DvgHj$-<{Y+h zCHK7N-NG1u9oy?cA_u<$P{H&)`lus0>BNov>=#?mBaOZr=bS?t4KBHI3*Wr(H5`B9 zHoEQTw!3KF4y@bI%tD2rQqhaeVHr%yegJrAf%X^RP<-h?K46^V{g;D5&%78EuiW>( zK3cR^7o3bmQG&``mw`LqH;Lmh`xTxoI&YPd_ddA7gQ~^qfQ8>(Y0orNTW ztRBW|4CI~oTRMz$K|8eroJR<`^@`JFu_!g@qFzx7KRIMbvzH-edKU|bqf#oj}bl3cnM+r9}J1E((B z7X{r84>dC8&PK}igIk74m4APu_zmK$B}oz<@{oJ;vmalFj5Yhi6)$M*DW@|o zJb!{yPoECmV-f)AX9dIJ^rV^4nw?;NuV(GWdCEN2mLE_S1{+}znC!nZ6!Z%1$)M2` zni`BGalwW26uG07D_1SSe!eW6s$wqgD~e$gM$kSeMP4ajt3X*A4NsPaN;~gD@s|=P3s2E4@kN3t6*};UMsQdq)H2O{ zVL?2(Rm_$Sohp{sJ-xOfw~9PT$@2!Dj6|APpHfqGif#>0Lg*Kn;$4a^YFKUYwni!m z?v@)t1@ce?oW$$#DR|GL9&&oTE`NZu?!hMqy#)aD*V@A$b{21Z+vl*h%J0WXp68qw{b3pseNo^;R6%w?(J)N)pL*Lwk;Vi`MWKg`=qUOS|(DE|7O|0wmYEw zJf6{UMYmh$nqQ7`&Gn9Lw>9XtHI0U4{o$6APv~;=FWd4j(eS0GBKGmGiy*JSQj9`)~f(c!jp^8cAhLru|&dH zWl7SY%v9>J-4H0YCXh!cr73y}Z-~L<&|lHpfbB?TTXc6$;nNso?gw2$heL5Y(c1meS_?kJSx9RZ(lP$?>rMXsV@2Li;hT98+`Sy!-|0xc0(BL1ldD-*4kRABU4p+C$!{;w1}r z{M*q_IPo~^XpUxl7L6x~2@s zKb2suqFz&UdmuGM*CuE;!fhAV*?H>(ezJ}_IiaWv&LkL{V2utPj9iG1kQR8G;0j09 z)UbI5*C~;D){mPg4@xV1tIhq+I)Sx^9)fcgQK6Uh!HojAqgVidwU%0z@rRGN7gt?= z1FAWK9|}Ba?J4F`zVY=Y_qtyl=Q|64LD5Cyl>5x^#Aohc(+&H0#tZjR6nRJ`9_UAZ zgA#YnQ>$s(tx4YU`U$@K#WkRop+^+;WrJHQq7Xk$UKlPte?6C8FvVk@ww;&0M#$23 zAmgZ7Y4uMIwT5E*?lpYwV;P&Sty3Sdob#A24}I7Sd3TQC-i8aT{zc;B%UY;UYJT$d zCfELA9V=EhKpgvaujad7+Qq$2)ATx~@8}O@^9E_sMHW!l`Gu>mYVhHA96~m_3YB!} z&gEcB(t3@N2~BHyjF0}y7^mN_hDc{{J}0E9$NCk#?YuF*`0+JhR**ItG{zJvA@~V4 zSTM$oas6eh`I}3ox%6onfAOll6sAMZBpBr}9*WYTQ%~s~zNYAU&-U#NzVp^GwqCRx zWE2pxjRl9iAjKT5p`{|sS0PF;#-db;C{L0KNp8t+-p&E}&_&~o;oL`^1q5)}?QSRs zncQI%Ks6~J`rsg`%&F!;0-kp9}3ik#t*fcXR0;HUp;#j*IalAwPg*^9^*W{*=aC&H9BB~ zWPF0!Sb}rw`N~Ho*g10zZ+;upB@gM&A#*sX8Mg0P!^{59D7Rca9>=CQ|LYBA-==u- z(>nC#&4KYW!+#D%_e4M=k>y8lR)RV19nBGiRw16EMfx})5$UVZ1&iN@8U}GtuPZM4 zNrSSpijn1xZo9{^=ib60r}X&wKQE`8JB)0^v1yZLYLBpHolq1ZBakE>@hf=sU##KB z-&jv=#TdF7x(0V==b`M-kG`gLYU3l+M+N6s@*f{-uy5Y;($@>dv@wZbRp?n@J@j%# z={%<1;Cuf%%GRH+M~zJ2twB%}x7OM6-Wk@vX&kFVroo~ptVTj3X3V9lKiyNs5=ysKlhyt1z& zh9)%NBtfSmyzw>5xaPu*G?s^MJ9(!>B8fM~<+nF^{vsAFVPO)e`NS{_(S_eRXRd?t8z2d)#+BS6{dR5#g}A-NwV7(xct+ zl#)^kc;!N6Yf+^Z<5Y{Lt&6n=js)d3Gy57;h&-Q^-0y`g?r~0=sb4j?@{5+*cnV(V zDosbFlnO*V&9MgWc>QvI{EhWARyXk8(Cs!lR;{SVE)^gHV=L1eOR|4nE$_R zJn3MNJB$K=s3{T4F8txUP~#)mn2hO9e%;{nUv5y|e}rk%LQpv8`n1i8h?n=n zT0HE6y5aDN#(5k1V5*^1x*z=80i$DewtapXKmF=@(&Z_I^4Pf^M;*PKe|pcWc=$P| z0#}gxD=FM={`AjQ@Wro<^U7Cmr8TvlQuRnDNBQn&j$rK6Ygu<{POoJ{FH0cXmvG@# zHJVw7Gd%7-58`K6T*@t*chQ?`@*g+B#A=~5u?NE9)W0Wo910((YA5%Qr1PBe21o7k zX}lMXy=RB>b&KNGkQbCpQO-5!?Gc*m3_i(2P*pGV2gHMSj%LfVX9Xy2@#0V}O|2?t0JCp)Yu^sf|9{&ft>8+n&=Twd)N#A^T4s=7fura>#jep|E!z^2OJ&CE=uadih+FOC~S6KFyAqD0ofQ*73i`9ER z77Iz$(0c%M@CMXKi$ngPg$Jg4!rolal942y)|LjpeDiANZmzNJ>=yTa$zDvB2XfD7 z1u3OaUI52j&og#f&gjYx`FxYwNP}Cxc?>sv4bst+TFugF_gMA7Xqu8{QVy+vSf(&^iveGNZ<`3cKHbUMK+Cu_LY<5n`?|T6*Up+TA_WxVUL?5+X(rTW@=wad!|k? z(&3o@GecG{Nkx;1P!l0jniNeUicAz)9B*3vEk3+d;2lYQf@vq%mL_$Yx)u`esWlRI z{c?oqOD7ratYG`M#+lt(BddqSlqw0a0tuzHqC@;8J7k6vp0|_I%wy(Kl4gU(L>+NL zZ%@J6Gxu=ZWB1{@mP~7EwUo}zarRxl3{_W%Boy;A{N3xG#l25j&kdU&!A$;5YNIJS zY2f?_bGpxaF+%+zqj=xO zw-P!(Q`qe(^g7f|T#nu_MruIURm2u3(BQO?TA{e{=AcZ6aQ}D(Dm?c;e!_!jG#fZ; z4r=zF1IZmq0RVkJ^EuCaG$Uha==nFC`nI4(6kC5b!R&RDs2Vsg#D2l};g=UA5xP@w z!+)$GO(&^MdL&uNO=q%Lz%Mn7*NoFKS2@h&GP7`yu?{xo5Cz>Lql{ z6)46qWs0#0rVRe*TrS)?KS6Txy3m^3dvsc0<`QOa93`tqdTAY)f*ObK;gol7W93tK zGkI2<6_4A^Y46y{s`0-^Ix#Isic5-pU4 zVcoqfYwv0C^)4G8(qZ(_98*dFD~XEr?a``=m3G~(Wy7Plai6!{%*4stAe+MKebiS@ zbL=y=a^Kf&L$VgmrU8as7k1w`LNU`oC7!%ju;%bFp7E52vuEx#bnTUB8Wdf@2x18=!E&zlG zt8%Kb4sju7}`o+a6yDI(MPjySkdyBP* z81R8r$Z1EO#mKyPe7_3M;w52zZwfvxhHjVppK&Vn#zkDW{d3^!6i#U63hy)KOQBU5 zZZj*nWam1PiIj9@jeJ`N$uzi>-u4uweB}0UxCDGrGIH2DS2sdE~V!cox)N&%@203QlsPlDPYO>q37? z@B3DO)*3t4;!%Hie>NO>D9#lqb%$F{cRU3E#HJyd=RNO9{KtR(I0j{hoHHvV4b8Ui zE$7HbENA3|UD!O?-`){$##3A7Ir7nST=V`87_tS zA9}^a_$c$mH)&Z1F2gy+e38-4g}L0Y&yDcqn~!9svyw&wOd|u@RD3%{Cyj+VF?fdx z$|@I3Za_ja5u>DU@i>6gBCTay^seP>|IsL}NEtu8hteLOtQ;p;+@r1bV>Bq`Jc|) z-c}6Q+=5kN(b#z%I1ce(Ez2h-=;S}8Wu`ziFve5(l$ov}ad6f2NbAqz0y zpy*VbVhWwWpFQ)D@xK0UbNTlzcSr@GwZ?nPgC1}iXPSW3hvl|kGW5uwNpM?QTg6UP}^+nbCZ*J8yfdnvmhNj%>WB1V)a(ZwL| zrZaZz+RM!RZOpYiC6-RED2!vqrfll0;U~L};PTx^(9{_P38q&??_*nYC3vr+$v%iA z2hMRj=$wQT;{IN9M%k}=K9)diQlsmcA{Uy!cY5umKq^;XE> zK0!vP2+SG+sZbUcS9ow4WnQHKZQ$<9pxCvF%RfHJ{M9v*BxTJXv^eU?x8ZaL?{u(~ zyx{VHZS?VeIsA|k3UL{oSv>I`sFazxd1l+Q6kShlg}fBzt)XojT(o;5|GDKDX3CYM zi9wYOY&&`&J`%a;(8M#e^-ItyAPB9o0WB(ZY#}HI-XAr%0G!V|4EFL}XJ_|XqugZL)H z!2s_-H(}>bm$UEEm8>{(J4MIu59Er)HbRHB=WInM;H<%Rg)~i&YQ;#z3s}%~&-lhj zD&ZB>Gk)~58~K+DYxJ_C7*8|ywlb#5jLq{C{A%Y)X3Zp7ng){6={9D%g{X>@#ItgB zL8F#a))b;#pJ{gC<sEVWA|>cIxcCtVx!KT5g4V;JDT4Vfj zYCW6Ut1&huQI^s}eO^=S%28?H zpZ@fE(#cVrEyDp`A>vVajjKMrfkO^ChOQ|?nhmIS)&7F8I-2tV38y7w9To)90Piw< zTBAGGfYB+e6;v%@$Chc{^U2j@4}S#3?iLV3sKkJ( zJEm5h!ezG{N}fkxlS?8`UYM9wXIPIi3d+5ftX^Yx?;cET zIxMshqhh)ZR~5h!4xxH5~+c1U|m9~@}QWFqBcHC@uQuTbMr_u#g&e1B*Cb|Fq;$!n!*}%;!vAs zu)Ai-jyaODUPtl-X9Qo=u(=~q3BL6BA|pY85mFIa*KQhc8%8%z2$_(g-R1OCkK)mf zegNLPxcCn~My=mQ?yw2~!sOCA;dQV06CUxim*5#)@VWOM)l|$~Hc9VmC(^yg7_@E3 zUHL_(fIUEfU@<#ve>PZ>1kytj>46AFEpDC z%BwrHf3X|UftO!;ImwZAuFvj;y(U73y~IMI;q*rJ+bYH(!b>vE*gbd9yWw)=mRa0Q z^N2w6oTIo^9Yb+LEbsL)G&iOCdZ>cyI+};wg7$l9A9@&9-*Q;&4h($^buA&?HceAs zMX#{@!A8Z+GnyZ6wKQast2f;Z8hU8A95Oj75`zUq3l5cKBu89{`PCe%sYx3t^S_v3 z@|Y&+y;jial%e}E1tJMfYBa{is9n`z>gPL%Fj2w3ldYPbH2t$Q7 z;&H+z3m>$XVH1{xDIVvbnUb8k3VZV=lyBe@i!;LRk8EM$QOn5gIYF6%j(GcI-eZ3@ z$J|f$VoMcLb$GlpB&Th_q#@217B~YoUR%Wi!h%#{7L3a~PkxVejETlvz6a5o^!VkJ zr>vv!!SwePD@qm2T!iWOV~YIT!;slE*tMgGgnIh$dqEL8ENWwod|93P_BOA*U>+|* zLmX~hliFlPS(ZZ)mc>!A>OS_P5uibzemL&>D?&Rr@hC~T?K8U=xowiFt(2jg!1haD=wRVSO#VRB>xT<+X zj7^9S@t!pAGI!IBAdB{LacHF}=jXWZeU9QWkA6^eLn|eH$J+TTIjH*r{5E2(L+gY~ zF8vkvd+-Z!wK4pV*2ahGF4yhRc-Z0CKR6Q8DPpi{NC7MoCkstFSwI2gu?lKV@SnL1 zb4?3rG=UOqXos!WrPS64Nv($ILb0PuF>OMB0Zqu!w|Zp9Pa=PM3b`-oGjtBgKmmlm z)gnr+ibOO?5_Dv?Ep%wXB{AZ@-&)*HJZK*po|eNzSJiJu{o9=o@P zs$~OAL$_e$l%tWe?v5=iS`?{4M?L^+?T`YvW6B*~0YGG5)d_$6XMe|M z{_Xo@%U57cR00*FjuX(1`m^pyzM+Bd8Bn1O_X3Hof%|nl00Km!$ogDC{@JUso4X`U zjg#Oz`_kczi4pz;NfeTV`gsur?Hs*!gyfm0kWVIf6G$!kY9|)-tDy}ujE@EDv(HfE zZO9meA_o))=DByn=ZWX-V*+BTYw>qf-xu~snxMKC+0#W3%<>eUNobiB6$Qu;)mg-1#fHb0`287S+wSY~kK#KZ|x=L+J%YTY$#qCCOnU)c^DzbTSY-l-}#7 z`6>l4=-@M~;|J?T;jKd}g_`Z*zHvR~y6NECSGoGdJqA1>s8|@XBPP&Kx;x#KS!it@ zU3`hGcvUBy{Wv&>MB?Z|eM(go85Ph_x0-(HbAV$RT(5*h(C-T52h%>#sfL64rO+-A zqLS(`_wzC=AYBDaJp-0e7;43{;Ai{y#sfOQv*laYV}8C1kqn>4D#iuR#Yaxp(9W%X z%zDz(?}n)*h>7EN0?wgG0=<|h92yFbI3KtP0arn>9f;D@Oqce>7f{T%L93Yk8H_-@ zqHgE;`T76M-A_6m=iHJ4IG7}uV<~Ta=SO+gRci+0x5;<^=O>Fmh>UV z;LKo=_m5j7Rp_mfek`u~hjXC@9fiK+Hu4L1;&Y4BF}SqIq*sY!^o(_=`yD~M;gO;u z;E1oqE{UtgxX4n8?8aeo68i#3d~YZnI{Y6AimRdxtWuC6OcE7Hbanb_BUZPm_yzA= zq$yJiN2MZ#e?uxtwra4$(emn2y$3BMiKce>4mv;GO4%`pRuL#Pcosr9wt4FD z=(}yil@?c8d`#haV@X6)79mxtRH**MN=#{nA}L#zc<0e3^keMZbUo$fo1qpO&kU2a zYMoH-+smt8@nqierkC6)O#e#mGzuUz@qp4;&U(nRx#W^9)W=3CE8eh}#tG>#iyBWC z1=-zx{x=*TlvSCoZm?)Wk3CRz!OvRAU5FO5PyvYdNGj-+O>9wswf#wLl?Hsj&m2}p3-bQe{unNMpy{igakr}i`j$knAw%q7$TapU?A}^#hYdC^1ZVKZ zhsiC~av1(y8CuaW1l=1tLJ;gC*JSV|8TRX|1>uKwUWVChhktY6^X^Ae9*=cW{f3V2 zLgZYOg`?U3tJUJF7JbBFEb?(Yu}Nxm5ADK%C>8q{m>5$S9y&pZ4G1|GjiY+Lve2!o zR z#o$~2YP=d)Q4)i5vgm&JU=j8Ikp7#4#~6R0KMEJQ{)c3-0tkV>y*%Yfr}5cOyelq# zb-?SpqspC50eBxKZMRo&<~e`Ll~-*iYX(aoNVU-8|CPs<24-4u*NM}`yLf1v(0!Ai zh0lK&@;cn{qB4;e&y`VJE$9+f8a6-;{qWi@`E!iVn7*?PxaCH_BM!?(9-dv^e zE0Gw+`w&-Y)X=F89gX97DvJU@l*W}Mhpx2z{D&W9-THNSZ|`t~AHPZNbP50luD~xY zzKRDt^aVIQf|n|+HBbPd(L~O%hp*;^&v-a<^XqK}W@Y@oRI&h;zrDtBYS(v#}?hGGCB{nl>%Bl}>aN`bkd@AyH@LbRxa zK-wCDiGE<&;DbqDDS`^g=>=~+S+hy8<$AiCHj~tAk5$l#Owd&xxD68|441c zI-IqGJh(VKLXu|e*)_|3&pw{hPCpGOft10rT^xmXi~hdzn1la2vRDD$-u(lKD8B2TL+_&(53XSY=q0N>G8=6%8;@pSbD?CJ4tF3`*!l`mp`6I zKl)*mwj{k1`TfIkr?dcoP&nf(q7we_2`}TDU%!~z@=1&}3{N_xgtBK?+3@`6Yp>#j zd#xd#ZJ~4sS}8wlO2feGkMH#ZPMQOoq>K0F15Eh-jtdL(%7dWUZPQw;S-+PHc zzylmFMAV=2=*9?_UA&2XvvWM`q4%WMnnP&~&fV^k^+V&YcGN*&q~FD5;SYMpf_qhw zx(vAwFVFW6sPN4k9IqbqI4j#IDUiX{QrCzs}C8$c4D;GnMG%r*~$Xi>c!s;MKK;LBb<8QH?#TH?c8wX8(F<( z6knL%-0p6;cq-~U@5B8FNJBSROah^+@Q`m`DTCohBp%ypv3=VPHf&giD|!^xf-@xT z9H#?`38lexx^Tm7)OXF}(=YZ+dIQW7hlKxe9jR^uH=Nf9RVuE^-7d-_*e+#A;5KQ=q~_*R?rJ^zp6oVjx^ez;30fEXq+hBV1|{!8A$2jBBWYHLDC!VV!07X?M5;&Kj>u@S!Vt!w$) z*MFT~ZR%i6!dO#t)_qp<&Nn=ku8|Y2VAnKi=)AH4};|9qcP0 zk|N1cirss8?sFc>2jBO%VexA7Nh9Uye|IaNI$t59O?+v=wfOP-PvWeT z4Yp+#5Q+vWa)2^#+{t4A!=fT}b(W@l_T%Sq&wCzDF+EF~WDBF^&QqF# z+NyCr@x3{o`Q}?N=>%y_VwIUkc~4qb+_rs=?_c3aCnrb~&**rDxF+BF(GDa{@PvR@ z^$I9qAswl6%}pIX`t3dF<*P`>Mo3n#V-XvAGAe6Pr3rW-9~QrNo_1#jI{TQPo=5AH zTAHH0M|zIbfD(e!Ugl5WTX~*NLI0l5Tntx8pZT1XP$m6|N4=? zC(Du;!nkWJeh7q6+*R~|#GV2xmXGtLFT9hZk6K4QKTn#ZgJTF_8)H1#@+P1B?mUlw z^)1ZgBk0i-uR&4)rO}x}X@@sOEFTR3qO=BR34uYS;)~cvLqU?VuUFFZVe#P{%7sFl z*_jSV5qy5_V{Jh+jbp=*s~OdHiq`NgkVm`*{OIT9`S z(jdm65{1(a<#JR4Rv9GGC|x71fij?-B~vBYNJcWD(1{?aP#>*xYb)ci-`&K|=Sq@# zit{c~dJA4f0+lqy%(Xb~xYd0Avu|g7d^Dzw?>dVg?xaAVUnfc_thF4zVKx8p#kcXW zN4=2kJ6qJo8kn+NK>g*7BbylK8<({B!<ICtkLO4U;W;29zYMs3$z;-kuM9r;8bD zkmrukMvq6Hx`KSy45sTS`}1kwVo1Bo-B$`HtXEvK#X>WMj%D4XG&q3cdv2g-$z1Cmz=kgc`2!2noIre32k^NNfTf zutgG{mkTuHsS_@n(LCqkP25-*l144Kev5W=m1Y$4ZH`zw!q>m}cGj<3d8aG#`E5mf zztZB4O>FRAm5M*$(#x;o;pe`Ly|X>)%~48IA`$AcRnaqtdmY08arBYgI%1~<-SEYC`wcK0@C zZs?F#WEGJ%E3D|)+ech1@%4<11g70~EAOWQeXLn?=I46dJHH z${T-N=li#=gmg3~f?0#n;y|-%D^shw;13 zv*M_fTYmtQLRo13{Ma^6UERgF2Bzl`qbOSnUu0;jF-1a=E6UvCY(~#poDQHJQ-+kG zSAmp8`x=+~l25x1Q=gW820u+OCL-2yL!6HzR!Dg7<$o4Vw@xMd0 z4DkdDu{J7j1ZXXImGSNCH5c#Aa8l!*Yq~u0s1Av@6ed~l-O6MRievy-XpS`a@D(fh z_@%<=NJ`!{lzEqIR4ANqc&*F3PV3N|H$6&yzT-p23g1>M>MK;y=VPFXisLzHk zGW<{BsHYx7DA-BG(JBQek^epcO>kz1V0m^X^T7?c(y_FT~7u`1(xs?876E}cx! zvanD(fF+~P-|n$uMEL6G|A8~_cTcP-(8*oS3tk|1c?AFiOW=|#Zr~4}@G^F7&&kF| zu%;Z;u}2i37LT=p%L^WJ%nUEMXURm`rd>*(R3ucX34Ojk20QN%n6GbMsQB+H#aLO_ ziopV{vWWvy&sbd$H{R+ym5s0;p~n#XIb^_s0;uRxh9ln|MIkOY9Tg>5-yMlK23hpI zK8Id_M|)8y(*a(^eL&@FiuZj%lYjBWT-%UV`n_=(R1iT8-|! zM5Lvr?SMYTmmp5FTPgliX1TQPAPE+jUqmqC2u(=rT#vPr8DIJ0TRHvolQ5=;K%cut z0Q~l3NdZKuEP=~^bu*89!YkRdX@=V51f|UZ=`Wx_y6FLh%S(+pgzdYrPl2z1me zj5$?x2NJmk1_T|@-*SE2q5WV%IX}SGEsSCLMQ>I+uNsW4@1GCIK^OE%@cT3W20F39 zZ}_;ARxC+hoM(Be&@;alF~V9iBMPIN{@0vuVd-Xr;s<$HbZ%ZZ70}X z9ajiNFQs$}>l}HJfFyw>S!s+49hq-6j?c?HTk3{XQmpg`xZqwTNk%a<&(RxK@E@Oh zGbf&M4AzwB2Z=zaB?T~$$`ZI~%XXgdN3Y?^OE#0On8X;fKmk;u1Z*LUBpps! z*2CK>fG7dMhYZ2uBYZgI0*Cq$gaa&9?th_a->-MCB(72|fzU%#wG&59!7A`r$%3LI zfP=1`Wie)(FF|c=(4M41_{=nOyoUBhmc%lG7VLX7Q zbiv;@UgLa#M|&isLsh~SSJ2XqRE4%_!+b5(eN9qLv1gWhopd<=@r8e2;}PpHrX;z` zFMe230JkF*naJMhS)Tsqf6KSNd@bq#Rwa^X7(cYIJPRqjs`nTiGhFqz zV6FJUdoWm+3o*O#{4!+0>cQv3v|LpIld2Ye{A|Cf!%BHADoa*+pKkjT_^%*aSJKH7b$>{!t;Zjf03^` z=-Rc=S0BU>!vc{73Sj8`aYx>dhI~Qn!1vj47b}2pc27m#86AMYslPz^sNp~Qu0mB) zvH0!`n9Bw{1eGhXAnwrjh8$IvAy9NJ9H*0kE*g-#R0_Z&I=Dk-=M+zQ(*60!M_)&C zq`9>C`?90}ek-ZC1Vj|?`j^k}%2$36V@JqFGOQ`-d&K=6dm(|v@QoJ0<;2~1c)N#% zX2sA){qt3rfT3fMq1ms7cHQA~?`JX$-gbVlBjaNulL%1gbDmW;wHi`3gM~Kusp=oY zTzxWli6UTi{m}R0SBCop_W2is6`+&~iv9Qth7^)c63U{4`8odPrGLzMZ+!t^ zqe5R={0EXH1@LRc`_O?#Cz|h{|8t)G!nd>O#(ks{<2V<0sskv1@a4E8dWU$+X)9n& zqfMvhv0e}r7PgANQ$baAr0ez){N7LtAb3@Tz+hZ-{mwt4sR&8J@`=7(Wi3b*$a8(p zKB@M5@A@8tL{wiIS#N`$3M{z;KBL&C+_^}{?a-mmI39}D@2m$$#`voHK}2md0H> z+JAepqyT;kvBsj4lpVWw^ZXaTgRlJiPmuBDBpEonPysA9!2xh^pJgSlKC|GALmal~ zQHlf6i1YmgDN^~i63PIyMypy>0DfVsYEk`Oq7@03_f?jmTCyQ^=TMO?SrLdYf+6CZ z!@ICUmJv7;C<0u-8%T8~h1N+puY{J|#;D-Z46*Hs#8jwtp;4(QlEiV-<;%I@vSl>u zKD6s{RV*Od4&Eb_fMH%GeACbIjovhf!ru-y-wUC9TNH?<+%v^lryjwFKJs@Qf8vo? z+e52_0oB@)97vWFz;7v4h5+C_@BerH=DZIxKa-P~sJ zI>*M59^M!VV@ttZX$ z3SuQND5-GHG%A%@U$9+yu0@#92}v5V8V6DUeP<%ol-(|>)#DY+{JPwXpbg z1@Ku?0KZO_6u^H$s@%MY=2zF;%JX0RE-v`Nr6kKHQ98jIix+#q?}`VnG%hcY#BtK3 z=hRh}Lz|Y=6<8bk?RgdGz*Vd)G;sm+Jzc3>^w9lRqVGK5&mO>y<%3@=r02?+Y1Pztfc&{kB!J09~02yVeC{2QM zfofezO6~$}THPtoo~}}C@|Mj?nA0AYM1;Ws#$Sa`lu{^!VrGWp4qM54-|}J}@`MKl zcgE$>-(Onzzl|&@fZsu??n9-qyyIQ}#vA|dr)bYP((xvE+sEM_NR&977n~7N3XYku z+4*3P8IGnOSX=}FWg525IzD}qWkfsv zo+M{DQp=?F^vgDIU+hJCgXqvM6iVg5^NE;_O#O2GF0nXQwuG*RS1A4 zXbK#Aft|{8i+0TGB-Hi`cs?aa;{Z0=lIz0cWkK~Q#J&$GUR$y%4?U%*r$tC%< zvZMfhM;Ww-R$P9?jlBAG@8{d!xDaZiWTQ=tGYcd9i$oDhXT1#-1cx>Z$1Tgb`=n)E zU7?htz+)qVu-`+^hh2ZB&}?sO){nth>hOyUkACCOxQ_PA`o}c<=4nHsG#f`9&bR<` zb{_FbAQT0>OUa8e{Jc{*Z>i~&G8e3KXsxk%iYrrkWl5?urBT5puz^L%dLj22dj&R2 zkK44Tqr*V8=m_$W{z$7kS3!dGr9o&Lnf0E!Y8xJn0kr<9`I*wlMSe1hI zfvwiYEWP(anrOarTgpw-H8w6Uc;sOoU0S>h+ITy3sD-Z_<_eNbh!bd~z+2MTF{sD` zC!!!|t#Q@`)LfQgaJ1v@{3IC#=S&*rtSembYz=Wc+HL0QfIotu{AzamQt;CGhA6V5Hj z6x?{@7T)#V&+y4FozML4S=9IhNuqJiF2w#+zWb03U!h{11i+8RGU+X=5mu+dxM*rV zCAW@DEU5=Wpt}+;0XrX~_@SYtpsbIEKzLt#%szNeO()Xuv@}*DV{*O2V0Bg&TCFui|mK4DMk1Q#G|7P*thd@|#3$DF>6YqY{=lJyJzs>w~ z7e*&YYZ>0#ezzPLMzjsi6E;eT#y{XKi8YKOOp0f6k|o3m>5B#(aRX*}ak zAI1G2e0n_6V~s;6OFR7kj4UaD|5d4ifR$^I7d^iF?O*W8Pkobb{qRy|_q3qdM2(Dw zR`xFX*D*HGueOWWe({5RcUU435bR>_qDoN=)#%3qVu-!cFnaLsDp)82E_TML$_Ilj zk6N_OU8MwmjRFt}jW-=&dtGR?QH=(tpLhgMdh7#v(v#0&!;y!C;dmQdC`lsN@vg^J z`M)FoZ3i$wAw8-DK$!k_e|w-zpds&{fZK-`MYmjrK*}oM^t}^GQ$RaVjGu7SA)Nb%XYjbk zK8VxLI0=d592F7kz|>!o|5;g50KZr9evm#?DmEr@j$i!ZO1}QhpYXjOUCh2Sz zgCx+XlVs_F^xgt}(62uHO;Pb*uK=pb#M}SgLXqk}Dg^Sx3#E0??LoH-spgOk6a2w_ zPT|Rq{R19&&gnFljl?q=tZ^ht-{l%|{Qjk{0RFpZ!T*01!Ch66gsg${o+MV>#kaZk z+FSV1PcG$$KfHvCF1>**+xFp&g(QQlhE6?7h3^c=NCzB#!xn@49Tmdj@!c~oO4d&%=?_g@>MJQXEL@7k$<&>+xvaXk^(r$#6{nK z*hK)C+Bd^xmtD&be)3B$y69T2yKx&kcJ9M=ZO|D>25A=Bb_t|<$hY^E0`Spl7&aXi zD}Vuy;UMA{5|PgNpnwV!Ufx2QuwvyX#~i+fQ%*jT`<-zjXPka_jy?9U$kZFeIui?d zTer-xd*5#6 z=1P1H{bwf<+z90($DbPNI^3@{9=Q45f^&iFP&6nDqR1MWWy{9caL5WyI%XrM-1BHo zJn<-wKjBagS-%EIhel|lU$2)I{Xr?eF$S=t01hUp07ey#e*mZ%NNawcUAuR)b;}+$ zZQjk!ox9n)Z;GkuS!UdWJkV7&}A)>J>@}X_8WJCX6;~ET3pFIXTYCRm)kw zZUyUBPH@PERjfZ`4Xf5npj5VSe3V#g!N+oDRap{CLA`@h_EP{$3;vEI-UkiV6w>p#-^N0a6kQ`IbETJAf=AlU-5(cNUR;p-&%PJX8`~apMh? z#Nx#o?En5b;t3X>Lt?5=me%?mR`yc>w-*FlQV4fexji_+lAC^4kcA51uH6~&E-Fh4 zeMu@AQUFUk{*o-oonIEN@1+p!k}SzxSbmKQuw)4=$&%dJWyu0qk|ntd%Wt6o)Ngm# zk}S!R+~H(tWiQE++=XQ*25@^m|57YqNtWc!DoYl?k}SzxSd!no!!O|jmgN6OmgdjF zEK3%^k}SzxSpFLcL6&4mmgJ5mOBTSAEXiG1mK4B}EXiG1mK4B}EXiG1{tM;6`!~Z} wlK&0)eMixM-@m^k|C6$00W8Uq+=b=;0VP#mvYa#DUH||907*qoM6N<$g1`sGSpWb4 literal 0 HcmV?d00001 diff --git a/rcg/fuzzy/categories.py b/rcg/fuzzy/categories.py index 65a448d..009e8c1 100644 --- a/rcg/fuzzy/categories.py +++ b/rcg/fuzzy/categories.py @@ -80,7 +80,8 @@ def __str__(self) -> str: f"{self.higher_hills} {self.mountains} {self.highest_mountains}" ) - def get_all_categories(self) -> List[str]: + @staticmethod + def get_all_categories() -> List[str]: """ Returns a list of all land form types. @@ -88,15 +89,15 @@ def get_all_categories(self) -> List[str]: - List[str]: A list of strings representing all land form types. """ return [ - self.marshes_and_lowlands, - self.flats_and_plateaus, - self.flats_and_plateaus_in_combination_with_hills, - self.hills_with_gentle_slopes, - self.steeper_hills_and_foothills, - self.hills_and_outcrops_of_mountain_ranges, - self.higher_hills, - self.mountains, - self.highest_mountains, + "marshes_and_lowlands", + "flats_and_plateaus", + "flats_and_plateaus_in_combination_with_hills", + "hills_with_gentle_slopes", + "steeper_hills_and_foothills", + "hills_and_outcrops_of_mountain_ranges", + "higher_hills", + "mountains", + "highest_mountains", ] @@ -108,14 +109,15 @@ class LandCover: defined in the __init__ method return string values for each land cover type. Class Attributes: - - medium_conditions (int): Numeric value for medium conditions land cover type. - permeable_areas (int): Numeric value for permeable areas land cover type. - permeable_terrain_on_plains (int): Numeric value for permeable terrain on plains land cover type. - - hilly (int): Numeric value for hilly land cover type. - - mountains (int): Numeric value for mountains land cover type. - - bare_rocky_slopes (int): Numeric value for bare rocky slopes land cover type. - - urban (int): Numeric value for urban land cover type. - - suburban (int): Numeric value for suburban land cover type. + - mountains_vegetated (int): Numeric value for vegetated mountains land cover type. + - mountains_rocky (int): Numeric value for rocky mountains land cover type. + - urban_weakly_impervious (int): Numeric value for weakly impervious urban land cover type. + - urban_moderately_impervious (int): Numeric value for moderately impervious urban land cover type. + - urban_highly_impervious (int): Numeric value for highly impervious urban land cover type. + - suburban_weakly_impervious (int): Numeric value for weakly impervious suburban land cover type. + - suburban_highly_impervious (int): Numeric value for moderately impervious suburban land cover type. - rural (int): Numeric value for rural land cover type. - forests (int): Numeric value for forests land cover type. - meadows (int): Numeric value for meadows land cover type. @@ -123,45 +125,47 @@ class LandCover: - marshes (int): Numeric value for marshes land cover type. Attributes: - - medium_conditions (str): Medium conditions land cover type. - permeable_areas (str): Permeable areas land cover type. - permeable_terrain_on_plains (str): Permeable terrain on plains land cover type. - - hilly (str): Hilly land cover type. - - mountains (str): Mountains land cover type. - - bare_rocky_slopes (str): Bare rocky slopes land cover type. - - urban (str): Urban land cover type. - - suburban (str): Suburban land cover type. + - mountains_vegetated (str): Vegetated mountains land cover type. + - mountains_rocky (str): Rocky mountains land cover type. + - urban_weakly_impervious (str): Weakly impervious urban land cover type. + - urban_moderately_impervious (str): Moderately impervious urban land cover type. + - urban_highly_impervious (str): Highly impervious urban land cover type. + - suburban_weakly_impervious (str): Weakly impervious suburban land cover type. + - suburban_highly_impervious (str): Moderately impervious suburban land cover type. - rural (str): Rural land cover type. - forests (str): Forests land cover type. - meadows (str): Meadows land cover type. - arable (str): Arable land cover type. - marshes (str): Marshes land cover type. """ - - medium_conditions = 1 - permeable_areas = 2 - permeable_terrain_on_plains = 3 - hilly = 4 - mountains = 5 - bare_rocky_slopes = 6 - urban = 7 - suburban = 8 - rural = 9 - forests = 10 - meadows = 11 - arable = 12 - marshes = 13 + permeable_areas = 1 + permeable_terrain_on_plains = 2 + mountains_vegetated = 3 + mountains_rocky = 4 + urban_weakly_impervious = 5 + urban_moderately_impervious = 6 + urban_highly_impervious = 7 + suburban_weakly_impervious = 8 + suburban_highly_impervious = 9 + rural = 10 + forests = 11 + meadows = 12 + arable = 13 + marshes = 14 def __init__(self) -> None: """Initialize a LandCover object with predefined land cover types as string attributes.""" - self.medium_conditions = "medium_conditions" self.permeable_areas = "permeable_areas" self.permeable_terrain_on_plains = "permeable_terrain_on_plains" - self.hilly = "hilly" - self.mountains = "mountains" - self.bare_rocky_slopes = "bare_rocky_slopes" - self.urban = "urban" - self.suburban = "suburban" + self.mountains_vegetated = "mountains_vegetated" + self.mountains_rocky = "mountains_rocky" + self.urban_weakly_impervious = "urban_weakly_impervious" + self.urban_moderately_impervious = "urban_moderately_impervious" + self.urban_highly_impervious = "urban_highly_impervious" + self.suburban_weakly_impervious = "suburban_weakly_impervious" + self.suburban_highly_impervious = "suburban_highly_impervious" self.rural = "rural" self.forests = "forests" self.meadows = "meadows" @@ -171,14 +175,15 @@ def __init__(self) -> None: def __str__(self) -> str: """Return a string representation of the LandCover object.""" return ( - f"{self.medium_conditions}: {self.medium_conditions}" f"\n{self.permeable_areas}: {self.permeable_areas}" f"\n{self.permeable_terrain_on_plains}: {self.permeable_terrain_on_plains}" - f"\n{self.hilly}: {self.hilly}" - f"\n{self.mountains}: {self.mountains}" - f"\n{self.bare_rocky_slopes}: {self.bare_rocky_slopes}" - f"\n{self.urban}: {self.urban}" - f"\n{self.suburban}: {self.suburban}" + f"\n{self.mountains_vegetated}: {self.mountains_vegetated}" + f"\n{self.mountains_rocky}: {self.mountains_rocky}" + f"\n{self.urban_weakly_impervious}: {self.urban_weakly_impervious}" + f"\n{self.urban_moderately_impervious}: {self.urban_moderately_impervious}" + f"\n{self.urban_highly_impervious}: {self.urban_highly_impervious}" + f"\n{self.suburban_weakly_impervious}: {self.suburban_weakly_impervious}" + f"\n{self.suburban_highly_impervious}: {self.suburban_highly_impervious}" f"\n{self.rural}: {self.rural}" f"\n{self.forests}: {self.forests}" f"\n{self.meadows}: {self.meadows}" @@ -186,7 +191,8 @@ def __str__(self) -> str: f"\n{self.marshes}: {self.marshes}" ) - def get_all_categories(self) -> List[str]: + @staticmethod + def get_all_categories() -> List[str]: """ Returns a list of all land cover types. @@ -194,19 +200,20 @@ def get_all_categories(self) -> List[str]: - List[str]: A list of strings representing all land cover types. """ return [ - self.medium_conditions, - self.permeable_areas, - self.permeable_terrain_on_plains, - self.hilly, - self.mountains, - self.bare_rocky_slopes, - self.urban, - self.suburban, - self.rural, - self.forests, - self.meadows, - self.arable, - self.marshes, + "permeable_areas", + "permeable_terrain_on_plains", + "mountains_vegetated", + "mountains_rocky", + "urban_weakly_impervious", + "urban_moderately_impervious", + "urban_highly_impervious", + "suburban_weakly_impervious", + "suburban_highly_impervious", + "rural", + "forests", + "meadows", + "arable", + "marshes", ] @@ -280,10 +287,14 @@ class Impervious: - meadows (str): Meadow land impervious type. - forests (str): Forest land impervious type. - rural (str): Rural land impervious type. - - suburban (str): Suburban land impervious type. - - urban (str): Urban land impervious type. - - mountains (str): Mountain land impervious type. - - hilly (str): Hilly land impervious type. + - suburban_weakly_impervious (str): Suburban weakly impervious type. + - suburban_highly_impervious (str): Suburban moderately impervious type. + - suburban_highly_impervious (str): Suburban highly impervious type. + - urban_weakly_impervious (str): Urban weakly impervious type. + - urban_moderately_impervious (str): Urban moderately impervious type. + - urban_highly_impervious (str): Urban highly impervious type. + - mountains_rocky (str): Rocky mountain land impervious type. + - mountains_vegetated (str): Vegetated mountain land impervious type. """ def __init__(self) -> None: @@ -295,20 +306,23 @@ def __init__(self) -> None: self.meadows = "meadows" self.forests = "forests" self.rural = "rural" - self.suburban = "suburban" - self.urban = "urban" - self.mountains = "mountains" - self.hilly = "hilly" + self.suburban_weakly_impervious = "suburban_weakly_impervious" + self.suburban_highly_impervious = "suburban_highly_impervious" + self.urban_weakly_impervious = "urban_weakly_impervious" + self.urban_moderately_impervious = "urban_moderately_impervious" + self.urban_highly_impervious = "urban_highly_impervious" + self.mountains_rocky = "mountains_rocky" + self.mountains_vegetated = "mountains_vegetated" def __str__(self) -> str: """ Returns a string representation of the impervious types. """ - return f"{self.marshes}, {self.arable}, {self.meadows}, {self.forests}, {self.rural}, {self.suburban}, {self.urban}, {self.mountains}, {self.hilly}" + return f"{self.marshes}, {self.arable}, {self.meadows}, {self.forests}, {self.rural}, {self.suburban_weakly_impervious}, {self.suburban_highly_impervious}, {self.urban_weakly_impervious}, {self.urban_moderately_impervious}, {self.urban_highly_impervious}, {self.mountains_rocky}, {self.mountains_vegetated}" def get_all_categories(self) -> List[str]: """ - Returns a dictionary containing all impervious type attributes as key-value pairs. + Returns a list containing all impervious type attributes as strings. Returns: - List[str]: A list of strings representing all Impervious types. @@ -319,10 +333,13 @@ def get_all_categories(self) -> List[str]: self.meadows, self.forests, self.rural, - self.suburban, - self.urban, - self.mountains, - self.hilly, + self.suburban_weakly_impervious, + self.suburban_highly_impervious, + self.urban_weakly_impervious, + self.urban_moderately_impervious, + self.urban_highly_impervious, + self.mountains_rocky, + self.mountains_vegetated, ] diff --git a/rcg/fuzzy/engine.py b/rcg/fuzzy/engine.py index c90f812..5ec16e3 100644 --- a/rcg/fuzzy/engine.py +++ b/rcg/fuzzy/engine.py @@ -1,19 +1,10 @@ import skfuzzy as fuzz from rcg.fuzzy import categories - from .rules import slope_rules, impervious_rules, catchment_rules -from .categories import ( - land_form, - land_cover, - slope_ctgr, - impervious_ctgr, - catchment_ctgr, -) from skfuzzy import control as ctrl from rcg.fuzzy.memberships import Memberships, membership - class FuzzyEngine: """ FuzzyEngine returns the result of calculating the slope, impervious, and catchment values. diff --git a/rcg/fuzzy/memberships.py b/rcg/fuzzy/memberships.py index d7497f7..f6b0471 100644 --- a/rcg/fuzzy/memberships.py +++ b/rcg/fuzzy/memberships.py @@ -27,8 +27,8 @@ def __init__(self) -> None: """ self.land_form_type = ctrl.Antecedent(np.arange(0, 10, 1), "land_form") self.land_cover_type = ctrl.Antecedent(np.arange(0, 14, 1), "land_cover") - self.slope = ctrl.Consequent(np.arange(1, 101, 1), "slope") - self.impervious = ctrl.Consequent(np.arange(1, 101, 1), "impervious") + self.slope = ctrl.Consequent(np.arange(0, 60, 1), "slope") + self.impervious = ctrl.Consequent(np.arange(0, 101, 1), "impervious") self.catchment = ctrl.Consequent(np.arange(1, 101, 1), "catchment") def populate_land_use(self): @@ -62,108 +62,124 @@ def populate_land_use(self): self.land_form_type[land_form.highest_mountains] = fuzz.trimf( self.land_form_type.universe, [8, 9, 10] ) - - def populate_land_form(self) -> None: + def populate_land_cover(self) -> None: """ Populate land form type with membership functions. """ - self.land_cover_type[land_cover.medium_conditions] = fuzz.trimf( + self.land_cover_type[land_cover.permeable_areas] = fuzz.trimf( self.land_cover_type.universe, [0, 1, 2] ) - self.land_cover_type[land_cover.permeable_areas] = fuzz.trimf( + self.land_cover_type[land_cover.permeable_terrain_on_plains] = fuzz.trimf( self.land_cover_type.universe, [1, 2, 3] ) - self.land_cover_type[land_cover.permeable_terrain_on_plains] = fuzz.trimf( + self.land_cover_type[land_cover.mountains_vegetated] = fuzz.trimf( self.land_cover_type.universe, [2, 3, 4] ) - self.land_cover_type[land_cover.hilly] = fuzz.trimf( + self.land_cover_type[land_cover.mountains_rocky] = fuzz.trimf( self.land_cover_type.universe, [3, 4, 5] ) - self.land_cover_type[land_cover.mountains] = fuzz.trimf( + self.land_cover_type[land_cover.urban_weakly_impervious] = fuzz.trimf( self.land_cover_type.universe, [4, 5, 6] ) - self.land_cover_type[land_cover.bare_rocky_slopes] = fuzz.trimf( + self.land_cover_type[land_cover.urban_moderately_impervious] = fuzz.trimf( self.land_cover_type.universe, [5, 6, 7] ) - self.land_cover_type[land_cover.urban] = fuzz.trimf( + self.land_cover_type[land_cover.urban_highly_impervious] = fuzz.trimf( self.land_cover_type.universe, [6, 7, 8] ) - self.land_cover_type[land_cover.suburban] = fuzz.trimf( + self.land_cover_type[land_cover.suburban_weakly_impervious] = fuzz.trimf( self.land_cover_type.universe, [7, 8, 9] ) - self.land_cover_type[land_cover.rural] = fuzz.trimf( + self.land_cover_type[land_cover.suburban_highly_impervious] = fuzz.trimf( self.land_cover_type.universe, [8, 9, 10] ) - self.land_cover_type[land_cover.forests] = fuzz.trimf( + self.land_cover_type[land_cover.rural] = fuzz.trimf( self.land_cover_type.universe, [9, 10, 11] ) - self.land_cover_type[land_cover.meadows] = fuzz.trimf( + self.land_cover_type[land_cover.forests] = fuzz.trimf( self.land_cover_type.universe, [10, 11, 12] ) - self.land_cover_type[land_cover.arable] = fuzz.trimf( + self.land_cover_type[land_cover.meadows] = fuzz.trimf( self.land_cover_type.universe, [11, 12, 13] ) - self.land_cover_type[land_cover.marshes] = fuzz.trimf( + self.land_cover_type[land_cover.arable] = fuzz.trimf( self.land_cover_type.universe, [12, 13, 14] ) + self.land_cover_type[land_cover.marshes] = fuzz.trimf( + self.land_cover_type.universe, [13, 14, 15] + ) + def populate_slope(self) -> None: """Populate slope with membership functions.""" self.slope[slope_ctgr.marshes_and_lowlands] = fuzz.trimf( - self.slope.universe, [0, 0, 5] + self.slope.universe, [0, 0, 1] ) self.slope[slope_ctgr.flats_and_plateaus] = fuzz.trimf( - self.slope.universe, [0, 5, 10] + self.slope.universe, [0, 1, 2.5] ) self.slope[ slope_ctgr.flats_and_plateaus_in_combination_with_hills - ] = fuzz.trimf(self.slope.universe, [5, 10, 15]) + ] = fuzz.trimf(self.slope.universe, [1, 2.5, 5]) self.slope[slope_ctgr.hills_with_gentle_slopes] = fuzz.trimf( - self.slope.universe, [10, 15, 20] + self.slope.universe, [2.5, 5, 8] ) self.slope[slope_ctgr.steeper_hills_and_foothills] = fuzz.trimf( - self.slope.universe, [15, 20, 25] + self.slope.universe, [5, 8, 15] ) self.slope[slope_ctgr.hills_and_outcrops_of_mountain_ranges] = fuzz.trimf( - self.slope.universe, [20, 30, 40] + self.slope.universe, [8, 15, 20] ) self.slope[slope_ctgr.higher_hills] = fuzz.trimf( - self.slope.universe, [30, 45, 60] + self.slope.universe, [15, 20, 30] + ) + self.slope[slope_ctgr.mountains] = fuzz.trimf( + self.slope.universe, [20, 30, 40] ) - self.slope[slope_ctgr.mountains] = fuzz.trimf(self.slope.universe, [45, 60, 80]) self.slope[slope_ctgr.highest_mountains] = fuzz.trimf( - self.slope.universe, [60, 80, 100] + self.slope.universe, [30, 50, 60] ) + def populate_impervious(self) -> None: """Populate impervious with membership functions.""" self.impervious[impervious_ctgr.marshes] = fuzz.trimf( - self.impervious.universe, [0, 0, 5] + self.impervious.universe, [0, 0, 2] ) self.impervious[impervious_ctgr.arable] = fuzz.trimf( - self.impervious.universe, [0, 5, 10] + self.impervious.universe, [0, 2, 4] ) self.impervious[impervious_ctgr.meadows] = fuzz.trimf( - self.impervious.universe, [5, 10, 15] + self.impervious.universe, [2, 5, 8] ) self.impervious[impervious_ctgr.forests] = fuzz.trimf( - self.impervious.universe, [10, 15, 20] + self.impervious.universe, [5, 7, 9] ) self.impervious[impervious_ctgr.rural] = fuzz.trimf( - self.impervious.universe, [15, 20, 25] + self.impervious.universe, [7, 11, 15] + ) + self.impervious[impervious_ctgr.suburban_weakly_impervious] = fuzz.trimf( + self.impervious.universe, [10, 25, 40] ) - self.impervious[impervious_ctgr.suburban] = fuzz.trimf( - self.impervious.universe, [20, 25, 30] + self.impervious[impervious_ctgr.suburban_highly_impervious] = fuzz.trimf( + self.impervious.universe, [35, 50, 65] ) - self.impervious[impervious_ctgr.urban] = fuzz.trimf( - self.impervious.universe, [25, 30, 35] + self.impervious[impervious_ctgr.urban_weakly_impervious] = fuzz.trimf( + self.impervious.universe, [30, 45, 60] ) - self.impervious[impervious_ctgr.mountains] = fuzz.trimf( - self.impervious.universe, [30, 65, 100] + self.impervious[impervious_ctgr.urban_moderately_impervious] = fuzz.trimf( + self.impervious.universe, [50, 65, 80] ) - self.impervious[impervious_ctgr.hilly] = fuzz.trimf( - self.impervious.universe, [50, 100, 100] + self.impervious[impervious_ctgr.urban_highly_impervious] = fuzz.trimf( + self.impervious.universe, [75, 85, 100] ) + self.impervious[impervious_ctgr.mountains_rocky] = fuzz.trimf( + self.impervious.universe, [20, 40, 60] + ) + self.impervious[impervious_ctgr.mountains_vegetated] = fuzz.trimf( + self.impervious.universe, [5, 15, 25] + ) + def populate_catchment(self) -> None: """Populate catchment with membership functions.""" @@ -192,7 +208,7 @@ def populate_catchment(self) -> None: membership = Memberships() membership.populate_land_use() -membership.populate_land_form() +membership.populate_land_cover() membership.populate_slope() membership.populate_impervious() membership.populate_catchment() diff --git a/rcg/fuzzy/rules.py b/rcg/fuzzy/rules.py index 92299fb..9ad2565 100644 --- a/rcg/fuzzy/rules.py +++ b/rcg/fuzzy/rules.py @@ -16,58 +16,12 @@ def __init__(self) -> None: self.rule1 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.mountains] - & membership.land_form_type[ - categories.land_form.steeper_hills_and_foothills - ] - ) - | ( - membership.land_cover_type[categories.land_cover.mountains] - & membership.land_form_type[ - categories.land_form.hills_and_outcrops_of_mountain_ranges - ] - ) - | ( - membership.land_cover_type[categories.land_cover.mountains] - & membership.land_form_type[categories.land_form.higher_hills] - ) - | ( - membership.land_cover_type[categories.land_cover.mountains] - & membership.land_form_type[categories.land_form.mountains] - ) - | ( - membership.land_cover_type[categories.land_cover.mountains] - & membership.land_form_type[categories.land_form.highest_mountains] - ) - | ( - membership.land_cover_type[categories.land_cover.bare_rocky_slopes] - & membership.land_form_type[ - categories.land_form.hills_and_outcrops_of_mountain_ranges - ] - ) - | ( - membership.land_cover_type[categories.land_cover.bare_rocky_slopes] - & membership.land_form_type[categories.land_form.higher_hills] - ) - | ( - membership.land_cover_type[categories.land_cover.bare_rocky_slopes] - & membership.land_form_type[categories.land_form.mountains] - ) - | ( - membership.land_cover_type[categories.land_cover.bare_rocky_slopes] - & membership.land_form_type[categories.land_form.highest_mountains] - ) - | ( - membership.land_cover_type[categories.land_cover.hilly] - & membership.land_form_type[categories.land_form.higher_hills] - ) - | ( - membership.land_cover_type[categories.land_cover.hilly] - & membership.land_form_type[categories.land_form.mountains] + membership.land_cover_type[categories.land_cover.mountains_vegetated] + & membership.land_form_type[categories.land_form.marshes_and_lowlands] ) | ( - membership.land_cover_type[categories.land_cover.hilly] - & membership.land_form_type[categories.land_form.highest_mountains] + membership.land_cover_type[categories.land_cover.mountains_rocky] + & membership.land_form_type[categories.land_form.marshes_and_lowlands] ) ) ) @@ -81,18 +35,6 @@ def __init__(self) -> None: membership.land_cover_type[categories.land_cover.marshes] & membership.land_form_type[categories.land_form.highest_mountains] ) - | ( - membership.land_cover_type[categories.land_cover.medium_conditions] - & membership.land_form_type[categories.land_form.higher_hills] - ) - | ( - membership.land_cover_type[categories.land_cover.medium_conditions] - & membership.land_form_type[categories.land_form.mountains] - ) - | ( - membership.land_cover_type[categories.land_cover.medium_conditions] - & membership.land_form_type[categories.land_form.highest_mountains] - ) | ( membership.land_cover_type[categories.land_cover.permeable_areas] & membership.land_form_type[categories.land_form.mountains] @@ -175,95 +117,119 @@ def __init__(self) -> None: self.rule3 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.urban] - & membership.land_form_type[categories.land_form.marshes_and_lowlands] + membership.land_cover_type[categories.land_cover.mountains_vegetated] + & membership.land_form_type[categories.land_form.flats_and_plateaus] + ) + | ( + membership.land_cover_type[categories.land_cover.mountains_vegetated] + & membership.land_form_type[categories.land_form.flats_and_plateaus_in_combination_with_hills] + ) + | ( + membership.land_cover_type[categories.land_cover.mountains_vegetated] + & membership.land_form_type[categories.land_form.hills_and_outcrops_of_mountain_ranges] + ) + | ( + membership.land_cover_type[categories.land_cover.mountains_vegetated] + & membership.land_form_type[categories.land_form.hills_with_gentle_slopes] + ) + | ( + membership.land_cover_type[categories.land_cover.mountains_vegetated] + & membership.land_form_type[categories.land_form.steeper_hills_and_foothills] ) ) ) self.rule4 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.urban] - & membership.land_form_type[categories.land_form.flats_and_plateaus] + membership.land_cover_type[categories.land_cover.mountains_vegetated] + & membership.land_form_type[categories.land_form.hills_and_outcrops_of_mountain_ranges] ) ) ) self.rule6 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.urban] - & membership.land_form_type[ - categories.land_form.flats_and_plateaus_in_combination_with_hills - ] + membership.land_cover_type[categories.land_cover.mountains_vegetated] + & membership.land_form_type[categories.land_form.higher_hills] + ) + | ( + membership.land_cover_type[categories.land_cover.mountains_vegetated] + & membership.land_form_type[categories.land_form.mountains] + ) + | ( + membership.land_cover_type[categories.land_cover.mountains_vegetated] + & membership.land_form_type[categories.land_form.highest_mountains] ) ) ) self.rule7 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.urban] - & membership.land_form_type[ - categories.land_form.hills_with_gentle_slopes - ] + membership.land_cover_type[categories.land_cover.mountains_rocky] + & membership.land_form_type[categories.land_form.flats_and_plateaus] ) ) ) self.rule8 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.urban] - & membership.land_form_type[ - categories.land_form.steeper_hills_and_foothills - ] + membership.land_cover_type[categories.land_cover.mountains_rocky] + & membership.land_form_type[categories.land_form.flats_and_plateaus_in_combination_with_hills] ) ) ) self.rule9 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.urban] - & membership.land_form_type[ - categories.land_form.hills_and_outcrops_of_mountain_ranges - ] + membership.land_cover_type[categories.land_cover.mountains_rocky] + & membership.land_form_type[categories.land_form.hills_with_gentle_slopes] ) ) ) self.rule10 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.urban] - & membership.land_form_type[categories.land_form.higher_hills] + membership.land_cover_type[categories.land_cover.mountains_rocky] + & membership.land_form_type[categories.land_form.steeper_hills_and_foothills] ) ) ) self.rule11 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.urban] - & membership.land_form_type[categories.land_form.mountains] + membership.land_cover_type[categories.land_cover.mountains_rocky] + & membership.land_form_type[categories.land_form.hills_and_outcrops_of_mountain_ranges] ) ) ) self.rule12 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.urban] - & membership.land_form_type[categories.land_form.highest_mountains] + membership.land_cover_type[categories.land_cover.mountains_rocky] + & membership.land_form_type[categories.land_form.higher_hills] ) ) ) self.rule13 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.suburban] - & membership.land_form_type[categories.land_form.marshes_and_lowlands] + membership.land_cover_type[categories.land_cover.mountains_rocky] + & membership.land_form_type[categories.land_form.mountains] + ) + | ( + membership.land_cover_type[categories.land_cover.mountains_rocky] + & membership.land_form_type[categories.land_form.highest_mountains] ) ) ) self.rule14 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.suburban] + membership.land_cover_type[categories.land_cover.urban_weakly_impervious] + & membership.land_form_type[categories.land_form.marshes_and_lowlands] + ) + | ( + membership.land_cover_type[categories.land_cover.urban_weakly_impervious] & membership.land_form_type[categories.land_form.flats_and_plateaus] ) ) @@ -271,64 +237,96 @@ def __init__(self) -> None: self.rule15 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.suburban] - & membership.land_form_type[ - categories.land_form.flats_and_plateaus_in_combination_with_hills - ] + membership.land_cover_type[categories.land_cover.urban_weakly_impervious] + & membership.land_form_type[categories.land_form.flats_and_plateaus_in_combination_with_hills] + ) + | ( + membership.land_cover_type[categories.land_cover.urban_weakly_impervious] + & membership.land_form_type[categories.land_form.hills_with_gentle_slopes] ) ) ) self.rule16 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.suburban] - & membership.land_form_type[ - categories.land_form.hills_with_gentle_slopes - ] + membership.land_cover_type[categories.land_cover.urban_weakly_impervious] + & membership.land_form_type[categories.land_form.steeper_hills_and_foothills] + ) + | ( + membership.land_cover_type[categories.land_cover.urban_weakly_impervious] + & membership.land_form_type[categories.land_form.hills_and_outcrops_of_mountain_ranges] ) ) ) self.rule17 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.suburban] - & membership.land_form_type[ - categories.land_form.steeper_hills_and_foothills - ] + membership.land_cover_type[categories.land_cover.urban_weakly_impervious] + & membership.land_form_type[categories.land_form.higher_hills] + ) + | ( + membership.land_cover_type[categories.land_cover.urban_weakly_impervious] + & membership.land_form_type[categories.land_form.mountains] + ) + | ( + membership.land_cover_type[categories.land_cover.urban_weakly_impervious] + & membership.land_form_type[categories.land_form.highest_mountains] ) ) ) self.rule18 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.suburban] - & membership.land_form_type[ - categories.land_form.hills_and_outcrops_of_mountain_ranges - ] + membership.land_cover_type[categories.land_cover.urban_moderately_impervious] + & membership.land_form_type[categories.land_form.marshes_and_lowlands] + ) + | ( + membership.land_cover_type[categories.land_cover.urban_moderately_impervious] + & membership.land_form_type[categories.land_form.flats_and_plateaus] ) ) ) self.rule19 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.suburban] - & membership.land_form_type[categories.land_form.higher_hills] + membership.land_cover_type[categories.land_cover.urban_moderately_impervious] + & membership.land_form_type[categories.land_form.flats_and_plateaus_in_combination_with_hills] + ) + | ( + membership.land_cover_type[categories.land_cover.urban_moderately_impervious] + & membership.land_form_type[categories.land_form.hills_with_gentle_slopes] + ) + | ( + membership.land_cover_type[categories.land_cover.urban_moderately_impervious] + & membership.land_form_type[categories.land_form.steeper_hills_and_foothills] ) ) ) self.rule20 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.suburban] + membership.land_cover_type[categories.land_cover.urban_moderately_impervious] + & membership.land_form_type[categories.land_form.hills_and_outcrops_of_mountain_ranges] + ) + | ( + membership.land_cover_type[categories.land_cover.urban_moderately_impervious] + & membership.land_form_type[categories.land_form.higher_hills] + ) + | ( + membership.land_cover_type[categories.land_cover.urban_moderately_impervious] & membership.land_form_type[categories.land_form.mountains] ) + | ( + membership.land_cover_type[categories.land_cover.urban_moderately_impervious] + & membership.land_form_type[categories.land_form.highest_mountains] + ) ) ) self.rule21 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.suburban] - & membership.land_form_type[categories.land_form.highest_mountains] + membership.land_cover_type[categories.land_cover.urban_highly_impervious] + & membership.land_form_type[categories.land_form.marshes_and_lowlands] ) ) ) @@ -581,19 +579,11 @@ def __init__(self) -> None: self.rule42 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.bare_rocky_slopes] - & membership.land_form_type[categories.land_form.marshes_and_lowlands] - ) - | ( - membership.land_cover_type[categories.land_cover.bare_rocky_slopes] + membership.land_cover_type[categories.land_cover.urban_highly_impervious] & membership.land_form_type[categories.land_form.flats_and_plateaus] ) | ( - membership.land_cover_type[categories.land_cover.mountains] - & membership.land_form_type[categories.land_form.marshes_and_lowlands] - ) - | ( - membership.land_cover_type[categories.land_cover.mountains] + membership.land_cover_type[categories.land_cover.urban_highly_impervious] & membership.land_form_type[categories.land_form.flats_and_plateaus] ) ) @@ -601,38 +591,36 @@ def __init__(self) -> None: self.rule43 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.bare_rocky_slopes] - & membership.land_form_type[ - categories.land_form.flats_and_plateaus_in_combination_with_hills - ] + membership.land_cover_type[categories.land_cover.urban_highly_impervious] + & membership.land_form_type[categories.land_form.hills_with_gentle_slopes] ) | ( - membership.land_cover_type[categories.land_cover.mountains] - & membership.land_form_type[ - categories.land_form.flats_and_plateaus_in_combination_with_hills - ] + membership.land_cover_type[categories.land_cover.urban_highly_impervious] + & membership.land_form_type[categories.land_form.steeper_hills_and_foothills] + ) + | ( + membership.land_cover_type[categories.land_cover.urban_highly_impervious] + & membership.land_form_type[categories.land_form.hills_with_gentle_slopes] + ) + | ( + membership.land_cover_type[categories.land_cover.urban_highly_impervious] + & membership.land_form_type[categories.land_form.hills_and_outcrops_of_mountain_ranges] ) ) ) self.rule44 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.bare_rocky_slopes] - & membership.land_form_type[ - categories.land_form.hills_with_gentle_slopes - ] + membership.land_cover_type[categories.land_cover.urban_highly_impervious] + & membership.land_form_type[categories.land_form.higher_hills] ) | ( - membership.land_cover_type[categories.land_cover.bare_rocky_slopes] - & membership.land_form_type[ - categories.land_form.steeper_hills_and_foothills - ] + membership.land_cover_type[categories.land_cover.urban_highly_impervious] + & membership.land_form_type[categories.land_form.mountains] ) | ( - membership.land_cover_type[categories.land_cover.mountains] - & membership.land_form_type[ - categories.land_form.hills_with_gentle_slopes - ] + membership.land_cover_type[categories.land_cover.urban_highly_impervious] + & membership.land_form_type[categories.land_form.highest_mountains] ) ) ) @@ -640,10 +628,6 @@ def __init__(self) -> None: self.rule45 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.medium_conditions] - & membership.land_form_type[categories.land_form.marshes_and_lowlands] - ) - | ( membership.land_cover_type[categories.land_cover.permeable_areas] & membership.land_form_type[categories.land_form.marshes_and_lowlands] ) @@ -658,10 +642,6 @@ def __init__(self) -> None: self.rule46 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.medium_conditions] - & membership.land_form_type[categories.land_form.flats_and_plateaus] - ) - | ( membership.land_cover_type[categories.land_cover.permeable_areas] & membership.land_form_type[categories.land_form.flats_and_plateaus] ) @@ -676,12 +656,6 @@ def __init__(self) -> None: self.rule47 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.medium_conditions] - & membership.land_form_type[ - categories.land_form.flats_and_plateaus_in_combination_with_hills - ] - ) - | ( membership.land_cover_type[categories.land_cover.permeable_areas] & membership.land_form_type[ categories.land_form.flats_and_plateaus_in_combination_with_hills @@ -700,12 +674,6 @@ def __init__(self) -> None: self.rule48 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.medium_conditions] - & membership.land_form_type[ - categories.land_form.hills_with_gentle_slopes - ] - ) - | ( membership.land_cover_type[categories.land_cover.permeable_areas] & membership.land_form_type[ categories.land_form.hills_with_gentle_slopes @@ -724,12 +692,6 @@ def __init__(self) -> None: self.rule49 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.medium_conditions] - & membership.land_form_type[ - categories.land_form.steeper_hills_and_foothills - ] - ) - | ( membership.land_cover_type[categories.land_cover.permeable_areas] & membership.land_form_type[ categories.land_form.steeper_hills_and_foothills @@ -748,12 +710,6 @@ def __init__(self) -> None: self.rule50 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.medium_conditions] - & membership.land_form_type[ - categories.land_form.hills_and_outcrops_of_mountain_ranges - ] - ) - | ( membership.land_cover_type[categories.land_cover.permeable_areas] & membership.land_form_type[ categories.land_form.hills_and_outcrops_of_mountain_ranges @@ -772,43 +728,120 @@ def __init__(self) -> None: self.rule51 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.hilly] + membership.land_cover_type[categories.land_cover.suburban_weakly_impervious] & membership.land_form_type[categories.land_form.marshes_and_lowlands] ) + ) + ) + self.rule52 = ctrl.Rule( + antecedent=( + ( + membership.land_cover_type[categories.land_cover.suburban_weakly_impervious] + & membership.land_form_type[categories.land_form.flats_and_plateaus] + ) | ( - membership.land_cover_type[categories.land_cover.hilly] + membership.land_cover_type[categories.land_cover.suburban_weakly_impervious] + & membership.land_form_type[categories.land_form.flats_and_plateaus_in_combination_with_hills] + ) + ) + ) + self.rule53 = ctrl.Rule( + antecedent=( + ( + membership.land_cover_type[categories.land_cover.suburban_weakly_impervious] + & membership.land_form_type[categories.land_form.hills_with_gentle_slopes] + ) + | ( + membership.land_cover_type[categories.land_cover.suburban_weakly_impervious] + & membership.land_form_type[categories.land_form.steeper_hills_and_foothills] + ) + ) + ) + self.rule54 = ctrl.Rule( + antecedent=( + ( + membership.land_cover_type[categories.land_cover.suburban_weakly_impervious] + & membership.land_form_type[categories.land_form.hills_and_outcrops_of_mountain_ranges] + ) + ) + ) + self.rule55 = ctrl.Rule( + antecedent=( + ( + membership.land_cover_type[categories.land_cover.suburban_weakly_impervious] + & membership.land_form_type[categories.land_form.higher_hills] + ) + | ( + membership.land_cover_type[categories.land_cover.suburban_weakly_impervious] + & membership.land_form_type[categories.land_form.mountains] + ) + | ( + membership.land_cover_type[categories.land_cover.suburban_weakly_impervious] + & membership.land_form_type[categories.land_form.highest_mountains] + ) + ) + ) + self.rule56 = ctrl.Rule( + antecedent=( + ( + membership.land_cover_type[categories.land_cover.suburban_highly_impervious] + & membership.land_form_type[categories.land_form.marshes_and_lowlands] + ) + | ( + membership.land_cover_type[categories.land_cover.suburban_highly_impervious] & membership.land_form_type[categories.land_form.flats_and_plateaus] ) + ) + ) + self.rule57 = ctrl.Rule( + antecedent=( + ( + membership.land_cover_type[categories.land_cover.suburban_highly_impervious] + & membership.land_form_type[categories.land_form.flats_and_plateaus_in_combination_with_hills] + ) + ) + ) + self.rule58 = ctrl.Rule( + antecedent=( + ( + membership.land_cover_type[categories.land_cover.suburban_highly_impervious] + & membership.land_form_type[categories.land_form.hills_with_gentle_slopes] + ) | ( - membership.land_cover_type[categories.land_cover.hilly] - & membership.land_form_type[ - categories.land_form.flats_and_plateaus_in_combination_with_hills - ] + membership.land_cover_type[categories.land_cover.suburban_highly_impervious] + & membership.land_form_type[categories.land_form.steeper_hills_and_foothills] + ) + | ( + membership.land_cover_type[categories.land_cover.suburban_highly_impervious] + & membership.land_form_type[categories.land_form.hills_and_outcrops_of_mountain_ranges] ) ) ) - self.rule52 = ctrl.Rule( + self.rule59 = ctrl.Rule( antecedent=( ( - membership.land_cover_type[categories.land_cover.hilly] - & membership.land_form_type[ - categories.land_form.hills_with_gentle_slopes - ] + membership.land_cover_type[categories.land_cover.suburban_highly_impervious] + & membership.land_form_type[categories.land_form.higher_hills] ) | ( - membership.land_cover_type[categories.land_cover.hilly] - & membership.land_form_type[ - categories.land_form.steeper_hills_and_foothills - ] + membership.land_cover_type[categories.land_cover.suburban_highly_impervious] + & membership.land_form_type[categories.land_form.mountains] ) | ( - membership.land_cover_type[categories.land_cover.hilly] - & membership.land_form_type[ - categories.land_form.hills_and_outcrops_of_mountain_ranges - ] + membership.land_cover_type[categories.land_cover.suburban_highly_impervious] + & membership.land_form_type[categories.land_form.highest_mountains] ) ) ) + self.rule60 = ctrl.Rule( + antecedent=( + ( + membership.land_cover_type[categories.land_cover.urban_highly_impervious] + & membership.land_form_type[categories.land_form.flats_and_plateaus_in_combination_with_hills] + ) + + ) + ) class SlopeRule(RulesSet): @@ -817,59 +850,27 @@ class SlopeRule(RulesSet): """ def __init__(self): super().__init__() - self.rule1.consequent = membership.slope[ - categories.slope_ctgr.highest_mountains - ] - self.rule2.consequent = membership.slope[categories.slope_ctgr.mountains] - self.rule3.consequent = membership.slope[ - categories.slope_ctgr.marshes_and_lowlands - ] - self.rule4.consequent = membership.slope[ - categories.slope_ctgr.flats_and_plateaus - ] - self.rule6.consequent = membership.slope[ - categories.slope_ctgr.flats_and_plateaus_in_combination_with_hills - ] - self.rule7.consequent = membership.slope[ - categories.slope_ctgr.hills_with_gentle_slopes - ] - self.rule8.consequent = membership.slope[ - categories.slope_ctgr.steeper_hills_and_foothills - ] - self.rule9.consequent = membership.slope[ - categories.slope_ctgr.hills_and_outcrops_of_mountain_ranges - ] - self.rule10.consequent = membership.slope[categories.slope_ctgr.higher_hills] - self.rule11.consequent = membership.slope[categories.slope_ctgr.mountains] - self.rule12.consequent = membership.slope[ - categories.slope_ctgr.highest_mountains - ] - self.rule13.consequent = membership.slope[ - categories.slope_ctgr.marshes_and_lowlands - ] - self.rule14.consequent = membership.slope[ - categories.slope_ctgr.flats_and_plateaus - ] - self.rule15.consequent = membership.slope[ - categories.slope_ctgr.flats_and_plateaus_in_combination_with_hills - ] - self.rule16.consequent = membership.slope[ - categories.slope_ctgr.hills_with_gentle_slopes - ] - self.rule17.consequent = membership.slope[ - categories.slope_ctgr.steeper_hills_and_foothills - ] - self.rule18.consequent = membership.slope[ - categories.slope_ctgr.hills_and_outcrops_of_mountain_ranges - ] - self.rule19.consequent = membership.slope[categories.slope_ctgr.higher_hills] - self.rule20.consequent = membership.slope[categories.slope_ctgr.mountains] - self.rule21.consequent = membership.slope[ - categories.slope_ctgr.highest_mountains - ] - self.rule22.consequent = membership.slope[ - categories.slope_ctgr.marshes_and_lowlands - ] + self.rule1.consequent = membership.slope[categories.slope_ctgr.flats_and_plateaus] + self.rule2.consequent = membership.slope[categories.slope_ctgr.steeper_hills_and_foothills] + self.rule3.consequent = membership.slope[categories.slope_ctgr.steeper_hills_and_foothills] + self.rule4.consequent = membership.slope[categories.slope_ctgr.hills_and_outcrops_of_mountain_ranges] + self.rule6.consequent = membership.slope[categories.slope_ctgr.higher_hills] + self.rule7.consequent = membership.slope[categories.slope_ctgr.flats_and_plateaus] + self.rule8.consequent = membership.slope[categories.slope_ctgr.flats_and_plateaus_in_combination_with_hills] + self.rule9.consequent = membership.slope[categories.slope_ctgr.hills_with_gentle_slopes] + self.rule10.consequent = membership.slope[categories.slope_ctgr.steeper_hills_and_foothills] + self.rule11.consequent = membership.slope[categories.slope_ctgr.hills_and_outcrops_of_mountain_ranges] + self.rule12.consequent = membership.slope[categories.slope_ctgr.higher_hills] + self.rule13.consequent = membership.slope[categories.slope_ctgr.mountains] + self.rule14.consequent = membership.slope[categories.slope_ctgr.marshes_and_lowlands] + self.rule15.consequent = membership.slope[categories.slope_ctgr.flats_and_plateaus_in_combination_with_hills] + self.rule16.consequent = membership.slope[categories.slope_ctgr.steeper_hills_and_foothills] + self.rule17.consequent = membership.slope[categories.slope_ctgr.higher_hills] + self.rule18.consequent = membership.slope[categories.slope_ctgr.marshes_and_lowlands] + self.rule19.consequent = membership.slope[categories.slope_ctgr.flats_and_plateaus_in_combination_with_hills] + self.rule20.consequent = membership.slope[categories.slope_ctgr.hills_and_outcrops_of_mountain_ranges] + self.rule21.consequent = membership.slope[categories.slope_ctgr.marshes_and_lowlands] + self.rule22.consequent = membership.slope[categories.slope_ctgr.marshes_and_lowlands] self.rule23.consequent = membership.slope[ categories.slope_ctgr.flats_and_plateaus ] @@ -887,9 +888,7 @@ def __init__(self): ] self.rule28.consequent = membership.slope[categories.slope_ctgr.higher_hills] self.rule29.consequent = membership.slope[categories.slope_ctgr.mountains] - self.rule30.consequent = membership.slope[ - categories.slope_ctgr.highest_mountains - ] + self.rule30.consequent = membership.slope[categories.slope_ctgr.highest_mountains] self.rule31.consequent = membership.slope[ categories.slope_ctgr.flats_and_plateaus ] @@ -923,8 +922,8 @@ def __init__(self): self.rule41.consequent = membership.slope[ categories.slope_ctgr.steeper_hills_and_foothills ] - self.rule42.consequent = membership.slope[categories.slope_ctgr.higher_hills] - self.rule43.consequent = membership.slope[categories.slope_ctgr.mountains] + self.rule42.consequent = membership.slope[categories.slope_ctgr.flats_and_plateaus] + self.rule43.consequent = membership.slope[categories.slope_ctgr.hills_with_gentle_slopes] self.rule44.consequent = membership.slope[categories.slope_ctgr.mountains] self.rule45.consequent = membership.slope[ categories.slope_ctgr.marshes_and_lowlands @@ -944,12 +943,16 @@ def __init__(self): self.rule50.consequent = membership.slope[ categories.slope_ctgr.hills_and_outcrops_of_mountain_ranges ] - self.rule51.consequent = membership.slope[ - categories.slope_ctgr.steeper_hills_and_foothills - ] - self.rule52.consequent = membership.slope[ - categories.slope_ctgr.hills_and_outcrops_of_mountain_ranges - ] + self.rule51.consequent = membership.slope[categories.slope_ctgr.marshes_and_lowlands] + self.rule52.consequent = membership.slope[categories.slope_ctgr.flats_and_plateaus] + self.rule53.consequent = membership.slope[categories.slope_ctgr.hills_with_gentle_slopes] + self.rule54.consequent = membership.slope[categories.slope_ctgr.hills_and_outcrops_of_mountain_ranges] + self.rule55.consequent = membership.slope[categories.slope_ctgr.higher_hills] + self.rule56.consequent = membership.slope[categories.slope_ctgr.marshes_and_lowlands] + self.rule57.consequent = membership.slope[categories.slope_ctgr.flats_and_plateaus_in_combination_with_hills] + self.rule58.consequent = membership.slope[categories.slope_ctgr.hills_with_gentle_slopes] + self.rule59.consequent = membership.slope[categories.slope_ctgr.higher_hills] + self.rule60.consequent = membership.slope[categories.slope_ctgr.flats_and_plateaus_in_combination_with_hills] class ImperviousRule(RulesSet): @@ -958,57 +961,45 @@ class ImperviousRule(RulesSet): """ def __init__(self): super().__init__() - self.rule1.consequent = membership.impervious[ - categories.impervious_ctgr.mountains - ] - self.rule2.consequent = membership.impervious[ - categories.impervious_ctgr.mountains - ] - self.rule3.consequent = membership.impervious[categories.impervious_ctgr.urban] - self.rule4.consequent = membership.impervious[categories.impervious_ctgr.urban] - self.rule6.consequent = membership.impervious[categories.impervious_ctgr.urban] - self.rule7.consequent = membership.impervious[categories.impervious_ctgr.urban] - self.rule8.consequent = membership.impervious[categories.impervious_ctgr.urban] - self.rule9.consequent = membership.impervious[categories.impervious_ctgr.urban] - self.rule10.consequent = membership.impervious[categories.impervious_ctgr.urban] - self.rule11.consequent = membership.impervious[categories.impervious_ctgr.urban] - self.rule12.consequent = membership.impervious[categories.impervious_ctgr.urban] - self.rule13.consequent = membership.impervious[ - categories.impervious_ctgr.suburban - ] - self.rule14.consequent = membership.impervious[ - categories.impervious_ctgr.suburban - ] - self.rule15.consequent = membership.impervious[ - categories.impervious_ctgr.suburban - ] - self.rule16.consequent = membership.impervious[ - categories.impervious_ctgr.suburban - ] - self.rule17.consequent = membership.impervious[ - categories.impervious_ctgr.suburban + self.rule1.consequent = membership.impervious[categories.impervious_ctgr.mountains_vegetated] + self.rule2.consequent = membership.impervious[categories.impervious_ctgr.mountains_vegetated] + self.rule3.consequent = membership.impervious[categories.impervious_ctgr.mountains_vegetated] + self.rule4.consequent = membership.impervious[categories.impervious_ctgr.mountains_vegetated] + self.rule6.consequent = membership.impervious[categories.impervious_ctgr.mountains_rocky] + self.rule7.consequent = membership.impervious[categories.impervious_ctgr.mountains_rocky] + self.rule8.consequent = membership.impervious[categories.impervious_ctgr.mountains_rocky] + self.rule9.consequent = membership.impervious[categories.impervious_ctgr.mountains_rocky] + self.rule10.consequent = membership.impervious[categories.impervious_ctgr.mountains_rocky] + self.rule11.consequent = membership.impervious[categories.impervious_ctgr.mountains_rocky] + self.rule12.consequent = membership.impervious[categories.impervious_ctgr.mountains_rocky] + self.rule13.consequent = membership.impervious[categories.impervious_ctgr.mountains_rocky] + self.rule14.consequent = membership.impervious[categories.impervious_ctgr.urban_weakly_impervious] + self.rule15.consequent = membership.impervious[categories.impervious_ctgr.urban_weakly_impervious] + self.rule16.consequent = membership.impervious[categories.impervious_ctgr.urban_highly_impervious] + self.rule17.consequent = membership.impervious[categories.impervious_ctgr.urban_moderately_impervious] + self.rule18.consequent = membership.impervious[categories.impervious_ctgr.urban_moderately_impervious] + self.rule19.consequent = membership.impervious[categories.impervious_ctgr.urban_moderately_impervious] + self.rule20.consequent = membership.impervious[categories.impervious_ctgr.urban_moderately_impervious] + self.rule21.consequent = membership.impervious[categories.impervious_ctgr.urban_highly_impervious] + self.rule22.consequent = membership.impervious[categories.impervious_ctgr.rural] + self.rule23.consequent = membership.impervious[ + categories.impervious_ctgr.rural ] - self.rule18.consequent = membership.impervious[ - categories.impervious_ctgr.suburban + self.rule24.consequent = membership.impervious[categories.impervious_ctgr.rural] + self.rule25.consequent = membership.impervious[categories.impervious_ctgr.rural] + self.rule26.consequent = membership.impervious[ categories.impervious_ctgr.rural ] + self.rule27.consequent = membership.impervious[ + categories.impervious_ctgr.rural ] - self.rule19.consequent = membership.impervious[ - categories.impervious_ctgr.suburban + self.rule28.consequent = membership.impervious[ + categories.impervious_ctgr.rural ] - self.rule20.consequent = membership.impervious[ - categories.impervious_ctgr.suburban + self.rule29.consequent = membership.impervious[ + categories.impervious_ctgr.rural ] - self.rule21.consequent = membership.impervious[ - categories.impervious_ctgr.suburban + self.rule30.consequent = membership.impervious[ + categories.impervious_ctgr.rural ] - self.rule22.consequent = membership.impervious[categories.impervious_ctgr.rural] - self.rule23.consequent = membership.impervious[categories.impervious_ctgr.rural] - self.rule24.consequent = membership.impervious[categories.impervious_ctgr.rural] - self.rule25.consequent = membership.impervious[categories.impervious_ctgr.rural] - self.rule26.consequent = membership.impervious[categories.impervious_ctgr.rural] - self.rule27.consequent = membership.impervious[categories.impervious_ctgr.rural] - self.rule28.consequent = membership.impervious[categories.impervious_ctgr.rural] - self.rule29.consequent = membership.impervious[categories.impervious_ctgr.rural] - self.rule30.consequent = membership.impervious[categories.impervious_ctgr.rural] self.rule31.consequent = membership.impervious[ categories.impervious_ctgr.forests ] @@ -1042,13 +1033,9 @@ def __init__(self): self.rule41.consequent = membership.impervious[ categories.impervious_ctgr.arable ] - self.rule42.consequent = membership.impervious[categories.impervious_ctgr.hilly] - self.rule43.consequent = membership.impervious[ - categories.impervious_ctgr.mountains - ] - self.rule44.consequent = membership.impervious[ - categories.impervious_ctgr.mountains - ] + self.rule42.consequent = membership.impervious[categories.impervious_ctgr.urban_highly_impervious] + self.rule43.consequent = membership.impervious[categories.impervious_ctgr.urban_highly_impervious] + self.rule44.consequent = membership.impervious[categories.impervious_ctgr.urban_highly_impervious] self.rule45.consequent = membership.impervious[ categories.impervious_ctgr.marshes ] @@ -1067,13 +1054,16 @@ def __init__(self): self.rule50.consequent = membership.impervious[ categories.impervious_ctgr.arable ] - self.rule51.consequent = membership.impervious[ - categories.impervious_ctgr.meadows - ] - self.rule52.consequent = membership.impervious[ - categories.impervious_ctgr.forests - ] - + self.rule51.consequent = membership.impervious[categories.impervious_ctgr.suburban_weakly_impervious] + self.rule52.consequent = membership.impervious[categories.impervious_ctgr.suburban_weakly_impervious] + self.rule53.consequent = membership.impervious[categories.impervious_ctgr.suburban_weakly_impervious] + self.rule54.consequent = membership.impervious[categories.impervious_ctgr.suburban_weakly_impervious] + self.rule55.consequent = membership.impervious[categories.impervious_ctgr.suburban_weakly_impervious] + self.rule56.consequent = membership.impervious[categories.impervious_ctgr.suburban_highly_impervious] + self.rule57.consequent = membership.impervious[categories.impervious_ctgr.suburban_highly_impervious] + self.rule58.consequent = membership.impervious[categories.impervious_ctgr.suburban_highly_impervious] + self.rule59.consequent = membership.impervious[categories.impervious_ctgr.suburban_highly_impervious] + self.rule60.consequent = membership.impervious[categories.impervious_ctgr.urban_highly_impervious] class CatchmentsRule(RulesSet): """ @@ -1081,49 +1071,28 @@ class CatchmentsRule(RulesSet): """ def __init__(self): super().__init__() - self.rule1.consequent = membership.catchment[ - categories.catchment_ctgr.mountains - ] - self.rule2.consequent = membership.catchment[ - categories.catchment_ctgr.mountains - ] - self.rule3.consequent = membership.catchment[categories.catchment_ctgr.urban] - self.rule4.consequent = membership.catchment[categories.catchment_ctgr.urban] - self.rule6.consequent = membership.catchment[categories.catchment_ctgr.urban] - self.rule7.consequent = membership.catchment[categories.catchment_ctgr.urban] - self.rule8.consequent = membership.catchment[categories.catchment_ctgr.urban] - self.rule9.consequent = membership.catchment[categories.catchment_ctgr.urban] - self.rule10.consequent = membership.catchment[categories.catchment_ctgr.urban] - self.rule11.consequent = membership.catchment[categories.catchment_ctgr.urban] - self.rule12.consequent = membership.catchment[categories.catchment_ctgr.urban] - self.rule13.consequent = membership.catchment[ - categories.catchment_ctgr.suburban - ] - self.rule14.consequent = membership.catchment[ - categories.catchment_ctgr.suburban - ] - self.rule15.consequent = membership.catchment[ - categories.catchment_ctgr.suburban - ] - self.rule16.consequent = membership.catchment[ - categories.catchment_ctgr.suburban - ] - self.rule17.consequent = membership.catchment[ - categories.catchment_ctgr.suburban - ] - self.rule18.consequent = membership.catchment[ - categories.catchment_ctgr.suburban - ] - self.rule19.consequent = membership.catchment[ - categories.catchment_ctgr.suburban - ] - self.rule20.consequent = membership.catchment[ - categories.catchment_ctgr.suburban - ] - self.rule21.consequent = membership.catchment[ - categories.catchment_ctgr.suburban - ] + self.rule1.consequent = membership.catchment[categories.catchment_ctgr.meadows] + self.rule2.consequent = membership.catchment[categories.catchment_ctgr.mountains] + self.rule3.consequent = membership.catchment[categories.catchment_ctgr.mountains] + self.rule4.consequent = membership.catchment[categories.catchment_ctgr.mountains] + self.rule6.consequent = membership.catchment[categories.catchment_ctgr.mountains] + self.rule7.consequent = membership.catchment[categories.catchment_ctgr.mountains] + self.rule8.consequent = membership.catchment[categories.catchment_ctgr.mountains] + self.rule9.consequent = membership.catchment[categories.catchment_ctgr.mountains] + self.rule10.consequent = membership.catchment[categories.catchment_ctgr.mountains] + self.rule11.consequent = membership.catchment[categories.catchment_ctgr.mountains] + self.rule12.consequent = membership.catchment[categories.catchment_ctgr.mountains] + self.rule13.consequent = membership.catchment[categories.catchment_ctgr.mountains] + self.rule14.consequent = membership.catchment[categories.catchment_ctgr.urban] + self.rule15.consequent = membership.catchment[categories.catchment_ctgr.urban] + self.rule16.consequent = membership.catchment[categories.catchment_ctgr.urban] + self.rule17.consequent = membership.catchment[categories.catchment_ctgr.urban] + self.rule18.consequent = membership.catchment[categories.catchment_ctgr.urban] + self.rule19.consequent = membership.catchment[categories.catchment_ctgr.urban] + self.rule20.consequent = membership.catchment[categories.catchment_ctgr.urban] + self.rule21.consequent = membership.catchment[categories.catchment_ctgr.urban] self.rule22.consequent = membership.catchment[categories.catchment_ctgr.rural] + self.rule23.consequent = membership.catchment[categories.catchment_ctgr.rural] self.rule24.consequent = membership.catchment[categories.catchment_ctgr.rural] self.rule25.consequent = membership.catchment[categories.catchment_ctgr.rural] @@ -1146,20 +1115,24 @@ def __init__(self): self.rule42.consequent = membership.catchment[ categories.catchment_ctgr.mountains ] - self.rule43.consequent = membership.catchment[ - categories.catchment_ctgr.mountains - ] - self.rule44.consequent = membership.catchment[ - categories.catchment_ctgr.mountains - ] + self.rule43.consequent = membership.catchment[categories.catchment_ctgr.urban] + self.rule44.consequent = membership.catchment[categories.catchment_ctgr.urban] self.rule45.consequent = membership.catchment[categories.catchment_ctgr.meadows] self.rule46.consequent = membership.catchment[categories.catchment_ctgr.meadows] self.rule47.consequent = membership.catchment[categories.catchment_ctgr.meadows] self.rule48.consequent = membership.catchment[categories.catchment_ctgr.arable] self.rule49.consequent = membership.catchment[categories.catchment_ctgr.arable] self.rule50.consequent = membership.catchment[categories.catchment_ctgr.arable] - self.rule51.consequent = membership.catchment[categories.catchment_ctgr.meadows] - self.rule52.consequent = membership.catchment[categories.catchment_ctgr.forests] + self.rule51.consequent = membership.catchment[categories.catchment_ctgr.suburban] + self.rule52.consequent = membership.catchment[categories.catchment_ctgr.suburban] + self.rule53.consequent = membership.catchment[categories.catchment_ctgr.suburban] + self.rule54.consequent = membership.catchment[categories.catchment_ctgr.suburban] + self.rule55.consequent = membership.catchment[categories.catchment_ctgr.suburban] + self.rule56.consequent = membership.catchment[categories.catchment_ctgr.suburban] + self.rule57.consequent = membership.catchment[categories.catchment_ctgr.suburban] + self.rule58.consequent = membership.catchment[categories.catchment_ctgr.suburban] + self.rule59.consequent = membership.catchment[categories.catchment_ctgr.suburban] + self.rule60.consequent = membership.catchment[categories.catchment_ctgr.urban] slope_rules = [rule for rule in vars(SlopeRule()).values()] diff --git a/rcg/fuzzy/test_fuzzy/test_categories.py b/rcg/fuzzy/test_fuzzy/test_categories.py index a8e7420..0b1ef6a 100644 --- a/rcg/fuzzy/test_fuzzy/test_categories.py +++ b/rcg/fuzzy/test_fuzzy/test_categories.py @@ -61,7 +61,7 @@ def tearDown(self) -> None: del self.categories -class TestLandUse(unittest.TestCase): +class TestLandForm(unittest.TestCase): def setUp(self): self.land_form = LandForm() @@ -130,13 +130,10 @@ def test_highest_mountains(self): self.assertEqual(self.land_form.highest_mountains, "highest_mountains") -class TestLandForm(unittest.TestCase): +class TestLandCover(unittest.TestCase): def setUp(self) -> None: self.land_cover = LandCover() - def test_medium_conditions(self): - self.assertEqual(self.land_cover.medium_conditions, "medium_conditions") - def test_permeable_areas(self): self.assertEqual(self.land_cover.permeable_areas, "permeable_areas") @@ -145,20 +142,26 @@ def test_permeable_terrain_on_plains(self): self.land_cover.permeable_terrain_on_plains, "permeable_terrain_on_plains" ) - def test_hilly(self): - self.assertEqual(self.land_cover.hilly, "hilly") + def test_mountains_vegetated(self): + self.assertEqual(self.land_cover.mountains_vegetated, "mountains_vegetated") - def test_mountains(self): - self.assertEqual(self.land_cover.mountains, "mountains") + def test_mountains_rocky(self): + self.assertEqual(self.land_cover.mountains_rocky, "mountains_rocky") - def test_bare_rocky_slopes(self): - self.assertEqual(self.land_cover.bare_rocky_slopes, "bare_rocky_slopes") + def test_urban_weakly_impervious(self): + self.assertEqual(self.land_cover.urban_weakly_impervious, "urban_weakly_impervious") - def test_urban(self): - self.assertEqual(self.land_cover.urban, "urban") + def test_urban_moderately_impervious(self): + self.assertEqual(self.land_cover.urban_moderately_impervious, "urban_moderately_impervious") - def test_suburban(self): - self.assertEqual(self.land_cover.suburban, "suburban") + def test_urban_highly_impervious(self): + self.assertEqual(self.land_cover.urban_highly_impervious, "urban_highly_impervious") + + def test_suburban_weakly_impervious(self): + self.assertEqual(self.land_cover.suburban_weakly_impervious, "suburban_weakly_impervious") + + def test_suburban_highly_impervious(self): + self.assertEqual(self.land_cover.suburban_highly_impervious, "suburban_highly_impervious") def test_rural(self): self.assertEqual(self.land_cover.rural, "rural") @@ -237,17 +240,26 @@ def test_forests_attribute(self): def test_rural_attribute(self): self.assertEqual(self.impervious.rural, "rural") - def test_suburban_attribute(self): - self.assertEqual(self.impervious.suburban, "suburban") + def test_suburban_weakly_imperviousattribute(self): + self.assertEqual(self.impervious.suburban_weakly_impervious, "suburban_weakly_impervious") + + def test_suburban_highly_impervious_attribute(self): + self.assertEqual(self.impervious.suburban_highly_impervious, "suburban_highly_impervious") + + def test_urban_weakly_impervious_attribute(self): + self.assertEqual(self.impervious.urban_weakly_impervious, "urban_weakly_impervious") + + def test_urban_moderately_impervious_attribute(self): + self.assertEqual(self.impervious.urban_moderately_impervious, "urban_moderately_impervious") - def test_urban_attribute(self): - self.assertEqual(self.impervious.urban, "urban") + def test_urban_highly_impervious_attribute(self): + self.assertEqual(self.impervious.urban_highly_impervious, "urban_highly_impervious") - def test_hilly_attribute(self): - self.assertEqual(self.impervious.hilly, "hilly") + def test_mountains_rocky_attribute(self): + self.assertEqual(self.impervious.mountains_rocky, "mountains_rocky") - def test_mountains_attribute(self): - self.assertEqual(self.impervious.mountains, "mountains") + def test_mountains_vegetated_attribute(self): + self.assertEqual(self.impervious.mountains_vegetated, "mountains_vegetated") class TestCatchments(unittest.TestCase): diff --git a/rcg/fuzzy/test_fuzzy/test_memberships.py b/rcg/fuzzy/test_fuzzy/test_memberships.py index 723a7d9..7eb5b5d 100644 --- a/rcg/fuzzy/test_fuzzy/test_memberships.py +++ b/rcg/fuzzy/test_fuzzy/test_memberships.py @@ -9,7 +9,7 @@ class TestMemberships(unittest.TestCase): def setUp(self): self.memberships = Memberships() self.memberships.populate_land_use() - self.memberships.populate_land_form() + self.memberships.populate_land_cover() self.memberships.populate_slope() self.memberships.populate_impervious() self.memberships.populate_catchment() diff --git a/rcg/inp_manage/inp.py b/rcg/inp_manage/inp.py index 1a9c78b..fbf3693 100644 --- a/rcg/inp_manage/inp.py +++ b/rcg/inp_manage/inp.py @@ -1,9 +1,10 @@ import math -import swmmio -import pandas as pd +from typing import List, Optional, Tuple + import numpy as np +import pandas as pd +import swmmio -from typing import List, Tuple, Optional from rcg.fuzzy.engine import Prototype from rcg.fuzzy.categories import LandForm, LandCover from swmmio.utils.modify_model import replace_inp_section @@ -102,22 +103,13 @@ def _get_land_form(): str The land use type for the subcatchment entered by the user. """ - land_form_options = [ - "marshes_and_lowlands", - "flats_and_plateaus", - "flats_and_plateaus_in_combination_with_hills", - "hills_with_gentle_slopes", - "steeper_hills_and_foothills", - "hills_and_outcrops_of_mountain_ranges", - "higher_hills", - "mountains", - "highest_mountains", - ] + land_form_options = LandForm.get_all_categories() + land_form = "" while land_form not in land_form_options: land_form = input( - "Enter the land use type (choose one):\n{}\n:".format( - "\n".join(land_form_options) + "Enter the land use type (choose one):\n\t{}\n:".format( + "\n\t".join(land_form_options) ) ) return land_form @@ -136,26 +128,12 @@ def _get_land_cover() -> str: str The land cover type for the subcatchment entered by the user. """ - land_cover_options = [ - "medium_conditions", - "permeable_areas", - "permeable_terrain_on_plains", - "hilly", - "mountains", - "bare_rocky_slopes", - "urban", - "suburban", - "rural", - "forests", - "meadows", - "arable", - "marshes", - ] + land_cover_options = LandCover.get_all_categories() land_cover = "" while land_cover not in land_cover_options: land_cover = input( - "Enter the land cover type (choose one):\n{}\n:".format( - "\n".join(land_cover_options) + "Enter the land cover type (choose one):\n\t{}\n:".format( + "\n\t".join(land_cover_options) ) ) return land_cover @@ -177,7 +155,6 @@ def _get_subcatchment_values(self) -> Tuple[float, Prototype]: area = self._get_area() land_form = self._get_land_form() land_cover = self._get_land_cover() - prototype_result = Prototype( land_form=getattr(LandForm, land_form), land_cover=getattr(LandCover, land_cover), @@ -261,7 +238,7 @@ def _add_raingage(self) -> None: raingage = pd.DataFrame( data={ "RainType": ["INTENSITY"], - "TimeIntrvl": ["1:00"], + "TimeIntrvl": ["0:01"], "SnowCatch": ["1.0"], "DataSource": ["TIMESERIES"], "DataSourceName": [self._get_timeseries()], @@ -287,7 +264,7 @@ def _get_raingage(self) -> str: self._add_raingage() return self.model.inp.raingages.index[0] - def _get_outlet(self) -> Optional[str]: + def _get_outlet(self, subcatchment_id: str) -> Optional[str]: """ Get the name of the first junction in the SWMM model's input data. @@ -298,11 +275,16 @@ def _get_outlet(self) -> Optional[str]: Optional[str] The name of the first junction in the model's input data, or None if there are no junctions. """ - if len(self.model.inp.junctions) == 0: - return None - return self.model.inp.junctions.index[0] + if len(self.model.inp.outfalls) != 0: + return self.model.inp.outfalls.index[-1] + else: + if len(self.model.inp.junctions) != 0: + return self.model.inp.junctions.index[-1] + return subcatchment_id - def _add_subcatchment(self, subcatchment_id: str, catchment_values: Tuple[float, Prototype]) -> None: + def _add_subcatchment( + self, subcatchment_id: str, catchment_values: Tuple[float, Prototype] + ) -> None: """ Add a new subcatchment to the SWMM model's input data. @@ -320,20 +302,19 @@ def _add_subcatchment(self, subcatchment_id: str, catchment_values: Tuple[float, ------- None """ - if self._get_outlet() is None: + if self._get_outlet(subcatchment_id) is None: outlet = subcatchment_id else: - outlet = self._get_outlet() + outlet = self._get_outlet(subcatchment_id) self.model.inp.subcatchments.loc[subcatchment_id] = { "Name": subcatchment_id, "Raingage": self._get_raingage(), "Outlet": outlet, "Area": catchment_values[0], - "PercImperv": catchment_values[1].impervious_result, - # "Width": math.sqrt((float(catchment_values[0]) * 10_000)), - "Width": (float(catchment_values[0]) * 10_000) / (math.sqrt(float(catchment_values[0]) * 10_000) * math.sqrt(2)), - "PercSlope": catchment_values[1].slope_result, + "PercImperv": round(catchment_values[1].impervious_result, 2), + "Width": round(math.sqrt((float(catchment_values[0]) * 10_000)), 2), + "PercSlope": round(catchment_values[1].slope_result, 2), "CurbLength": 0, } replace_inp_section( @@ -369,13 +350,13 @@ def _add_subarea(self, subcatchment_id: str, prototype: Prototype) -> None: "mountains": (0.013, 0.05), } map_depression = { - "urban": (0.05, 0.20, 90), - "suburban": (0.05, 0.20, 80), - "rural": (0.05, 0.20, 70), + "urban": (0.05, 0.20, 50), + "suburban": (0.05, 0.20, 40), + "rural": (0.05, 0.20, 35), "forests": (0.05, 0.30, 5), "meadows": (0.05, 0.20, 10), "arable": (0.05, 0.20, 10), - "mountains": (0.05, 0.20, 80), + "mountains": (0.05, 0.20, 10), } populate_key = Prototype.get_populate(prototype.catchment_result) @@ -390,57 +371,28 @@ def _add_subarea(self, subcatchment_id: str, prototype: Prototype) -> None: } replace_inp_section(self.model.inp.path, "[SUBAREAS]", self.model.inp.subareas) - def _get_existing_coordinates(self) -> List[Tuple[float, float]]: + def _add_coords(self, subcatchment_id: str, area: float) -> None: """ - Retrieves the existing coordinates for the last subcatchment in the SWMM model's input data. - - Returns - ------- - List[Tuple[float, float]] - A list of tuples containing the X and Y coordinates of the existing polygons. - """ - if len(self.model.inp.polygons) < 4: - return [ - (0, 0), - (0, 5), - (5, 5), - (5, 0), - ] - else: - return [ - (self.model.inp.polygons["X"].iloc[-1], self.model.inp.polygons["Y"].iloc[-1]), - (self.model.inp.polygons["X"].iloc[-2], self.model.inp.polygons["Y"].iloc[-2]), - (self.model.inp.polygons["X"].iloc[-3], self.model.inp.polygons["Y"].iloc[-3]), - (self.model.inp.polygons["X"].iloc[-4], self.model.inp.polygons["Y"].iloc[-4]), - ] - - def _add_coords(self, subcatchment_id: str) -> None: - """ - Adds coordinates for a subcatchment to the SWMM model's input data. - - If existing polygons don't exist, it generates new coordinates for the subcatchment. - The new coordinates are based on the existing polygons or created with a default offset. + Add coordinates for a square-shaped subcatchment based on the given area. Parameters ---------- - subcatchment_id : str - The unique ID of the subcatchment to add the coordinates to. - - Returns - ------- - None - """ - exist = self._get_existing_coordinates() + subcatchment_id : str + The unique ID of the subcatchment to add the coordinates to. + area : float + The area of the subcatchment [ha]. + """ + side_length = math.sqrt(area * 10_000) + if len(self.model.inp.polygons) == 0: + base_x, base_y = 0, 0 + else: + base_x = self.model.inp.polygons["X"].iloc[-1] + base_y = self.model.inp.polygons["Y"].iloc[-1] coords = pd.DataFrame( data={ - "X": [exist[0][0], exist[1][0], exist[2][0], exist[3][0]], - "Y": [ - exist[0][1] - 5, - exist[1][1] - 5, - exist[2][1] - 5, - exist[3][1] - 5, - ], + "X": [base_x, base_x + side_length, base_x + side_length, base_x], + "Y": [base_y, base_y, base_y - side_length, base_y - side_length], }, index=[subcatchment_id for _ in range(4)], ) @@ -510,10 +462,12 @@ def add_subcatchment(self) -> None: catchment_values = self._get_subcatchment_values() self._add_subcatchment(subcatchment_id, catchment_values) self._add_subarea(subcatchment_id, catchment_values[1]) - self._add_coords(subcatchment_id) + self._add_coords(subcatchment_id, catchment_values[0]) self._add_infiltration(subcatchment_id) - def add_subcatchment_form_gui(self, area: float, land_form: str, land_cover: str) -> None: + def add_subcatchment_form_gui( + self, area: float, land_form: str, land_cover: str + ) -> None: """ Adds a new subcatchment to the project, including its subarea, coordinates, and infiltration parameters. @@ -529,5 +483,6 @@ def add_subcatchment_form_gui(self, area: float, land_form: str, land_cover: str catchment_values = (area, prototype_result) self._add_subcatchment(subcatchment_id, catchment_values) self._add_subarea(subcatchment_id, catchment_values[1]) - self._add_coords(subcatchment_id) - self._add_infiltration(subcatchment_id) \ No newline at end of file + self._add_coords(subcatchment_id, area) + self._add_infiltration(subcatchment_id) + self._add_infiltration(subcatchment_id) diff --git a/rcg/runner.py b/rcg/runner.py index f1da2ae..d0071ed 100644 --- a/rcg/runner.py +++ b/rcg/runner.py @@ -51,7 +51,6 @@ def add_multiple_subcatchments(model): to which the subcatchments will be added. """ model.add_subcatchment() - user_input = input("Do you want to add another subcatchment? (y/n): ").lower() if user_input == "y": add_multiple_subcatchments(model) diff --git a/setup.py b/setup.py index 6d24df6..0bf7e2a 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ with codecs.open(os.path.join(here, "README.md"), encoding="utf-8") as fh: long_description = "\n" + fh.read() -VERSION = "0.0.11" +VERSION = "0.1.0" DESCRIPTION = "Rapid catchment generator - is a tool for rapid prototyping of a hydraulic model catchments that can be read and edited with SWMM." # Setting up