Skip to content

Commit fe89c79

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 dbbda49 commit fe89c79

File tree

12 files changed

+290
-118
lines changed

12 files changed

+290
-118
lines changed

examples/example_2.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,7 @@ def map_created_in_view():
5757
)
5858

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

6462

6563
if __name__ == "__main__":

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

flask_googlemaps/pin.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,40 @@
1-
import uuid
2-
from dataclasses import dataclass, field
1+
from dataclasses import dataclass
32
from typing import Optional
43

4+
from flask_googlemaps.marker_content import MarkerContent
55

6-
@dataclass(frozen=True)
7-
class Pin:
8-
border_color: str
9-
glyph_color: str
10-
background: str
11-
glyph: Optional[str] = None
12-
uuid: Optional[str] = field(default_factory=lambda: f"_{str(uuid.uuid1())[0:8]}")
6+
7+
@dataclass
8+
class Pin(MarkerContent):
9+
10+
def __post_init__(self):
11+
MarkerContent.__post_init__(self)
12+
self.dom = []
13+
if self.glyph_color:
14+
self.dom.append(f"\tglyphColor: '{self.glyph_color}',")
15+
if self.background:
16+
self.dom.append(f"\tbackground: '{self.background}',")
17+
if self.border_color:
18+
self.dom.append(f"\tborderColor: '{self.border_color}',")
19+
if self.glyph:
20+
self.dom.append(f"\tglyph: '{self.glyph}',")
21+
if self.dom:
22+
self.dom.insert(0, f"const {self.name} = new PinElement({{")
23+
self.dom.append("\t\t});\n")
24+
25+
def dom_element(self) -> Optional[str]:
26+
27+
if self.dom:
28+
return "\n".join(self.dom)
29+
return None
30+
31+
border_color: str = ""
32+
glyph_color: str = ""
33+
background: str = ""
34+
glyph: Optional[str] = ""
1335
scale: float = 1.0
36+
37+
def content(self) -> str:
38+
if self.dom:
39+
return f"{self.name}.element"
40+
return ""

0 commit comments

Comments
 (0)