Skip to content

Commit 2a16682

Browse files
committed
WIP: Introduce Marker and MarkerContent
- MarkerContent is an abstract method. It can be either a Pin or Image - Marker object comprises of MarkerContent - Add unit tests - I am bothered by the use of `eval` method for the content
1 parent 282787e commit 2a16682

File tree

15 files changed

+317
-135
lines changed

15 files changed

+317
-135
lines changed

examples/example.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
from flask import Flask, render_template, request
44
from flask_googlemaps import GoogleMaps
55
from flask_googlemaps import Map, icons
6-
#from dynaconf import FlaskDynaconf
6+
7+
# from dynaconf import FlaskDynaconf
78

89
app = Flask(__name__, template_folder="templates")
9-
#FlaskDynaconf(app) # will read GOOGLEMAPS_KEY from .secrets.toml
10+
# FlaskDynaconf(app) # will read GOOGLEMAPS_KEY from .secrets.toml
1011

1112

1213
# you can set key as config
@@ -148,7 +149,11 @@ def mapview():
148149
varname="circlemap",
149150
lat=33.678,
150151
lng=-116.243,
151-
circles=[circle, [33.685, -116.251, 1000], (33.685, -116.251, 1500),],
152+
circles=[
153+
circle,
154+
[33.685, -116.251, 1000],
155+
(33.685, -116.251, 1500),
156+
],
152157
)
153158

154159
polyline = {

examples/example_2.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
from flask import Flask, render_template
22
from flask_googlemaps import GoogleMaps, Map, icons
3-
#from dynaconf import FlaskDynaconf
4-
#enter the api key below
5-
api = ''
3+
4+
# from dynaconf import FlaskDynaconf
5+
# enter the api key below
6+
api = ""
67
app = Flask(__name__)
7-
GoogleMaps(app, key = api)
8-
#FlaskDynaconf(app)
8+
GoogleMaps(app, key=api)
9+
# FlaskDynaconf(app)
910

1011
import json
1112

1213

1314
@app.route("/")
1415
def map_created_in_view():
1516

16-
with open('dark_mode.json') as d:
17+
with open("dark_mode.json") as d:
1718
dark_data = json.load(d)
1819

1920
wmap = Map(
@@ -26,10 +27,9 @@ def map_created_in_view():
2627
icons.dots.blue: [(37.4300, -122.1400, "Hello World")],
2728
},
2829
style="height:400px;width:600px;margin:0;color:#242f3e;",
29-
bicycle_layer = True,
30+
bicycle_layer=True,
3031
)
3132

32-
3333
gmap = Map(
3434
identifier="gmap",
3535
varname="gmap",
@@ -40,7 +40,7 @@ def map_created_in_view():
4040
icons.dots.blue: [(37.4300, -122.1400, "Hello World")],
4141
},
4242
style="height:400px;width:600px;margin:0;color:#242f3e;",
43-
layer = "https://geo.data.gov.sg/dengue-cluster/2020/09/02/kml/dengue-cluster.kml"
43+
layer="https://geo.data.gov.sg/dengue-cluster/2020/09/02/kml/dengue-cluster.kml",
4444
)
4545

4646
dmap = Map(
@@ -54,11 +54,10 @@ def map_created_in_view():
5454
},
5555
style="height:400px;width:600px;margin:0;color:#242f3e;",
5656
styles=dark_data,
57-
5857
)
5958

60-
# print(get_address(api, 22.4761596, 88.4149326))
61-
return render_template("example_2.html", dmap=dmap ,gmap = gmap, wmap = wmap,key = api)
59+
# print(get_address(api, 22.4761596, 88.4149326))
60+
return render_template("example_2.html", dmap=dmap, gmap=gmap, wmap=wmap, key=api)
6261

6362

6463
if __name__ == "__main__":

examples/jsonify_examples.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,11 @@ def circle_view():
154154
varname="circlemap",
155155
lat=33.678,
156156
lng=-116.243,
157-
circles=[circle, [33.685, -116.251, 1000], (33.685, -116.251, 1500),],
157+
circles=[
158+
circle,
159+
[33.685, -116.251, 1000],
160+
(33.685, -116.251, 1500),
161+
],
158162
)
159163

160164
return jsonify(circlemap.as_json())

examples/simple.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
11
from flask import Flask, render_template
22

33
from flask_googlemaps import GoogleMaps, Map
4+
from flask_googlemaps.image import Image
45
from flask_googlemaps.pin import Pin
56

67
app = Flask(__name__)
78
GoogleMaps(app)
89

9-
red = Pin(border_color="", glyph_color="",
10-
background="",
11-
glyph="https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png")
12-
blue = Pin(border_color="blue", glyph_color="",
13-
background="black",
14-
glyph="https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png")
15-
1610

