├── .gitignore ├── INSTALL.sh ├── LICENSE ├── README.md ├── config.json ├── example.py ├── requirements.txt └── simplemap ├── __init__.py ├── html_render.py ├── map.py └── templates ├── basic.html └── static └── css └── basic.css /.gitignore: -------------------------------------------------------------------------------- 1 | #Project specific 2 | dev_config.json 3 | config.json 4 | 5 | 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Distribution / packaging 15 | .Python 16 | env/ 17 | build/ 18 | develop-eggs/ 19 | dist/ 20 | downloads/ 21 | eggs/ 22 | .eggs/ 23 | lib/ 24 | lib64/ 25 | parts/ 26 | sdist/ 27 | var/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *,cover 51 | .hypothesis/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | 60 | # Sphinx documentation 61 | docs/_build/ 62 | 63 | # PyBuilder 64 | target/ -------------------------------------------------------------------------------- /INSTALL.sh: -------------------------------------------------------------------------------- 1 | # Need to install pythons package manager 2 | sudo apt-get update 3 | sudo apt-get -y install python-pip 4 | 5 | # Now install relevant python modules 6 | sudo pip install -r requirements.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Patrick Servello 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simplemap 2 | 3 | ![Example](http://i.imgur.com/wocHdFk.png "Example") 4 | 5 | 6 | ###Overview 7 | This Python library allows you to plot GPS coordinates on a standalone google map contained in an HTML file. Functionality is focused around mapping of points, so other google maps API features will not be implemented (i.e. directions, reverse geocoding, etc). 8 | 9 | My plans for features to be implemented in the mediate future are: 10 | * Google map interface event callbacks 11 | * Custom marker labels 12 | * Custom marker text 13 | * Customer marker images 14 | 15 | ======= 16 | 17 | ###Installation 18 | Installation is very straightforward. Simply clone the repository and install the necessary requirements 19 | ```sh 20 | git clone https://github.com/patrick--/simplemap/ 21 | cd simplemap 22 | sudo ./INSTALL.sh 23 | ``` 24 | 25 | ========= 26 | 27 | ###Prerequisites 28 | 29 | Before using this library you will need to obtain a Google Maps Javascript API key. The following link has instructions on getting a key 30 | * [Get a Google Maps API Key](https://developers.google.com/maps/documentation/javascript/get-api-key) 31 | 32 | 33 | Once you get a key, make sure to put it in the `config.json` file. Any problems with API authentication will result in an error popup in your `output.html` file. 34 | 35 | ![API Error Example](http://i.imgur.com/g6aG2Zk.png "API Error") 36 | 37 | 38 | 39 | ======= 40 | 41 | ###Usage 42 | 43 | First, import the simplemap library 44 | ```py 45 | import simplemap 46 | ``` 47 | 48 | Next, initialize your map object: `simplemap.Map()`. 49 | 50 | #####Parameters: 51 | 52 | * `title` - The title of the HTML map webpage 53 | * `center` - `list` of lat/lon values for map center. Default value of `None` auto centers map - Optional 54 | * `zoom` - Zoom level of the map, defaults to 11 - Optional 55 | * `markers` - `list` of markers, where a marker is a `list`: optional hovertext, lat and lon - Optional 56 | * `html_template` - HTML template used to generate a map. Currently the default value of `basic.html` is the only option - Optional 57 | * `config_file` - JSON file containing the google maps `api_key`, defaults to `config.json` - Optional 58 | 59 | **Note:** By default the map will automatically center and zoom based on points given. For a custom center point, make sure to give `center` a lat/lon value. 60 | 61 | 62 | 63 | Finally, write the map to an HTML file: `Map.write()` 64 | #####Parameters: 65 | * `output_file_name` - Filename that HTML will be written to 66 | 67 | 68 | ###Examples 69 | 70 | In this first example, notice that `zoom` and `center` were not given values. This allows the map to center and zoom itself based on the points provided. 71 | 72 | ```py 73 | import simplemap 74 | 75 | map_title = 'Example Map' 76 | gps_markers = [ ['Example text', 34.4563,-118.1241], [34.6432,-118.1554] ] 77 | 78 | example_map = simplemap.Map(map_title, markers=gps_markers) 79 | example_map.write('example.html') 80 | 81 | ``` 82 | 83 | This example demonstrates that `markers` is a property of the `Map()` class - so it can be given a value after initialization. 84 | 85 | ```py 86 | import simplemap 87 | 88 | example_map = simplemap.Map('Test Title', [34.5124, -118.1111]) 89 | example_map.markers = [ ['Example text', 34.4563,-118.1241], [34.6432,-118.1554] ] 90 | example_map.write('example.html') 91 | 92 | ``` 93 | 94 | Finally, this example demonstrates custom values given to both `center` and `zoom`. 95 | 96 | ```py 97 | import simplemap 98 | 99 | map_title = 'Example Map' 100 | center_point = [34.5124, -118.1111] 101 | zoom_level = 5 102 | gps_markers = [ ['Example text', 34.4563,-118.1241], [34.6432,-118.1554] ] 103 | 104 | example_map = simplemap.Map(map_title, center=center_point, zoom=zoom_level, markers=gps_markers) 105 | example_map.write('example.html') 106 | ``` 107 | ======== 108 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_key": "YOUR_API_KEY_HERE" 3 | } -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | import simplemap, webbrowser 2 | 3 | map_title = 'Example Map' 4 | center_point = [34.5124, -118.1111] 5 | #center_point = None 6 | 7 | gps_markers = [ ['Example text', 34.4563,-118.1241], [34.5235,-118.1245], [34.6432,-118.1554] ] 8 | # TIL that python asigns by reference. Meaning that even though you shift data around in new_gps 9 | # it will effect the original :|. To avoid this I could have done `new_gps = gps_makers[:]` which 10 | # would have made a new list. The issue is gps_markers is a 2 dimensional array/list, so it was only 11 | # looking one level deep. So we need to go deeper morty. 12 | new_gps = [sublist[:] for sublist in gps_markers] 13 | 14 | example_map = simplemap.Map(map_title, markers=gps_markers) 15 | file_url = example_map.write('example.html') 16 | print('HTML page written to: ' + file_url) 17 | webbrowser.open(file_url) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Jinja2==2.8 2 | MarkupSafe==0.23 3 | wheel==0.24.0 4 | -------------------------------------------------------------------------------- /simplemap/__init__.py: -------------------------------------------------------------------------------- 1 | from .map import Map -------------------------------------------------------------------------------- /simplemap/html_render.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | simplemap.html_render 4 | ~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | This module contains everything related to Jinja2 HTML rendering. 7 | """ 8 | 9 | from jinja2 import Undefined 10 | 11 | 12 | class SilentUndefined(Undefined): 13 | """ Allow Jinja2 to continue rendering if undefined tag is parsed """ 14 | 15 | def _fail_with_undefined_error(self, *args, **kwargs): 16 | return None -------------------------------------------------------------------------------- /simplemap/map.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | simplemap.map.py 4 | ~~~~~~~~~~~~~~~~ 5 | 6 | This module contains all core functionality related to map generation 7 | 8 | """ 9 | 10 | from jinja2 import Environment, FileSystemLoader 11 | from simplemap.html_render import SilentUndefined 12 | import json 13 | import os 14 | import sys 15 | import traceback 16 | 17 | TEMPLATES_DIR = FileSystemLoader('simplemap/templates') 18 | ZOOM_DEFAULT = 11 19 | LINES_DEFAULT = [] 20 | 21 | class Map(object): 22 | def __init__(self, title, center=None, zoom=11, markers=None, points=None, html_template='basic.html', config_file='config.json'): 23 | self._env = Environment(loader=TEMPLATES_DIR, trim_blocks=True, undefined=SilentUndefined) 24 | self.title = title 25 | self.template = self._env.get_template(html_template) 26 | self.center = center 27 | self.zoom = zoom 28 | self.config = config_file 29 | self.markers = markers 30 | # points for lines added in 31 | self.points = points 32 | 33 | def set_center(self, center_point): 34 | self._center = '{{ lat:{}, lng:{}}}'.format(*center_point) if center_point else 'null' 35 | 36 | def get_center(self): 37 | return self._center 38 | 39 | def set_zoom(self, zoom): 40 | if zoom: 41 | self._zoom = zoom 42 | else: 43 | #Don't allow zoom to be null if customer center is given 44 | self._zoom = ZOOM_DEFAULT if self.center else 'null' 45 | 46 | def get_zoom(self): 47 | return self._zoom 48 | 49 | def set_config(self, config_file): 50 | try: 51 | with open(config_file, "r") as config: 52 | self._config = json.load(config) 53 | except IOError: 54 | print("Error, unable to open {0} config file.".format(config_file)) 55 | sys.exit() 56 | 57 | except KeyError: 58 | print("Error, `api_entry` not found in {0} config file.".format(config_file)) 59 | sys.exit() 60 | 61 | except Exception: 62 | print("An unknown error occured while attempting to read {0} config file.".format(config_file)) 63 | traceback.print_exc() 64 | sys.exit() 65 | 66 | def get_config(self): 67 | return self._config 68 | 69 | def set_markers(self, markers): 70 | if markers: 71 | for i in markers: 72 | if len(i) == 2: 73 | i.insert(0, '') 74 | self._markers = markers 75 | 76 | def get_markers(self): 77 | return self._markers 78 | 79 | # Points setter and getter 80 | def set_points(self, points): 81 | if points: 82 | self._points = points 83 | else: 84 | self._points = LINES_DEFAULT 85 | 86 | def get_points(self): 87 | return self._points 88 | 89 | config = property(get_config, set_config) 90 | markers = property(get_markers, set_markers) 91 | center = property(get_center, set_center) 92 | zoom = property(get_zoom, set_zoom) 93 | # Declare the names of the setter and getters 94 | points = property(get_points, set_points) 95 | 96 | def write(self, output_path): 97 | try: 98 | html = self.template.render(map_title = self.title, center=self.center, 99 | zoom=self.zoom, markers=self.markers, points=self.points, api_key=self.config['api_key']) 100 | with open(output_path, "w") as out_file: 101 | out_file.write(html) 102 | return 'file://' + os.path.join(os.path.abspath(os.curdir), output_path) 103 | except IOError: 104 | print("Error, unable to write {0}".format(output_path)) 105 | sys.exit() 106 | 107 | except Exception: 108 | print("Undefined error occured while writing generating {0}".format(output_path)) 109 | traceback.print_exc() 110 | sys.exit() 111 | -------------------------------------------------------------------------------- /simplemap/templates/basic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{map_title}} 5 | 6 | 7 | 8 | 9 | 10 |
11 | 61 | 63 | 64 | -------------------------------------------------------------------------------- /simplemap/templates/static/css/basic.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | #map { 8 | height: 100%; 9 | } --------------------------------------------------------------------------------