Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add Crime Layer to Map, merge changes from master into dev #182

Merged
merged 14 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
.github/
*.csv
*.pyc
*.xlsx
*.xlsx
assets/datasets/oil_well.geojson
63 changes: 63 additions & 0 deletions assets/additional_layer_popups.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
window.myNamespace = Object.assign({}, window.myNamespace, {
mySubNamespace: {
drawOilIcon: function(feature, latlng) {
const OilIcon = L.icon({
iconUrl: '/assets/oil_derrick_icon.png',
iconSize: [20, 20] // Adjust the size as needed
});
var marker = L.marker(latlng, {icon: OilIcon});

// Check if the required properties exist and create the popup content
if (feature.properties) {
var popupContent = '<h4>Oil/Gas Well Info</h4>';
popupContent += 'API Number: ' + (feature.properties.API || 'N/A') + '<br>';
popupContent += 'Lease Name: ' + (feature.properties.LeaseName || 'N/A') + '<br>';
popupContent += 'Start Date: ' + (feature.properties.SpudDate || 'N/A') + '<br>';
popupContent += 'Well Operator: ' + (feature.properties.OperatorNa || 'N/A') + '<br>';
// Check the Well Status and set the color
var wellStatus = feature.properties.WellStatus || 'N/A';
var wellStatusColor = 'black';
if (wellStatus === 'Plugged') {
wellStatusColor = 'green';
}
else if (wellStatus === 'Active') {
wellStatusColor = 'red';
}
else if (wellStatus === 'Idle') {
wellStatusColor = '#DAA520'; // Dark yellow
}
popupContent += 'Well Status: <span style="color:' + wellStatusColor + ';">' + wellStatus + '</span><br>';
popupContent += 'Well Type: ' + (feature.properties.WellTypeLa || 'N/A') + '<br>';

marker.bindPopup(popupContent);
}

return marker;
},
drawCrimeIcon: function(feature, latlng) {
const CrimeIcon = L.icon({
iconUrl: '/assets/crime_icon.png',
iconSize: [20, 20] // Adjust the size as needed
});
var marker = L.marker(latlng, {icon: CrimeIcon});

// Check if the required properties exist and create the popup content
if (feature.properties) {
var popupContent = '<h4>Crime Info</h4>';
popupContent += 'DR No: ' + (feature.properties.dr_no || 'N/A') + '<br>';
popupContent += 'Date Occurred: ' + (feature.properties.date_occ || 'N/A') + '<br>';
popupContent += 'Time Occurred: ' + (feature.properties.time_occ || 'N/A') + '<br>';
popupContent += 'Crime Code Description: ' + (feature.properties.crm_cd_desc || 'N/A') + '<br>';
popupContent += 'Victim Age: ' + (feature.properties.vict_age || 'N/A') + '<br>';
popupContent += 'Victim Sex: ' + (feature.properties.vict_sex || 'N/A') + '<br>';
popupContent += 'Premise Description: ' + (feature.properties.premis_desc || 'N/A') + '<br>';
popupContent += 'Weapon Description: ' + (feature.properties.weapon_desc || 'N/A') + '<br>';
popupContent += 'Status Description: ' + (feature.properties.status_desc || 'N/A') + '<br>';

marker.bindPopup(popupContent);
}

return marker;
}
}
});
Binary file added assets/crime_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 0 additions & 3 deletions assets/more_info.css

This file was deleted.

13 changes: 0 additions & 13 deletions assets/more_info.js

This file was deleted.

38 changes: 0 additions & 38 deletions assets/oil_popup.js

This file was deleted.

46 changes: 44 additions & 2 deletions functions/geojson_processing_utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from typing import Dict, List
from typing import Dict, List, Any
import json
import requests
import uuid

def optimize_geojson(input_filepath: str, output_filepath: str, fields_to_keep: List[str]) -> None:
"""
Expand All @@ -26,4 +28,44 @@ def optimize_geojson(input_filepath: str, output_filepath: str, fields_to_keep:

# Save the optimized GeoJSON data to the output file
with open(output_filepath, 'w') as f:
json.dump(data, f)
json.dump(data, f)

def fetch_json_data(url: str) -> Any:
"""
Fetches JSON data from a URL.

Args:
url (str): The URL to fetch the JSON data from.