1711
@app.route("/")
1812
def map_created_in_view():
@@ -21,10 +15,20 @@ def map_created_in_view():
2115
varname="gmap",
2216
lat=37.4419,
2317
lng=-122.1419,
24-
markers={
25-
red: [(37.4419, -122.1419),],
26-
blue: [(37.4300, -122.1400, "Hello World")],
27-
},
18+
markers=[
19+
{
20+
"latitude": 37.4419,
21+
"longitude": -122.1419,
22+
"label": "1",
23+
# 'content': Pin(border_color="blue", background="blue")
24+
"content": Pin(border_color="blue", background="blue"),
25+
},
26+
{
27+
"latitude": 37.4519,
28+
"longitude": -122.1519,
29+
"content": Pin(border_color="blue", background="blue"),
30+
},
31+
],
2832
style="height:400px;width:600px;margin:0;",
2933
)
3034

flask_googlemaps/__init__.py

Lines changed: 8 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
"""FlaskGoogleMaps - Google Maps Extension for Flask"""
22

3-
__version__ = "0.4.2"
3+
__version__ = "0.5.0"
44

55
from json import dumps
66
from typing import Optional, Dict, Any, List, Union, Tuple, Text
7+
78
import requests
89
from flask import Blueprint, g, render_template
910
from markupsafe import Markup
1011

11-
from flask_googlemaps.pin import Pin
12+
from flask_googlemaps.marker import Marker
1213

13-
DEFAULT_PIN = Pin(border_color="", glyph_color="", background="")
1414
DEFAULT_CLUSTER_IMAGE_PATH = "static/images/m"
1515

1616