Returns:
Any: The fetched JSON data.
"""
response = requests.get(url)
response.raise_for_status() # Raise an exception if the request was unsuccessful
return response.json()

def convert_to_geojson(data: List[Dict[str, Any]]) -> Dict[str, Any]:
"""
Converts a list of dictionaries to GeoJSON format.

Args:
data (List[Dict[str, Any]]): The data to convert.

Returns:
Dict[str, Any]: The data in GeoJSON format.
"""
return {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'id': str(uuid.uuid4()), # Assign a unique id to each feature
'geometry': {
'type': 'Point',
'coordinates': [float(item['lon']), float(item['lat'])]
},
'properties': item,
}
for item in data
],
}
61 changes: 51 additions & 10 deletions pages/components.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from dash import html, dcc
from dash_extensions.javascript import Namespace
from datetime import date
from functions.geojson_processing_utils import fetch_json_data, convert_to_geojson
from typing import Any, ClassVar, Optional
import dash_bootstrap_components as dbc
import dash_leaflet as dl
Expand All @@ -16,8 +17,8 @@ def create_toggle_button(index, page_type, initial_label="Hide"):
style={'display': 'inline-block'}
)

# Create a bass class for the oil well GeoJSON data
# The oil well GeoJSON data is used on both the Lease and Buy pages, so both classes inherit from this base class
# Create a bass class for the additional layers
# The additional layers are used in both the Lease and Sale pages, so we can use inheritance to avoid code duplication
class BaseClass:
oil_well_data: ClassVar[Optional[Any]] = None

Expand Down Expand Up @@ -58,9 +59,42 @@ def create_oil_well_geojson_layer(cls) -> dl.GeoJSON:
'minZoom': 3,
},
options=dict(
pointToLayer=ns("drawCustomIcon")
pointToLayer=ns("drawOilIcon")
)
)

@classmethod
def create_new_geojson_layer(cls, url: str) -> dl.GeoJSON:
"""
Creates a new Dash Leaflet GeoJSON layer with data fetched from a URL. If the data is not in GeoJSON format, it is converted.

Args:
url (str): The URL to fetch the data from.

Returns:
dl.GeoJSON: A Dash Leaflet GeoJSON component.
"""
data = fetch_json_data(url)

# Check if the data is already in GeoJSON format
if not ('type' in data and 'features' in data):
data = convert_to_geojson(data)

return dl.GeoJSON(
data=data,
id=str(uuid.uuid4()),
cluster=True,
zoomToBoundsOnClick=True,
superClusterOptions={
'radius': 160,
'maxClusterRadius': 40,
'minZoom': 3,
},
options=dict(
pointToLayer=Namespace("myNamespace", "mySubNamespace")("drawCrimeIcon")
)
)


# Create a class to hold all of the Dash components for the Lease page
class LeaseComponents(BaseClass):
Expand Down Expand Up @@ -911,8 +945,15 @@ def create_listed_date_components(self):
return listed_date_components

def create_map(self):
# Create a GeoJSON layer for oil wells with clustering
"""
Creates a Dash Leaflet map with multiple layers.

Returns:
dl.Map: A Dash Leaflet Map component.
"""
# Create additional layers
oil_well_layer = self.create_oil_well_geojson_layer()
crime_layer = self.create_new_geojson_layer('https://data.lacity.org/resource/2nrs-mtv8.json')

# Create the main map with the lease layer
map = dl.Map(
Expand All @@ -929,13 +970,13 @@ def create_map(self):
closePopupOnClick=True,
style={'width': '100%', 'height': '90vh', 'margin': "auto", "display": "inline-block"}
)

# Add layer control with the oil well layer as an overlay (unchecked by default)
# Add a layer control for the additional layers
layers_control = dl.LayersControl(
[
dl.Overlay(oil_well_layer, name="Oil Wells", checked=False)
[ # Create a list of layers to add to the control
dl.Overlay(oil_well_layer, name="Oil & Gas Wells", checked=False),
dl.Overlay(crime_layer, name="Crime", checked=False),
],
collapsed=False,
collapsed=True,
position='topleft'
)
map.children.append(layers_control)
Expand Down Expand Up @@ -1675,7 +1716,7 @@ def create_map(self):
[
dl.Overlay(oil_well_layer, name="Oil Wells", checked=False)
],
collapsed=False,
collapsed=True,
position='topleft'
)
map.children.append(layers_control)
Expand Down
Loading