@@ -28,7 +28,8 @@ def __init__(
2828
cls="map", # type: str
2929
language="en", # type: str
3030
region="US", # type: str
31-
rectangles=None, # type: Optional[List[Union[List, Tuple, Tuple[Tuple], Dict]]]
31+
rectangles=None,
32+
# type: Optional[List[Union[List, Tuple, Tuple[Tuple], Dict]]]
3233
circles=None, # type: Optional[List[Union[List, Tuple, Dict]]]
3334
polylines=None, # type: Optional[List[Union[List, Tuple, Dict]]]
3435
polygons=None, # type: Optional[List[Union[List, Tuple, Dict]]]
@@ -65,12 +66,11 @@ def __init__(
6566
self.language = language
6667
self.region = region
6768
self.varname = varname
68-
self.center = self.verify_lat_lng_coordinates(lat, lng)
69+
self.center = Marker.verify_latitude(lat), Marker.verify_longitude(lng)
6970
self.zoom = zoom
7071
self.maptype = maptype
71-
self.markers = [] # type: List[Any]
72+
self.markers = Marker.from_list(markers) # type: List[Marker]
7273
self.map_ids = map_ids
73-
self.build_markers(markers)
7474
self.rectangles = [] # type: List[Any]
7575
self.build_rectangles(rectangles)
7676
self.circles = [] # type: List[Any]
@@ -107,50 +107,6 @@ def __init__(
107107
self.heatmap_data = []
108108
self.build_heatmap(heatmap_data, heatmap_layer)
109109

110-
def build_markers(self, markers):
111-
# type: (Optional[Union[Dict, List, Tuple]]) -> None
112-
if not markers:
113-
return
114-
if not isinstance(markers, (dict, list, tuple)):
115-
raise AttributeError("markers accepts only dict, list and tuple")
116-
117-
if isinstance(markers, dict):
118-
for pin, marker_list in markers.items():
119-
for marker in marker_list:
120-
marker_dict = self.build_marker_dict(marker, pin=pin)
121-
self.add_marker(**marker_dict)
122-
else:
123-
for marker in markers:
124-
if isinstance(marker, dict):
125-
self.add_marker(**marker)
126-
elif isinstance(marker, (tuple, list)):
127-
marker_dict = self.build_marker_dict(marker)
128-
self.add_marker(**marker_dict)
129-
130-
def build_marker_dict(self, marker, pin=None):
131-
# type: (Union[List, Tuple], Optional[Pin]) -> Dict
132-
marker_dict = {
133-
"lat": marker[0],
134-
"lng": marker[1],
135-
"pin": pin or DEFAULT_PIN,
136-
}
137-
if len(marker) > 2:
138-
marker_dict["infobox"] = marker[2]
139-
if len(marker) > 3:
140-
marker_dict["pin"] = marker[3]
141-
return marker_dict
142-
143-
def add_marker(self, lat=None, lng=None, **kwargs):
144-
# type: (Optional[float], Optional[float], **Any) -> None
145-
if lat is not None:
146-
kwargs["lat"] = lat
147-
if lng is not None:
148-
kwargs["lng"] = lng
149-
if "lat" not in kwargs or "lng" not in kwargs:
150-
raise AttributeError("lat and lng required")
151-
152-
self.markers.append(kwargs)
153-
154110
def build_rectangles(self, rectangles):
155111
# type: (Optional[List[Union[List, Tuple, Tuple[Tuple], Dict]]]) -> None
156112
"""Process data to construct rectangles
@@ -805,24 +761,10 @@ def as_json(self):
805761

806762
return json_dict
807763

808-
def verify_lat_lng_coordinates(self, lat, lng):
809-
if not (90 >= lat >= -90):
810-
raise AttributeError(
811-
"Latitude must be between -90 and 90 degrees inclusive."
812-
)
813-
if not (180 >= lng >= -180):
814-
raise AttributeError(
815-
"Longitude must be between -180 and 180 degrees inclusive."
816-
)
817-
818-
return (lat, lng)
819-
820764
@property
821765
def js(self):
822766
# type: () -> Markup
823-
return Markup(
824-
self.render("googlemaps/gmapjs.html", gmap=self, DEFAULT_PIN=DEFAULT_PIN)
825-
)
767+
return Markup(self.render("googlemaps/gmapjs.html", gmap=self, DEFAULT_PIN=""))
826768

827769
@property
828770
def html(self):

flask_googlemaps/image.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from dataclasses import dataclass
2+
3+
from flask_googlemaps.marker_content import MarkerContent
4+
5+
6+
@dataclass
7+
class Image(MarkerContent):
8+
url: str
9+
10+
def content(self) -> str:
11+
return self.name
12+
13+
def dom_element(self) -> str:
14+
return (
15+
f"const {self.name} = document.createElement('img');\n"
16+
f"{self.name}.src = '{self.url}';"
17+
)

flask_googlemaps/marker.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
from typing import Optional, List, Any, Dict
2+
3+
from flask_googlemaps.marker_content import MarkerContent
4+
from flask_googlemaps.pin import Pin
5+
6+
DEFAULT_PIN = Pin()
7+
8+
9+
# from json import JSONEncoder
10+
#
11+
#
12+
# def wrapped_default(self, obj):
13+
# return getattr(obj.__class__, "__json__", wrapped_default.default)(obj)
14+
#
15+
#
16+
# wrapped_default.default = JSONEncoder().default
17+
#
18+
# # apply the patch
19+
# JSONEncoder.original_default = JSONEncoder.default
20+
# JSONEncoder.default = wrapped_default
21+
22+
23+
class Marker(dict):
24+
25+
def __init__(
26+
self,
27+
latitude: float,
28+
longitude: float,
29+
infobox: Optional[str] = None,
30+
content: Optional[MarkerContent] = None,
31+
label: Optional[str] = None,
32+
):
33+
self.latitude = Marker.verify_latitude(latitude)
34+
self.longitude = Marker.verify_longitude(longitude)
35+
self.infobox = infobox
36+
self.marker_icon = content
37+
self.content = content.content()
38+
self.dom_element = content.dom_element()
39+
self.label = label
40+
41+
dict.__init__(
42+
self,
43+
{
44+
"latitude": self.latitude,
45+
"longitude": self.longitude,
46+
"infobox": self.infobox,
47+
"content": self.content,
48+
"id": self.marker_icon.name,
49+
},
50+
)
51+
52+
@staticmethod
53+
def verify_latitude(latitude: float) -> Optional[float]:
54+
if not (90 >= latitude >= -90):
55+
raise AttributeError(
56+
"Latitude must be between -90 and 90 degrees inclusive."
57+
)
58+
return latitude
59+
60+
@staticmethod
61+
def verify_longitude(longitude: float) -> Optional[float]:
62+
if not (180 >= longitude >= -180):
63+
raise AttributeError(
64+
"Longitude must be between -180 and 180 degrees inclusive."
65+
)
66+
return longitude
67+
68+
@staticmethod
69+
def from_list(markers: List[Dict[str, Any]]) -> List["Marker"]:
70+
return list(map(lambda marker: Marker(**marker), markers))

flask_googlemaps/marker_content.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import uuid
2+
from abc import ABC, abstractmethod
3+
from dataclasses import dataclass
4+
5+
6+
@dataclass
7+
class MarkerContent(ABC):
8+
9+
def __post_init__(self):
10+
self.name = f"var_{str(uuid.uuid1())[0:8]}"
11+
12+
@abstractmethod
13+
def content(self) -> str:
14+
pass
15+
16+
@abstractmethod
17+
def dom_element(self) -> str:
18+
pass

0 commit comments

Comments
 (0)