diff --git a/README b/README
index 63d4ac429..b23ce644c 100644
--- a/README
+++ b/README
@@ -26,7 +26,12 @@ The following packages are *STRONGLY RECOMMENDED* to be installed:
GraphViz Enable creation of graphs using GraphViz engine
http://www.graphviz.org
-The following packages are *STRONGLY RECOMMENDED* to be installed:
+ osmgpsmap Used to show maps in the geography view.
+ The package is named osmgpsmap or osm-gps-map
+ http://nzjrs.github.com/osm-gps-map/
+
+The following packages are *STRONGLY RECOMMENDED* to be installed if you want
+to use the htmlview. The geography view doesn't use them any more :
PyWebKitGtk or python-gtkmozembed: PACKAGERS, Test if on your distribution
webkit and Gramps is stable. If you get crashes, patch
src/plugins/view/htmlrenderer.py and
diff --git a/configure.in b/configure.in
index 28456fb9c..b309bf316 100644
--- a/configure.in
+++ b/configure.in
@@ -139,6 +139,7 @@ src/plugins/gramplet/Makefile
src/plugins/graph/Makefile
src/plugins/import/Makefile
src/plugins/lib/Makefile
+src/plugins/lib/maps/Makefile
src/plugins/mapservices/Makefile
src/plugins/quickview/Makefile
src/plugins/rel/Makefile
diff --git a/src/config.py b/src/config.py
index 7f718b63a..df5e93bc3 100644
--- a/src/config.py
+++ b/src/config.py
@@ -158,12 +158,14 @@ register('export.proxy-order', [
["reference", 0],
])
-register('geoview.latitude', "0.0")
-register('geoview.lock', False)
-register('geoview.longitude', "0.0")
-register('geoview.map', "person")
-register('geoview.stylesheet', "")
-register('geoview.zoom', 0)
+register('geography.center-lon', 0.0)
+register('geography.lock', False)
+register('geography.center-lat', 0.0)
+register('geography.map', "person")
+register('geography.map_service', 1)
+register('geography.zoom', 0)
+register('geography.show_cross', False)
+register('geography.path', "")
register('htmlview.start-url', "http://gramps-project.org")
register('htmlview.url-handler', False)
diff --git a/src/gui/grampsgui.py b/src/gui/grampsgui.py
index 02f352b91..144117091 100644
--- a/src/gui/grampsgui.py
+++ b/src/gui/grampsgui.py
@@ -116,9 +116,13 @@ def register_stock_icons ():
('gramps-font-color', _('Font Color'), gtk.gdk.CONTROL_MASK, 0, ''),
('gramps-font-bgcolor', _('Font Background Color'), gtk.gdk.CONTROL_MASK, 0, ''),
('gramps-gramplet', _('Gramplets'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-geo', _('GeoView'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-geo-mainmap', _('GeoView'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-geo-altmap', _('GeoView'), gtk.gdk.CONTROL_MASK, 0, ''),
+ ('gramps-geo', _('Geography'), gtk.gdk.CONTROL_MASK, 0, ''),
+ ('gramps-geo-mainmap', _('Geography'), gtk.gdk.CONTROL_MASK, 0, ''),
+ ('gramps-geo-altmap', _('Geography'), gtk.gdk.CONTROL_MASK, 0, ''),
+ ('geo-show-person', _('GeoPerson'), gtk.gdk.CONTROL_MASK, 0, ''),
+ ('geo-show-family', _('GeoFamily'), gtk.gdk.CONTROL_MASK, 0, ''),
+ ('geo-show-event', _('GeoEvents'), gtk.gdk.CONTROL_MASK, 0, ''),
+ ('geo-show-place', _('GeoPlaces'), gtk.gdk.CONTROL_MASK, 0, ''),
('gramps-lock', _('Public'), gtk.gdk.CONTROL_MASK, 0, ''),
('gramps-media', _('Media'), gtk.gdk.CONTROL_MASK, 0, ''),
('gramps-merge', _('Merge'), gtk.gdk.CONTROL_MASK, 0, ''),
diff --git a/src/plugins/lib/Makefile.am b/src/plugins/lib/Makefile.am
index cd7d798c4..a931cf1e9 100644
--- a/src/plugins/lib/Makefile.am
+++ b/src/plugins/lib/Makefile.am
@@ -12,6 +12,8 @@ EXTRA_DIST=$(xml_in_files) $(xml_files)
pkgdatadir = $(datadir)/@PACKAGE@/plugins/lib
+SUBDIRS = maps
+
pkgdata_PYTHON = \
libcairodoc.py\
libformatting.py\
diff --git a/src/plugins/lib/maps/Makefile.am b/src/plugins/lib/maps/Makefile.am
new file mode 100644
index 000000000..17dbf7b78
--- /dev/null
+++ b/src/plugins/lib/maps/Makefile.am
@@ -0,0 +1,19 @@
+pkgdatadir = $(datadir)/@PACKAGE@/maps
+
+pkgdata_PYTHON = \
+ constants.py\
+ geography.py\
+ grampsmaps.py\
+ __init__.py
+
+pkgpyexecdir = @pkgpyexecdir@/maps
+pkgpythondir = @pkgpythondir@/maps
+
+# Clean up all the byte-compiled files
+MOSTLYCLEANFILES = *pyc *pyo
+
+GRAMPS_PY_MODPATH = "../"
+
+pycheck:
+ (export PYTHONPATH=$(GRAMPS_PY_MODPATH); \
+ pychecker $(pkgdata_PYTHON));
diff --git a/src/plugins/lib/maps/__init__.py b/src/plugins/lib/maps/__init__.py
new file mode 100644
index 000000000..ca46d147e
--- /dev/null
+++ b/src/plugins/lib/maps/__init__.py
@@ -0,0 +1,27 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2010 Serge Noiraud
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# $Id $
+
+"""
+The gen module provides packages that are common to all gramps
+interfaces (gui, cli and web).
+"""
+
+__all__ = []
diff --git a/src/plugins/lib/maps/constants.py b/src/plugins/lib/maps/constants.py
new file mode 100644
index 000000000..89c04c4ee
--- /dev/null
+++ b/src/plugins/lib/maps/constants.py
@@ -0,0 +1,108 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2010 Serge Noiraud
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+# $Id: $
+
+"Geography constants"
+
+#-------------------------------------------------------------------------
+#
+# standard python modules
+#
+#-------------------------------------------------------------------------
+from gen.ggettext import gettext as _
+import gen.lib
+import osmgpsmap
+
+#-------------------------------------------------------------------------
+#
+# Constants
+#
+#-------------------------------------------------------------------------
+
+ICONS = {
+ gen.lib.EventType.BIRTH : 'gramps-geo-birth',
+ gen.lib.EventType.DEATH : 'gramps-geo-death',
+ gen.lib.EventType.MARRIAGE : 'gramps-geo-marriage',
+}
+
+# map providers
+OPENSTREETMAP = 1
+OPENSTREETMAP_RENDERER = 2
+OPENAERIALMAP = 3
+MAPS_FOR_FREE = 4
+GOOGLE_STREET = 5
+GOOGLE_SATELLITE = 6
+GOOGLE_HYBRID = 7
+VIRTUAL_EARTH_STREET = 8
+VIRTUAL_EARTH_SATELLITE = 9
+VIRTUAL_EARTH_HYBRID = 10
+YAHOO_STREET = 11
+YAHOO_SATELLITE = 12
+YAHOO_HYBRID = 13
+
+tiles_path = {
+ OPENSTREETMAP : "openstreetmap",
+ OPENSTREETMAP_RENDERER : "openstreetmaprenderer",
+ OPENAERIALMAP : "openaerialmap",
+ MAPS_FOR_FREE : "mapsforfree",
+ GOOGLE_STREET : "googlestreet",
+ GOOGLE_SATELLITE : "googlesat",
+ GOOGLE_HYBRID : "googlehybrid",
+ VIRTUAL_EARTH_STREET : "virtualearthstreet",
+ VIRTUAL_EARTH_SATELLITE : "virtualearthsat",
+ VIRTUAL_EARTH_HYBRID : "virtualearthhybrid",
+ YAHOO_STREET : "yahoostreet",
+ YAHOO_SATELLITE : "yahoosat",
+ YAHOO_HYBRID : "yahoohybrid",
+}
+
+map_title = {
+ OPENSTREETMAP : "OpenStreetMap",
+ OPENSTREETMAP_RENDERER : "OpenStreetMap renderer",
+ OPENAERIALMAP : "OpenAerialMap",
+ MAPS_FOR_FREE : "Maps For Free",
+ GOOGLE_STREET : "Google street",
+ GOOGLE_SATELLITE : "Google sat",
+ GOOGLE_HYBRID : "Google hybrid",
+ VIRTUAL_EARTH_STREET : "Virtualearth street",
+ VIRTUAL_EARTH_SATELLITE : "Virtualearth sat",
+ VIRTUAL_EARTH_HYBRID : "Virtualearth hybrid",
+ YAHOO_STREET : "Yahoo street",
+ YAHOO_SATELLITE : "Yahoo sat",
+ YAHOO_HYBRID : "Yahoo hybrid",
+}
+
+map_type = {
+ OPENSTREETMAP : osmgpsmap.SOURCE_OPENSTREETMAP,
+ OPENSTREETMAP_RENDERER : osmgpsmap.SOURCE_OPENSTREETMAP_RENDERER,
+ OPENAERIALMAP : osmgpsmap.SOURCE_OPENAERIALMAP,
+ MAPS_FOR_FREE : osmgpsmap.SOURCE_MAPS_FOR_FREE,
+ GOOGLE_STREET : osmgpsmap.SOURCE_GOOGLE_STREET,
+ GOOGLE_SATELLITE : osmgpsmap.SOURCE_GOOGLE_SATELLITE,
+ GOOGLE_HYBRID : osmgpsmap.SOURCE_GOOGLE_HYBRID,
+ VIRTUAL_EARTH_STREET : osmgpsmap.SOURCE_VIRTUAL_EARTH_STREET,
+ VIRTUAL_EARTH_SATELLITE : osmgpsmap.SOURCE_VIRTUAL_EARTH_SATELLITE,
+ VIRTUAL_EARTH_HYBRID : osmgpsmap.SOURCE_VIRTUAL_EARTH_HYBRID,
+ YAHOO_STREET : osmgpsmap.SOURCE_YAHOO_STREET,
+ YAHOO_SATELLITE : osmgpsmap.SOURCE_YAHOO_SATELLITE,
+ YAHOO_HYBRID : osmgpsmap.SOURCE_YAHOO_HYBRID,
+}
+
diff --git a/src/plugins/lib/maps/geography.py b/src/plugins/lib/maps/geography.py
new file mode 100644
index 000000000..6c74a5e4f
--- /dev/null
+++ b/src/plugins/lib/maps/geography.py
@@ -0,0 +1,733 @@
+# -*- python -*-
+# -*- coding: utf-8 -*-
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2011 Serge Noiraud
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+# $Id: $
+
+#-------------------------------------------------------------------------
+#
+# Python modules
+#
+#-------------------------------------------------------------------------
+from gen.ggettext import sgettext as _
+from gen.ggettext import ngettext
+import sys
+import os
+import gobject
+import time
+
+#------------------------------------------------------------------------
+#
+# Set up logging
+#
+#------------------------------------------------------------------------
+import logging
+_LOG = logging.getLogger(".geography")
+
+#-------------------------------------------------------------------------
+#
+# GTK/Gnome modules
+#
+#-------------------------------------------------------------------------
+import gtk
+
+#-------------------------------------------------------------------------
+#
+# Gramps Modules
+#
+#-------------------------------------------------------------------------
+import gen.lib
+import Utils
+from gui.views.navigationview import NavigationView
+from libformatting import FormattingHelper
+import Errors
+import Bookmarks
+import const
+import constfunc
+from grampsmaps import *
+import constants
+from config import config
+from gui.editors import EditPlace, EditEvent, EditFamily, EditPerson
+from gui.selectors.selectplace import SelectPlace
+
+#-------------------------------------------------------------------------
+#
+# Constants
+#
+#-------------------------------------------------------------------------
+GEOGRAPHY_PATH = os.path.join(const.HOME_DIR, "maps")
+
+#-------------------------------------------------------------------------
+#
+# Functions
+#
+#-------------------------------------------------------------------------
+def _get_sign(value):
+ """
+ return 1 if we have a negative number, 0 in other case
+ """
+ if value < 0.0:
+ return 1
+ else:
+ return 0
+
+def _get_zoom_lat(value):
+ """
+ return the zoom value for latitude depending on the distance.
+ """
+ zoomlat = 1
+ for i, distance in enumerate([80.0, 40.0, 20.0, 10.0, 3.0,
+ 2.0, 1.0, 0.5, 0.2, 0.1]):
+ if value < distance:
+ zoomlat = i+1
+ return zoomlat + 3
+
+def _get_zoom_long(value):
+ """
+ return the zoom value for longitude depending on the distance.
+ """
+ zoomlong = 1
+ for i, distance in enumerate([120.0, 60.0, 30.0, 15.0, 7.0,
+ 4.0, 2.0, 1.0, .5, .2, .1]):
+ if value < distance:
+ zoomlong = i+1
+ return zoomlong + 3
+
+#-------------------------------------------------------------------------
+#
+# GeoGraphyView
+#
+#-------------------------------------------------------------------------
+class GeoGraphyView(osmGpsMap, NavigationView):
+ """
+ View for pedigree tree.
+ Displays the ancestors of a selected individual.
+ """
+ #settings in the config file
+ CONFIGSETTINGS = (
+ ('geography.path', GEOGRAPHY_PATH),
+
+ ('geography.zoom', 10),
+ ('geography.show_cross', True),
+ ('geography.lock', False),
+ ('geography.center-lat', 0.0),
+ ('geography.center-lon', 0.0),
+
+ #('geography.gps_mode', GPS_DISABLED),
+ #('geography.gps_update_rate', float(1.0)),
+ #('geography.max_gps_zoom', 16),
+ #('geography.gps_increment', GPS_INCREMENT),
+
+ ('geography.map_service', constants.OPENSTREETMAP),
+ )
+
+ def __init__(self, title, pdata, dbstate, uistate,
+ get_bookmarks, bm_type, nav_group):
+ NavigationView.__init__(self, title, pdata, dbstate, uistate,
+ get_bookmarks, bm_type, nav_group)
+
+ self.dbstate = dbstate
+ self.dbstate.connect('database-changed', self.change_db)
+ self.default_text = "Enter location here!"
+ self.centerlon = config.get("geography.center-lon")
+ self.centerlat = config.get("geography.center-lat")
+ self.zoom = config.get("geography.zoom")
+ self.lock = config.get("geography.lock")
+ if config.get('geography.path') == "" :
+ config.set('geography.path', GEOGRAPHY_PATH )
+ osmGpsMap.__init__(self)
+
+ self.format_helper = FormattingHelper(self.dbstate)
+ self.centerlat = self.centerlon = 0.0
+ self.cross_map = None
+ self.current_map = None
+ self.without = 0
+ self.geo_mainmap = gtk.gdk.pixbuf_new_from_file_at_size(
+ os.path.join(const.ROOT_DIR, "images", "22x22",
+ ('gramps-geo-mainmap' + '.png' )),
+ 22, 22)
+ self.geo_altmap = gtk.gdk.pixbuf_new_from_file_at_size(
+ os.path.join(const.ROOT_DIR, "images", "22x22",
+ ('gramps-geo-altmap' + '.png' )),
+ 22, 22)
+ if ( config.get('geography.map_service') in
+ ( constants.OPENSTREETMAP, constants.OPENSTREETMAP_RENDERER )):
+ default_image = self.geo_mainmap
+ else:
+ default_image = self.geo_altmap
+ self.geo_othermap = {}
+ for id in ( gen.lib.EventType.BIRTH,
+ gen.lib.EventType.DEATH,
+ gen.lib.EventType.MARRIAGE ):
+ self.geo_othermap[id] = gtk.gdk.pixbuf_new_from_file_at_size(
+ os.path.join(const.ROOT_DIR, "images", "22x22",
+ (constants.ICONS.get(int(id), default_image) + '.png' )),
+ 22, 22)
+
+ def change_page(self):
+ """Called when the page changes."""
+ NavigationView.change_page(self)
+ self.uistate.clear_filter_results()
+
+ def on_delete(self):
+ """
+ Save all modified environment
+ """
+ NavigationView.on_delete(self)
+ self._config.save()
+
+ def change_db(self, db):
+ """
+ Callback associated with DbState. Whenever the database
+ changes, this task is called. In this case, we rebuild the
+ columns, and connect signals to the connected database. Tree
+ is no need to store the database, since we will get the value
+ from self.state.db
+ """
+ self.bookmarks.update_bookmarks(self.dbstate.db.get_bookmarks())
+ if self.active:
+ self.bookmarks.redraw()
+
+ def can_configure(self):
+ """
+ See :class:`~gui.views.pageview.PageView
+ :return: bool
+ """
+ return True
+
+ def config_connect(self):
+ """
+ Overwriten from :class:`~gui.views.pageview.PageView method
+ This method will be called after the ini file is initialized,
+ use it to monitor changes in the ini file
+ """
+
+ #-------------------------------------------------------------------------
+ #
+ # Map Menu
+ #
+ #-------------------------------------------------------------------------
+ def build_nav_menu(self, obj, event, lat, lon):
+ """Builds the menu for actions on the map."""
+ menu = gtk.Menu()
+ menu.set_title(_('Map Menu'))
+
+ if config.get("geography.show_cross"):
+ title = _('Remove cross hair')
+ else:
+ title = _('Add cross hair')
+ add_item = gtk.MenuItem(title)
+ add_item.connect("activate", self.config_crosshair, event, lat , lon)
+ add_item.show()
+ menu.append(add_item)
+
+ if config.get("geography.lock"):
+ title = _('Unlock zoom and position')
+ else:
+ title = _('Lock zoom and position')
+ add_item = gtk.MenuItem(title)
+ add_item.connect("activate", self.config_zoom_and_position,
+ event, lat , lon)
+ add_item.show()
+ menu.append(add_item)
+
+ add_item = gtk.MenuItem(_("Add place"))
+ add_item.connect("activate", self._add_place, event, lat , lon)
+ add_item.show()
+ menu.append(add_item)
+
+ add_item = gtk.MenuItem(_("Link place"))
+ add_item.connect("activate", self._link_place, event, lat , lon)
+ add_item.show()
+ menu.append(add_item)
+
+ add_item = gtk.MenuItem(_("Center here"))
+ add_item.connect("activate", self.set_center, event, lat , lon)
+ add_item.show()
+ menu.append(add_item)
+
+ # Add specific module menu
+ self.add_specific_menu(menu, event, lat, lon)
+ # Add a separator line
+ add_item = gtk.MenuItem(None)
+ add_item.show()
+ menu.append(add_item)
+
+ map_name = constants.map_title[config.get("geography.map_service")]
+ title = _("Replace '%(map)s' by =>" % {
+ 'map' : map_name
+ })
+ add_item = gtk.MenuItem(title)
+ add_item.show()
+ menu.append(add_item)
+
+ changemap = gtk.Menu()
+ changemap.set_title(title)
+ changemap.show()
+ add_item.set_submenu(changemap)
+ # show in the map menu all available providers
+ for map in constants.map_type:
+ changemapitem = gtk.MenuItem(constants.map_title[map])
+ changemapitem.show()
+ changemapitem.connect("activate", self.change_map, map)
+ changemap.append(changemapitem)
+ menu.popup(None, None, None, 0, event.time)
+ return 1
+
+ def add_specific_menu(self, menu, event, lat, lon):
+ """
+ Add specific entry to the navigation menu.
+ Must be done in the associated menu.
+ """
+ raise NotImplementedError
+
+ def set_center(self, menu, event, lat, lon):
+ """
+ Center the map at the new position then save it.
+ """
+ self.osm.set_center_and_zoom(lat, lon, 12)
+ self.save_center(lat, lon)
+
+ #-------------------------------------------------------------------------
+ #
+ # Markers management
+ #
+ #-------------------------------------------------------------------------
+ def is_there_a_marker_here(self, event, lat, lon):
+ """
+ Is there a marker at this position ?
+ """
+ found = False
+ mark_selected = []
+ self.uistate.set_busy_cursor(1)
+ for mark in self.sort:
+ # as we are not precise with our hand, reduce the precision
+ # depending on the zoom.
+ precision = {
+ 1 : '%3.0f', 2 : '%3.0f', 3 : '%3.0f', 4 : '%3.0f',
+ 5 : '%3.0f', 6 : '%3.1f', 7 : '%3.1f', 8 : '%3.1f',
+ 9 : '%3.1f', 10 : '%3.1f', 11 : '%3.2f', 12 : '%3.2f',
+ 13 : '%3.2f', 14 : '%3.3f', 15 : '%3.3f', 16 : '%3.4f',
+ 17 : '%3.4f', 18 : '%3.5f'
+ }.get(config.get("geography.zoom"), '%3.1f')
+ latp = precision % lat
+ lonp = precision % lon
+ mlatp = precision % float(mark[3])
+ mlonp = precision % float(mark[4])
+ _LOG.debug(" compare latitude : %s with %s (precision = %s)"
+ " place='%s'" % (float(mark[3]), lat, precision,
+ mark[0]))
+ _LOG.debug("compare longitude : %s with %s (precision = %s)"
+ " zoom=%d" % (float(mark[4]), lon, precision,
+ config.get("geography.zoom")))
+ if mlatp == latp and mlonp == lonp:
+ _LOG.debug(" compare latitude : %s with %s OK" % (mlatp, latp))
+ _LOG.debug("compare longitude : %s with %s OK" % (mlonp, lonp))
+ mark_selected.append(mark)
+ found = True
+ if found:
+ self.bubble_message(event, lat, lon, mark_selected)
+ self.uistate.set_busy_cursor(0)
+
+ def bubble_message(self, event, lat, lon, mark):
+ """
+ Display the bubble message. depends on the view.
+ """
+ raise NotImplementedError
+
+ def add_marker(self, menu, event, lat, lon, event_type, differtype):
+ """
+ Add a new marker
+ """
+ mapservice = config.get('geography.map_service')
+ if ( mapservice in ( constants.OPENSTREETMAP,
+ constants.OPENSTREETMAP_RENDERER )):
+ default_image = self.geo_mainmap
+ else:
+ default_image = self.geo_altmap
+ value = default_image
+ if event_type is not None:
+ value = self.geo_othermap.get(int(event_type), default_image)
+ if differtype: # in case multiple evts
+ value = default_image # we use default icon.
+ marker = self.osm.image_add_with_alignment(float(lat),
+ float(lon), value, 0.2, 1.0)
+
+ def remove_all_gps(self):
+ """
+ Remove all gps points on the map
+ """
+ self.osm.gps_clear()
+
+ def remove_all_tracks(self):
+ """
+ Remove all tracks on the map
+ """
+ self.osm.track_remove_all()
+
+ def remove_all_markers(self):
+ """
+ Remove all markers on the map
+ """
+ self.osm.image_remove_all()
+
+ def _present_in_places_list(self, index, string):
+ """
+ Search a string in place_list depending index
+ """
+ found = any(p[index] == string for p in self.place_list)
+ return found
+
+ def _append_to_places_list(self, place, evttype, name, lat,
+ longit, descr, year, icontype,
+ gramps_id, place_id, event_id, family_id
+ ):
+ """
+ Create a list of places with coordinates.
+ """
+ found = any(p[0] == place for p in self.place_list)
+ if not found:
+ self.nbplaces += 1
+ self.place_list.append([place, name, evttype, lat,
+ longit, descr, year, icontype,
+ gramps_id, place_id, event_id, family_id
+ ])
+ self.nbmarkers += 1
+ tfa = float(lat)
+ tfb = float(longit)
+ if year is not None:
+ tfc = int(year)
+ if tfc != 0:
+ if tfc < self.minyear:
+ self.minyear = tfc
+ if tfc > self.maxyear:
+ self.maxyear = tfc
+ tfa += 0.00000001 if tfa >= 0 else -0.00000001
+ tfb += 0.00000001 if tfb >= 0 else -0.00000001
+ if self.minlat == 0.0 or tfa < self.minlat:
+ self.minlat = tfa
+ if self.maxlat == 0.0 or tfa > self.maxlat:
+ self.maxlat = tfa
+ if self.minlon == 0.0 or tfb < self.minlon:
+ self.minlon = tfb
+ if self.maxlon == 0.0 or tfb > self.maxlon:
+ self.maxlon = tfb
+
+ def _append_to_places_without_coord(self, gid, place):
+ """
+ Create a list of places without coordinates.
+ """
+ if not [gid, place] in self.place_without_coordinates:
+ self.place_without_coordinates.append([gid, place])
+ self.without += 1
+
+ def _create_markers(self):
+ """
+ Create all markers for the specified person.
+ """
+ self.remove_all_markers()
+ self.remove_all_gps()
+ self.remove_all_tracks()
+ if ( self.current_map is not None and
+ self.current_map != config.get("geography.map_service") ):
+ self.change_map(self.osm, config.get("geography.map_service"))
+ last = ""
+ current = ""
+ differtype = False
+ savetype = None
+ lat = 0.0
+ lon = 0.0
+ icon = None
+ self.uistate.set_busy_cursor(True)
+ _LOG.debug("%s" % time.strftime("start create_marker : "
+ "%a %d %b %Y %H:%M:%S", time.gmtime()))
+ for mark in self.sort:
+ current = ([mark[3], mark[4]])
+ if last == "":
+ last = current
+ lat = mark[3]
+ lon = mark[4]
+ icon = mark[7]
+ differtype = False
+ continue
+ if last != current:
+ self.add_marker(None, None, lat, lon, icon, differtype)
+ differtype = False
+ last = current
+ lat = mark[3]
+ lon = mark[4]
+ icon = mark[7]
+ differtype = False
+ else: # This marker already exists. add info.
+ if ( mark[6] and icon != mark[7] ):
+ differtype = True
+ if ( lat != 0.0 and lon != 0.0 ):
+ self.add_marker(None, None, lat, lon, icon, differtype)
+ self._set_center_and_zoom()
+ _LOG.debug("%s" % time.strftime(" stop create_marker : "
+ "%a %d %b %Y %H:%M:%S", time.gmtime()))
+ self.uistate.set_busy_cursor(False)
+
+ def _set_center_and_zoom(self):
+ """
+ Calculate the zoom.
+ The best should be an auto zoom to have all markers on the screen.
+ need some works here.
+ we start at zoom 1 until zoom y ( for this a preference )
+ If all markers are present, continue to zoom.
+ If some markers are missing : return to the zoom - 1
+ The following is too complex. In some case, all markers are not present.
+ """
+ # Select the center of the map and the zoom
+ signminlon = _get_sign(self.minlon)
+ signminlat = _get_sign(self.minlat)
+ signmaxlon = _get_sign(self.maxlon)
+ signmaxlat = _get_sign(self.maxlat)
+ # auto zoom ?
+ if signminlon == signmaxlon:
+ maxlong = abs(abs(self.minlon) - abs(self.maxlon))
+ else:
+ maxlong = abs(abs(self.minlon) + abs(self.maxlon))
+ if signminlat == signmaxlat:
+ maxlat = abs(abs(self.minlat) - abs(self.maxlat))
+ else:
+ maxlat = abs(abs(self.minlat) + abs(self.maxlat))
+ # Calculate the zoom. all places must be displayed on the map.
+ zoomlat = _get_zoom_lat(maxlat)
+ zoomlong = _get_zoom_long(maxlong)
+ self.new_zoom = zoomlat if zoomlat < zoomlong else zoomlong
+ self.new_zoom -= 1
+ if self.new_zoom < 2:
+ self.new_zoom = 2
+ # We center the map on a point at the center of all markers
+ self.centerlat = maxlat/2
+ self.centerlon = maxlong/2
+ latit = longt = 0.0
+ for mark in self.sort:
+ if ( signminlat == signmaxlat ):
+ if signminlat == 1:
+ latit = self.minlat+self.centerlat
+ else:
+ latit = self.maxlat-self.centerlat
+ elif self.maxlat > self.centerlat:
+ latit = self.maxlat-self.centerlat
+ else:
+ latit = self.minlat+self.centerlat
+ if ( signminlon == signmaxlon ):
+ if signminlon == 1:
+ longt = self.minlon+self.centerlon
+ else:
+ longt = self.maxlon-self.centerlon
+ elif self.maxlon > self.centerlon:
+ longt = self.maxlon-self.centerlon
+ else:
+ longt = self.minlon+self.centerlon
+ # all maps: 0.0 for longitude and latitude means no location.
+ if latit == longt == 0.0:
+ latit = longt = 0.00000001
+ self.mustcenter = False
+ self.latit = latit
+ self.longt = longt
+ if not (latit == longt == 0.0):
+ self.mustcenter = True
+ if config.get("geography.lock"):
+ self.osm.set_center_and_zoom(config.get("geography.center-lat"),
+ config.get("geography.center-lon"),
+ config.get("geography.zoom") )
+ else:
+ self.osm.set_center_and_zoom(self.latit, self.longt, self.new_zoom)
+ self.save_center(self.latit, self.longt)
+ config.set("geography.zoom",self.new_zoom)
+
+ #-------------------------------------------------------------------------
+ #
+ # Specific functionalities
+ #
+ #-------------------------------------------------------------------------
+ def center_here(self, menu, event, lat, lon, mark):
+ """
+ Center the map at the marker position
+ """
+ self.set_center(menu, event, float(mark[3]), float(mark[4]))
+
+ def add_place_bubble_message(self, event, lat, lon, marks,
+ menu, message, mark):
+ """
+ Create the place menu of a marker
+ """
+ add_item = gtk.MenuItem()
+ add_item.show()
+ menu.append(add_item)
+ add_item = gtk.MenuItem(message)
+ add_item.show()
+ menu.append(add_item)
+ itemoption = gtk.Menu()
+ itemoption.set_title(message)
+ itemoption.show()
+ add_item.set_submenu(itemoption)
+ modify = gtk.MenuItem(_("Edit place"))
+ modify.show()
+ modify.connect("activate", self.edit_place, event, lat, lon, mark)
+ itemoption.append(modify)
+ center = gtk.MenuItem(_("Center on this place"))
+ center.show()
+ center.connect("activate", self.center_here, event, lat, lon, mark)
+ itemoption.append(center)
+ add_item = gtk.MenuItem()
+ add_item.show()
+ menu.append(add_item)
+
+ def edit_place(self, menu, event, lat, lon, mark):
+ """
+ Edit the selected place at the marker position
+ """
+ _LOG.debug("edit_place : %s" % mark[9])
+ # need to add code here to edit the event.
+ place = self.dbstate.db.get_place_from_gramps_id(mark[9])
+ try:
+ EditPlace(self.dbstate, self.uistate, [], place)
+ except Errors.WindowActiveError:
+ pass
+
+ def edit_person(self, menu, event, lat, lon, mark):
+ """
+ Edit the selected person at the marker position
+ """
+ _LOG.debug("edit_person : %s" % mark[8])
+ # need to add code here to edit the person.
+ person = self.dbstate.db.get_person_from_gramps_id(mark[8])
+ try:
+ EditPerson(self.dbstate, self.uistate, [], person)
+ except Errors.WindowActiveError:
+ pass
+
+ def edit_family(self, menu, event, lat, lon, mark):
+ """
+ Edit the selected family at the marker position
+ """
+ _LOG.debug("edit_family : %s" % mark[11])
+ # need to add code here to edit the family.
+ family = self.dbstate.db.get_family_from_gramps_id(mark[11])
+ try:
+ EditFamily(self.dbstate, self.uistate, [], family)
+ except Errors.WindowActiveError:
+ pass
+
+ def edit_event(self, menu, event, lat, lon, mark):
+ """
+ Edit the selected event at the marker position
+ """
+ _LOG.debug("edit_event : %s" % mark[10])
+ # need to add code here to edit the event.
+ event = self.dbstate.db.get_event_from_gramps_id(mark[10])
+ try:
+ EditEvent(self.dbstate, self.uistate, [], event)
+ except Errors.WindowActiveError:
+ pass
+
+ def _add_place(self, menu, event, lat, lon):
+ """
+ Add a new place using longitude and latitude of location centered
+ on the map
+ """
+ new_place = gen.lib.Place()
+ new_place.set_latitude(str(lat))
+ new_place.set_longitude(str(lon))
+ try:
+ EditPlace(self.dbstate, self.uistate, [], new_place)
+ except Errors.WindowActiveError:
+ pass
+
+ def _link_place(self, menu, event, lat, lon):
+ """
+ Link an existing place using longitude and latitude of location centered
+ on the map
+ """
+ selector = SelectPlace(self.dbstate, self.uistate, [])
+ place = selector.run()
+ if place:
+ place.set_latitude(str(lat))
+ place.set_longitude(str(lon))
+ try:
+ EditPlace(self.dbstate, self.uistate, [], place)
+ except Errors.WindowActiveError:
+ pass
+
+ #-------------------------------------------------------------------------
+ #
+ # Geography preferences
+ #
+ #-------------------------------------------------------------------------
+ def _get_configure_page_funcs(self):
+ """
+ The function which is used to create the configuration window.
+ """
+ return [self.map_options]
+
+ def config_zoom_and_position(self, client, cnxn_id, entry, data):
+ """
+ Do we need to lock the zoom and position ?
+ """
+ if config.get("geography.lock"):
+ config.set("geography.lock", False)
+ self._set_center_and_zoom()
+ else:
+ config.set("geography.lock", True)
+ self.lock = config.get("geography.lock")
+ pass
+
+ def config_crosshair(self, client, cnxn_id, entry, data):
+ """
+ We asked to change the crosshair.
+ """
+ if config.get("geography.show_cross"):
+ config.set("geography.show_cross", False)
+ else:
+ config.set("geography.show_cross", True)
+ self.set_crosshair(config.get("geography.show_cross"))
+ pass
+
+ def map_options(self, configdialog):
+ """
+ Function that builds the widget in the configuration dialog
+ for the map options.
+ """
+ table = gtk.Table(2, 2)
+ table.set_border_width(12)
+ table.set_col_spacings(6)
+ table.set_row_spacings(6)
+ configdialog.add_text(table,
+ _('Where to save the tiles for offline mode.'),
+ 1)
+ configdialog.add_entry(table, '',
+ 2, 'geography.path')
+ configdialog.add_text(table,
+ _('If you have no more space in your file system\n'
+ 'You can remove all tiles placed in the above path.\n'
+ 'Be careful! If you have no internet, you\'ll get no map.'),
+ 3)
+ # there is no button. I need to found a solution for this.
+ # it can be very dangerous ! if someone put / in geography.path ...
+ # perhaps we need some contrĂ´l on this path :
+ # should begin with : /home, /opt, /map, ...
+ #configdialog.add_button(table, '', 4, 'geography.clean')
+
+ return _('The map'), table
diff --git a/src/plugins/lib/maps/grampsmaps.py b/src/plugins/lib/maps/grampsmaps.py
new file mode 100644
index 000000000..871810ca3
--- /dev/null
+++ b/src/plugins/lib/maps/grampsmaps.py
@@ -0,0 +1,193 @@
+# -*- python -*-
+# -*- coding: utf-8 -*-
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2011 Serge Noiraud
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+# $Id: $
+
+#-------------------------------------------------------------------------
+#
+# Python modules
+#
+#-------------------------------------------------------------------------
+import sys
+import os
+import gobject
+
+#------------------------------------------------------------------------
+#
+# Set up logging
+#
+#------------------------------------------------------------------------
+import logging
+_LOG = logging.getLogger("maps.osmgpsmap")
+
+#-------------------------------------------------------------------------
+#
+# GTK/Gnome modules
+#
+#-------------------------------------------------------------------------
+import gtk
+
+#-------------------------------------------------------------------------
+#
+# Gramps Modules
+#
+#-------------------------------------------------------------------------
+import const
+import constants
+from gen.ggettext import sgettext as _
+from gen.ggettext import ngettext
+from config import config
+
+#-------------------------------------------------------------------------
+#
+# Constants
+#
+#-------------------------------------------------------------------------
+GEOGRAPHY_PATH = os.path.join(const.HOME_DIR, "maps")
+
+#-------------------------------------------------------------------------
+#
+# osmGpsMap
+#
+#-------------------------------------------------------------------------
+
+try:
+ import osmgpsmap
+except:
+ raise
+
+class DummyMapNoGpsPoint(osmgpsmap.GpsMap):
+ def do_draw_gps_point(self, drawable):
+ pass
+gobject.type_register(DummyMapNoGpsPoint)
+
+class DummyLayer(gobject.GObject, osmgpsmap.GpsMapLayer):
+ def __init__(self):
+ gobject.GObject.__init__(self)
+
+ def do_draw(self, gpsmap, gdkdrawable):
+ pass
+
+ def do_render(self, gpsmap):
+ pass
+
+ def do_busy(self):
+ return False
+
+ def do_button_press(self, gpsmap, gdkeventbutton):
+ return False
+gobject.type_register(DummyLayer)
+
+class osmGpsMap():
+ def __init__(self):
+ self.vbox = None
+ self.cross_map = None
+ self.osm = None
+ self.show_tooltips = True
+
+ def build_widget(self):
+ self.vbox = gtk.VBox(False, 0)
+ cache_path = os.path.join(const.HOME_DIR, 'maps')
+ if not os.path.isdir(cache_path):
+ try:
+ os.mkdir(cache_path, 0750)
+ except:
+ ErrorDialog(_("Can't create tiles cache directory %s") %
+ cache_path )
+ return self.vbox
+
+ self.change_map(None,config.get("geography.map_service"))
+ return self.vbox
+
+ def change_map(self, obj, map_type):
+ if obj is not None:
+ self.osm.layer_remove_all()
+ self.osm.image_remove_all()
+ self.vbox.remove(self.osm)
+ self.osm.destroy()
+ tiles_path=os.path.join(GEOGRAPHY_PATH, constants.tiles_path[map_type])
+ if not os.path.isdir(tiles_path):
+ try:
+ os.mkdir(tiles_path, 0750)
+ except:
+ ErrorDialog(_("Can't create tiles cache directory for '%s'.") %
+ constants.map_title[map_type])
+ config.set("geography.map_service", map_type)
+ self.current_map = map_type
+ if 0:
+ self.osm = DummyMapNoGpsPoint()
+ else:
+ self.osm = osmgpsmap.GpsMap(tile_cache=tiles_path,
+ map_source=constants.map_type[map_type])
+ current_map = osmgpsmap.GpsMapOsd( show_dpad=False, show_zoom=True)
+ self.osm.layer_add(current_map)
+ self.osm.layer_add( DummyLayer())
+ self.cross_map = osmgpsmap.GpsMapOsd( show_crosshair=False)
+ self.set_crosshair(config.get("geography.show_cross"))
+ self.osm.set_center_and_zoom(config.get("geography.center-lat"),
+ config.get("geography.center-lon"),
+ config.get("geography.zoom") )
+
+ self.osm.connect('button_release_event', self.map_clicked)
+ self.osm.connect('changed', self.zoom_changed)
+ self.osm.show()
+ self.vbox.pack_start(self.osm)
+ if obj is not None:
+ self._createmap(None)
+
+ def zoom_changed(self, zoom):
+ config.set("geography.zoom",self.osm.props.zoom)
+ self.save_center(self.osm.props.latitude, self.osm.props.longitude)
+
+ def save_center(self, lat, lon):
+ """
+ Save the longitude and lontitude in case we switch between maps.
+ """
+ config.set("geography.center-lat",lat)
+ config.set("geography.center-lon",lon)
+
+ def map_clicked(self, osm, event):
+ lat,lon = self.osm.get_event_location(event).get_degrees()
+ if event.button == 1:
+ # do we click on a marker ?
+ marker = self.is_there_a_marker_here(event, lat, lon)
+ elif event.button == 3:
+ self.build_nav_menu(osm, event, lat, lon )
+ else:
+ self.save_center(lat,lon)
+
+ def is_there_a_marker_here(self, lat, lon):
+ raise NotImplementedError
+
+ def set_crosshair(self,active):
+ """
+ Show or hide the crosshair ?
+ """
+ if active:
+ self.cross_map = osmgpsmap.GpsMapOsd( show_crosshair=True)
+ self.osm.layer_add( self.cross_map )
+ # The two following are to force the map to update
+ self.osm.zoom_in()
+ self.osm.zoom_out()
+ else:
+ self.osm.layer_remove(self.cross_map)
+ pass
diff --git a/src/plugins/view/Makefile.am b/src/plugins/view/Makefile.am
index bed3cf7a7..bfa7b61ab 100644
--- a/src/plugins/view/Makefile.am
+++ b/src/plugins/view/Makefile.am
@@ -10,8 +10,12 @@ pkgdata_PYTHON = \
familyview.py \
fanchartview.gpr.py \
fanchartview.py \
- geoview.py \
- geoview.gpr.py \
+ geoevents.py \
+ geoplaces.py \
+ geoperson.py \
+ geofamily.py \
+ geography.gpr.py \
+ htmlrenderer.gpr.py \
grampletview.py \
htmlrenderer.py \
mediaview.py \
diff --git a/src/plugins/view/geoevents.py b/src/plugins/view/geoevents.py
new file mode 100644
index 000000000..32622e229
--- /dev/null
+++ b/src/plugins/view/geoevents.py
@@ -0,0 +1,382 @@
+# -*- python -*-
+# -*- coding: utf-8 -*-
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2011 Serge Noiraud
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+# $Id: $
+
+"""
+Geography for events
+"""
+#-------------------------------------------------------------------------
+#
+# Python modules
+#
+#-------------------------------------------------------------------------
+from gen.ggettext import gettext as _
+import os
+import sys
+import urlparse
+import const
+import operator
+import locale
+from gtk.keysyms import Tab as KEY_TAB
+import socket
+import gtk
+
+#-------------------------------------------------------------------------
+#
+# set up logging
+#
+#-------------------------------------------------------------------------
+import logging
+_LOG = logging.getLogger("GeoGraphy.geoevents")
+
+#-------------------------------------------------------------------------
+#
+# Gramps Modules
+#
+#-------------------------------------------------------------------------
+import gen.lib
+import Utils
+import config
+import Errors
+from gen.display.name import displayer as _nd
+from PlaceUtils import conv_lat_lon
+from gui.views.pageview import PageView
+from gui.editors import EditPlace
+from gui.selectors.selectplace import SelectPlace
+from Filters.SideBar import EventSidebarFilter
+from gui.views.navigationview import NavigationView
+import Bookmarks
+from Utils import navigation_label
+from maps.geography import GeoGraphyView
+
+#-------------------------------------------------------------------------
+#
+# Constants
+#
+#-------------------------------------------------------------------------
+
+_UI_DEF = '''\
+
+
+
+
+
+
+
+
+
+
+
+
+'''
+
+#-------------------------------------------------------------------------
+#
+# GeoView
+#
+#-------------------------------------------------------------------------
+class GeoEvents(GeoGraphyView):
+ """
+ The view used to render events map.
+ """
+
+ def __init__(self, pdata, dbstate, uistate, nav_group=0):
+ GeoGraphyView.__init__(self, _('Events places map'),
+ pdata, dbstate, uistate,
+ dbstate.db.get_event_bookmarks(),
+ Bookmarks.EventBookmarks,
+ nav_group)
+ self.dbstate = dbstate
+ self.uistate = uistate
+ self.place_list = []
+ self.place_without_coordinates = []
+ self.minlat = self.maxlat = self.minlon = self.maxlon = 0.0
+ self.minyear = 9999
+ self.maxyear = 0
+ self.nbplaces = 0
+ self.nbmarkers = 0
+ self.sort = []
+ self.generic_filter = None
+ self.additional_uis.append(self.additional_ui())
+
+ def get_title(self):
+ """
+ Used to set the titlebar in the configuration window.
+ """
+ return _('GeoEvents')
+
+ def get_stock(self):
+ """
+ Returns the name of the stock icon to use for the display.
+ This assumes that this icon has already been registered
+ as a stock icon.
+ """
+ return 'geo-show-events'
+
+ def get_viewtype_stock(self):
+ """Type of view in category
+ """
+ return 'geo-show-events'
+
+ def additional_ui(self):
+ """
+ Specifies the UIManager XML code that defines the menus and buttons
+ associated with the interface.
+ """
+ return _UI_DEF
+
+ def navigation_type(self):
+ """
+ Indicates the navigation type. Navigation type can be the string
+ name of any of the primary objects.
+ """
+ return 'Event'
+
+ def get_bookmarks(self):
+ """
+ Return the bookmark object
+ """
+ return self.dbstate.db.get_event_bookmarks()
+
+ def goto_handle(self, handle=None):
+ """
+ Rebuild the tree with the given events handle as the root.
+ """
+ if handle:
+ self.change_active(handle)
+ self._createmap(handle)
+ self.uistate.modify_statusbar(self.dbstate)
+
+ def show_all_events(self, menu, event, lat, lon):
+ """
+ Ask to show all events.
+ """
+ self._createmap(None)
+
+ def build_tree(self):
+ """
+ This is called by the parent class when the view becomes visible. Since
+ all handling of visibility is now in rebuild_trees, see that for more
+ information.
+ """
+ active = self.uistate.get_active('Event')
+ if active:
+ self._createmap(active)
+ else:
+ self._createmap(None)
+
+ def _createmap_for_one_event(self,event):
+ """
+ Create all markers for each people's event in the database which has
+ a lat/lon.
+ """
+ dbstate = self.dbstate
+ descr = descr2 = ""
+ place_handle = event.get_place_handle()
+ eventyear = event.get_date_object().to_calendar(self.cal).get_year()
+ if place_handle:
+ place = dbstate.db.get_place_from_handle(place_handle)
+ if place:
+ descr1 = place.get_title()
+ longitude = place.get_longitude()
+ latitude = place.get_latitude()
+ latitude, longitude = conv_lat_lon(latitude, longitude, "D.D8")
+ # place.get_longitude and place.get_latitude return
+ # one string. We have coordinates when the two values
+ # contains non null string.
+ if ( longitude and latitude ):
+ person_list = [
+ dbstate.db.get_person_from_handle(ref_handle)
+ for (ref_type, ref_handle) in
+ dbstate.db.find_backlink_handles(event.handle)
+ if ref_type == 'Person'
+ ]
+ if person_list:
+ for person in person_list:
+ if descr2 == "":
+ descr2 = ("%s") % _nd.display(person)
+ else:
+ descr2 = ("%s - %s") % ( descr2,
+ _nd.display(person))
+ else:
+ # family list ?
+ family_list = [
+ dbstate.db.get_family_from_handle(ref_handle)
+ for (ref_type, ref_handle) in
+ dbstate.db.find_backlink_handles(event.handle)
+ if ref_type == 'Family'
+ ]
+ if family_list:
+ for family in family_list:
+ hdle = family.get_father_handle()
+ father = dbstate.db.get_person_from_handle(hdle)
+ hdle = family.get_mother_handle()
+ mother = dbstate.db.get_person_from_handle(hdle)
+ descr2 = ("%(father)s - %(mother)s") % {
+ 'father': _nd.display(father) if father is not None else "?",
+ 'mother': _nd.display(mother) if mother is not None else "?"
+ }
+ else:
+ descr2 = _("incomplete or unreferenced event ?")
+ self._append_to_places_list(descr1, None,
+ None,
+ latitude, longitude,
+ descr2,
+ eventyear,
+ event.get_type(),
+ None, # person.gramps_id
+ place.gramps_id,
+ event.gramps_id,
+ None
+ )
+ else:
+ descr = place.get_title()
+ self._append_to_places_without_coord(
+ place.gramps_id, descr)
+
+ def _createmap(self,obj):
+ """
+ Create all markers for each people's event in the database which has
+ a lat/lon.
+ """
+ dbstate = self.dbstate
+ self.place_list = []
+ self.place_without_coordinates = []
+ self.minlat = self.maxlat = self.minlon = self.maxlon = 0.0
+ self.minyear = 9999
+ self.maxyear = 0
+ latitude = ""
+ longitude = ""
+ self.without = 0
+ self.cal = config.get('preferences.calendar-format-report')
+
+ if self.generic_filter:
+ events_list = self.generic_filter.apply(dbstate.db)
+ for event_handle in events_list:
+ event = dbstate.db.get_event_from_handle(event_handle)
+ self._createmap_for_one_event(event)
+ else:
+ if obj is None:
+ events_handle = dbstate.db.iter_event_handles()
+ for event_hdl in events_handle:
+ event = dbstate.db.get_event_from_handle(event_hdl)
+ self._createmap_for_one_event(event)
+ else:
+ event = dbstate.db.get_event_from_handle(obj)
+ self._createmap_for_one_event(event)
+ self.sort = sorted(self.place_list,
+ key=operator.itemgetter(6)
+ )
+ self._create_markers()
+
+ def bubble_message(self, event, lat, lon, marks):
+ menu = gtk.Menu()
+ menu.set_title("events")
+ message = ""
+ oldplace = ""
+ prevmark = None
+ for mark in marks:
+ if message != "":
+ add_item = gtk.MenuItem(message)
+ add_item.show()
+ menu.append(add_item)
+ itemoption = gtk.Menu()
+ itemoption.set_title(message)
+ itemoption.show()
+ add_item.set_submenu(itemoption)
+ modify = gtk.MenuItem(_("Edit event"))
+ modify.show()
+ modify.connect("activate", self.edit_event,
+ event, lat, lon, prevmark)
+ itemoption.append(modify)
+ center = gtk.MenuItem(_("Center on this place"))
+ center.show()
+ center.connect("activate", self.center_here,
+ event, lat, lon, prevmark)
+ itemoption.append(center)
+ if mark[0] != oldplace:
+ if message != "":
+ add_item = gtk.MenuItem(message)
+ add_item.show()
+ menu.append(add_item)
+ itemoption = gtk.Menu()
+ itemoption.set_title(message)
+ itemoption.show()
+ add_item.set_submenu(itemoption)
+ modify = gtk.MenuItem(_("Edit event"))
+ modify.show()
+ modify.connect("activate", self.edit_event,
+ event, lat, lon, mark)
+ itemoption.append(modify)
+ center = gtk.MenuItem(_("Center on this place"))
+ center.show()
+ center.connect("activate", self.center_here,
+ event, lat, lon, mark)
+ itemoption.append(center)
+ message = "%s :" % mark[0]
+ self.add_place_bubble_message(event, lat, lon,
+ marks, menu, message, mark)
+ oldplace = mark[0]
+ message = "%s : %s" % (gen.lib.EventType( mark[7] ), mark[5] )
+ prevmark = mark
+ add_item = gtk.MenuItem(message)
+ add_item.show()
+ menu.append(add_item)
+ itemoption = gtk.Menu()
+ itemoption.set_title(message)
+ itemoption.show()
+ add_item.set_submenu(itemoption)
+ modify = gtk.MenuItem(_("Edit event"))
+ modify.show()
+ modify.connect("activate", self.edit_event, event, lat, lon, prevmark)
+ itemoption.append(modify)
+ center = gtk.MenuItem(_("Center on this place"))
+ center.show()
+ center.connect("activate", self.center_here, event, lat, lon, prevmark)
+ itemoption.append(center)
+ menu.popup(None, None, None, 0, event.time)
+ return 1
+
+ def add_specific_menu(self, menu, event, lat, lon):
+ """
+ Add specific entry to the navigation menu.
+ """
+ add_item = gtk.MenuItem()
+ add_item.show()
+ menu.append(add_item)
+ add_item = gtk.MenuItem(_("Show all events"))
+ add_item.connect("activate", self.show_all_events, event, lat , lon)
+ add_item.show()
+ menu.append(add_item)
+
diff --git a/src/plugins/view/geofamily.py b/src/plugins/view/geofamily.py
new file mode 100644
index 000000000..10f303c2c
--- /dev/null
+++ b/src/plugins/view/geofamily.py
@@ -0,0 +1,430 @@
+# -*- python -*-
+# -*- coding: utf-8 -*-
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2011 Serge Noiraud
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+# $Id: $
+
+"""
+Geography for one family
+"""
+#-------------------------------------------------------------------------
+#
+# Python modules
+#
+#-------------------------------------------------------------------------
+from gen.ggettext import gettext as _
+import os
+import sys
+import urlparse
+import const
+import operator
+import locale
+from gtk.keysyms import Tab as KEY_TAB
+import socket
+import gtk
+
+#-------------------------------------------------------------------------
+#
+# set up logging
+#
+#-------------------------------------------------------------------------
+import logging
+_LOG = logging.getLogger("GeoGraphy.geofamily")
+
+#-------------------------------------------------------------------------
+#
+# Gramps Modules
+#
+#-------------------------------------------------------------------------
+import gen.lib
+import Utils
+import config
+import Errors
+from gen.display.name import displayer as _nd
+from PlaceUtils import conv_lat_lon
+from gui.views.pageview import PageView
+from gui.editors import EditPlace
+from gui.selectors.selectplace import SelectPlace
+from Filters.SideBar import FamilySidebarFilter
+from gui.views.navigationview import NavigationView
+import Bookmarks
+from Utils import navigation_label
+from maps.geography import GeoGraphyView
+
+#-------------------------------------------------------------------------
+#
+# Constants
+#
+#-------------------------------------------------------------------------
+
+_UI_DEF = '''\
+
+
+
+
+
+
+
+
+
+
+
+
+'''
+
+#-------------------------------------------------------------------------
+#
+# GeoView
+#
+#-------------------------------------------------------------------------
+class GeoFamily(GeoGraphyView):
+ """
+ The view used to render person map.
+ """
+
+ def __init__(self, pdata, dbstate, uistate, nav_group=0):
+ GeoGraphyView.__init__(self, _('Family places map'),
+ pdata, dbstate, uistate,
+ dbstate.db.get_family_bookmarks(),
+ Bookmarks.FamilyBookmarks,
+ nav_group)
+ self.dbstate = dbstate
+ self.uistate = uistate
+ self.place_list = []
+ self.place_without_coordinates = []
+ self.minlat = self.maxlat = self.minlon = self.maxlon = 0.0
+ self.minyear = 9999
+ self.maxyear = 0
+ self.nbplaces = 0
+ self.nbmarkers = 0
+ self.sort = []
+ self.additional_uis.append(self.additional_ui())
+
+ def get_title(self):
+ """
+ Used to set the titlebar in the configuration window.
+ """
+ return _('GeoFamily')
+
+ def get_stock(self):
+ """
+ Returns the name of the stock icon to use for the display.
+ This assumes that this icon has already been registered
+ as a stock icon.
+ """
+ return 'geo-show-family'
+
+ def get_viewtype_stock(self):
+ """Type of view in category
+ """
+ return 'geo-show-family'
+
+ def additional_ui(self):
+ """
+ Specifies the UIManager XML code that defines the menus and buttons
+ associated with the interface.
+ """
+ return _UI_DEF
+
+ def navigation_type(self):
+ """
+ Indicates the navigation type. Navigation type can be the string
+ name of any of the primary objects.
+ """
+ return 'Family'
+
+ def get_bookmarks(self):
+ """
+ Return the bookmark object
+ """
+ return self.dbstate.db.get_family_bookmarks()
+
+ def goto_handle(self, handle=None):
+ """
+ Rebuild the tree with the given person handle as the root.
+ """
+ if handle:
+ self.change_active(handle)
+ self._createmap(handle)
+ self.uistate.modify_statusbar(self.dbstate)
+
+ def build_tree(self):
+ """
+ This is called by the parent class when the view becomes visible. Since
+ all handling of visibility is now in rebuild_trees, see that for more
+ information.
+ """
+ if self.uistate.get_active('Family'):
+ self._createmap(self.uistate.get_active('Family'))
+ else:
+ self._createmap(self.uistate.get_active('Person'))
+
+ def _get_father_and_mother_name(self, event):
+ """
+ Return the father and mother name of a family event
+ """
+ dbstate = self.dbstate
+ family_list = [
+ dbstate.db.get_family_from_handle(ref_handle)
+ for (ref_type, ref_handle) in
+ dbstate.db.find_backlink_handles(event.handle)
+ if ref_type == 'Family'
+ ]
+ if family_list:
+ for family in family_list:
+ handle = family.get_father_handle()
+ father = dbstate.db.get_person_from_handle(handle)
+ handle = family.get_mother_handle()
+ mother = dbstate.db.get_person_from_handle(handle)
+ fnam = _nd.display(father) if father else "???"
+ mnam = _nd.display(mother) if mother else "???"
+ return ( fnam, mnam )
+
+ def _createpersonmarkers(self, dbstate, person, comment, fam_id):
+ """
+ Create all markers for the specified person.
+ """
+ self.cal = config.get('preferences.calendar-format-report')
+ latitude = longitude = ""
+ if person:
+ # For each event, if we have a place, set a marker.
+ for event_ref in person.get_event_ref_list():
+ if not event_ref:
+ continue
+ event = dbstate.db.get_event_from_handle(event_ref.ref)
+ eyear = event.get_date_object().to_calendar(self.cal).get_year()
+ place_handle = event.get_place_handle()
+ if place_handle:
+ place = dbstate.db.get_place_from_handle(place_handle)
+ if place:
+ longitude = place.get_longitude()
+ latitude = place.get_latitude()
+ latitude, longitude = conv_lat_lon(latitude,
+ longitude, "D.D8")
+ descr = place.get_title()
+ evt = gen.lib.EventType(event.get_type())
+ descr1 = _("%(eventtype)s : %(name)s") % {
+ 'eventtype': evt,
+ 'name': _nd.display(person)}
+ # place.get_longitude and place.get_latitude return
+ # one string. We have coordinates when the two values
+ # contains non null string.
+ if ( longitude and latitude ):
+ if not self._present_in_places_list(2, str(descr1 + descr + str(evt))):
+ self._append_to_places_list(descr,
+ str(descr1 + descr + str(evt)),
+ _nd.display(person),
+ latitude, longitude,
+ descr1, eyear,
+ event.get_type(),
+ person.gramps_id,
+ place.gramps_id,
+ event.gramps_id,
+ fam_id
+ )
+ else:
+ self._append_to_places_without_coord(
+ place.gramps_id, descr)
+ family_list = person.get_family_handle_list()
+ for family_hdl in family_list:
+ family = self.dbstate.db.get_family_from_handle(family_hdl)
+ if family is not None:
+ for event_ref in family.get_event_ref_list():
+ if event_ref:
+ event = dbstate.db.get_event_from_handle(event_ref.ref)
+ if event.get_place_handle():
+ place_handle = event.get_place_handle()
+ if place_handle:
+ place = dbstate.db.get_place_from_handle(place_handle)
+ if place:
+ longitude = place.get_longitude()
+ latitude = place.get_latitude()
+ latitude, longitude = conv_lat_lon(latitude,
+ longitude, "D.D8")
+ descr = place.get_title()
+ evt = gen.lib.EventType(event.get_type())
+ (father_name, mother_name) = self._get_father_and_mother_name(event)
+ descr1 = "%s : %s - " % ( evt, father_name )
+ descr1 = "%s%s" % ( descr1, mother_name )
+ eyear = event.get_date_object().to_calendar(self.cal).get_year()
+ if ( longitude and latitude ):
+ if not self._present_in_places_list(2, str(descr1 + descr + str(evt))):
+ self._append_to_places_list(descr,
+ str(descr1 + descr + str(evt)),
+ _nd.display(person),
+ latitude, longitude,
+ descr1, eyear,
+ event.get_type(),
+ person.gramps_id,
+ place.gramps_id,
+ event.gramps_id,
+ family.gramps_id
+ )
+ else:
+ self._append_to_places_without_coord( place.gramps_id, descr)
+
+ def _createmap_for_one_family(self, family):
+ """
+ Create all markers for one family : all event's places with a lat/lon.
+ """
+ dbstate = self.dbstate
+ try:
+ person = dbstate.db.get_person_from_handle(family.get_father_handle())
+ except:
+ return
+ family_id = family.gramps_id
+ if person is None: # family without father ?
+ person = dbstate.db.get_person_from_handle(family.get_mother_handle())
+ if person is None:
+ person = dbstate.db.get_person_from_handle(self.uistate.get_active('Person'))
+ if person is not None:
+ family_list = person.get_family_handle_list()
+ if len(family_list) > 0:
+ fhandle = family_list[0] # first is primary
+ fam = dbstate.db.get_family_from_handle(fhandle)
+ handle = fam.get_father_handle()
+ father = dbstate.db.get_person_from_handle(handle)
+ if father:
+ comment = _("Father : %s : %s") % ( father.gramps_id,
+ _nd.display(father) )
+ self._createpersonmarkers(dbstate, father,
+ comment, family_id)
+ handle = fam.get_mother_handle()
+ mother = dbstate.db.get_person_from_handle(handle)
+ if mother:
+ comment = _("Mother : %s : %s") % ( mother.gramps_id,
+ _nd.display(mother) )
+ self._createpersonmarkers(dbstate, mother,
+ comment, family_id)
+ index = 0
+ child_ref_list = fam.get_child_ref_list()
+ if child_ref_list:
+ for child_ref in child_ref_list:
+ child = dbstate.db.get_person_from_handle(child_ref.ref)
+ if child:
+ index += 1
+ comment = _("Child : %(id)s - %(index)d "
+ ": %(name)s") % {
+ 'id' : child.gramps_id,
+ 'index' : index,
+ 'name' : _nd.display(child)
+ }
+ self._createpersonmarkers(dbstate, child,
+ comment, family_id)
+ else:
+ comment = _("Person : %(id)s %(name)s has no family.") % {
+ 'id' : person.gramps_id ,
+ 'name' : _nd.display(person)
+ }
+ self._createpersonmarkers(dbstate, person, comment, family_id)
+
+ def _createmap(self, family_x):
+ """
+ Create all markers for each people's event in the database which has
+ a lat/lon.
+ """
+ self.place_list = []
+ self.place_without_coordinates = []
+ self.minlat = self.maxlat = self.minlon = self.maxlon = 0.0
+ self.minyear = 9999
+ self.maxyear = 0
+ family = self.dbstate.db.get_family_from_handle(family_x)
+ if family is None:
+ person = self.dbstate.db.get_person_from_handle(self.uistate.get_active('Person'))
+ if not person:
+ return
+ family_list = person.get_family_handle_list()
+ for family_hdl in family_list:
+ family = self.dbstate.db.get_family_from_handle(family_hdl)
+ if family is not None:
+ self._createmap_for_one_family(family)
+ else:
+ self._createmap_for_one_family(family)
+ self.sort = sorted(self.place_list,
+ key=operator.itemgetter(3, 4, 6)
+ )
+ self._create_markers()
+
+ def add_event_bubble_message(self, event, lat, lon, mark, menu):
+ itemoption = gtk.Menu()
+ itemoption.show()
+ menu.set_submenu(itemoption)
+ modify = gtk.MenuItem(_("Edit family"))
+ modify.show()
+ modify.connect("activate", self.edit_family, event, lat, lon, mark)
+ itemoption.append(modify)
+ modify = gtk.MenuItem(_("Edit person"))
+ modify.show()
+ modify.connect("activate", self.edit_person, event, lat, lon, mark)
+ itemoption.append(modify)
+ modify = gtk.MenuItem(_("Edit event"))
+ modify.show()
+ modify.connect("activate", self.edit_event, event, lat, lon, mark)
+ itemoption.append(modify)
+ center = gtk.MenuItem(_("Center on this place"))
+ center.show()
+ center.connect("activate", self.center_here, event, lat, lon, mark)
+ itemoption.append(center)
+
+ def bubble_message(self, event, lat, lon, marks):
+ menu = gtk.Menu()
+ menu.set_title("family")
+ message = ""
+ oldplace = ""
+ prevmark = None
+ for mark in marks:
+ if message != "":
+ add_item = gtk.MenuItem(message)
+ add_item.show()
+ menu.append(add_item)
+ self.add_event_bubble_message(event, lat, lon,
+ prevmark, add_item)
+ if mark[0] != oldplace:
+ message = "%s :" % mark[0]
+ self.add_place_bubble_message(event, lat, lon,
+ marks, menu, message, mark)
+ oldplace = mark[0]
+ message = "%s" % mark[5]
+ prevmark = mark
+ add_item = gtk.MenuItem(message)
+ add_item.show()
+ menu.append(add_item)
+ self.add_event_bubble_message(event, lat, lon, prevmark, add_item)
+ menu.popup(None, None, None, 0, event.time)
+ return 1
+
+ def add_specific_menu(self, menu, event, lat, lon):
+ """
+ Add specific entry to the navigation menu.
+ """
+ return
+
diff --git a/src/plugins/view/geography.gpr.py b/src/plugins/view/geography.gpr.py
new file mode 100644
index 000000000..740ce9801
--- /dev/null
+++ b/src/plugins/view/geography.gpr.py
@@ -0,0 +1,104 @@
+# encoding:utf-8
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2011 Serge Noiraud
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+# $Id: $
+
+#------------------------------------------------------------------------
+#
+# Geography view
+#
+#------------------------------------------------------------------------
+
+try :
+ import osmgpsmap
+ OSMGPSMAP = True
+except:
+ OSMGPSMAP = False
+ print _("WARNING: osmgpsmap module not loaded. "
+ "Geography functionality will not be available.")
+
+if OSMGPSMAP:
+ # Load the view only if osmgpsmap library is present.
+ register(VIEW,
+ id = 'personmap',
+ name = _("person"),
+ description = _("A view allowing to see the places visited by "
+ "one person during his life."),
+ version = '1.0',
+ gramps_target_version = '3.3',
+ status = STABLE,
+ fname = 'geoperson.py',
+ authors = [u"Serge Noiraud"],
+ authors_email = [""],
+ category = ("Geography", _("Geography")),
+ viewclass = 'GeoPerson',
+ order = START,
+ stock_icon = 'geo-show-person',
+ )
+
+ register(VIEW,
+ id = 'placesmap',
+ name = _("places"),
+ description = _("A view allowing to see all places of the database."),
+ version = '1.0',
+ gramps_target_version = '3.3',
+ status = STABLE,
+ fname = 'geoplaces.py',
+ authors = [u"Serge Noiraud"],
+ authors_email = [""],
+ category = ("Geography", _("Geography")),
+ viewclass = 'GeoPlaces',
+ stock_icon = 'geo-show-place',
+ )
+
+ register(VIEW,
+ id = 'eventsmap',
+ name = _("events"),
+ description = _("A view allowing to see all events "
+ "places of the database."),
+ version = '1.0',
+ gramps_target_version = '3.3',
+ status = STABLE,
+ fname = 'geoevents.py',
+ authors = [u"Serge Noiraud"],
+ authors_email = [""],
+ category = ("Geography", _("Geography")),
+ viewclass = 'GeoEvents',
+ stock_icon = 'geo-show-event',
+ )
+
+ register(VIEW,
+ id = 'familymap',
+ name = _("family"),
+ description = _("A view allowing to see the places visited by "
+ "one family during all their life."),
+ version = '1.0',
+ gramps_target_version = '3.3',
+ status = STABLE,
+ order = START,
+ fname = 'geofamily.py',
+ authors = [u"Serge Noiraud"],
+ authors_email = [""],
+ category = ("Geography", _("Geography")),
+ viewclass = 'GeoFamily',
+ stock_icon = 'geo-show-family',
+ )
+
diff --git a/src/plugins/view/geoperson.py b/src/plugins/view/geoperson.py
new file mode 100644
index 000000000..0ed2164e0
--- /dev/null
+++ b/src/plugins/view/geoperson.py
@@ -0,0 +1,431 @@
+# -*- python -*-
+# -*- coding: utf-8 -*-
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2011 Serge Noiraud
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+# $Id: $
+
+"""
+Geography for one person
+"""
+#-------------------------------------------------------------------------
+#
+# Python modules
+#
+#-------------------------------------------------------------------------
+from gen.ggettext import gettext as _
+import os
+import sys
+import urlparse
+import const
+import operator
+import locale
+from gtk.keysyms import Tab as KEY_TAB
+import socket
+import gtk
+import glib
+
+#-------------------------------------------------------------------------
+#
+# set up logging
+#
+#-------------------------------------------------------------------------
+import logging
+_LOG = logging.getLogger("GeoGraphy.geoperson")
+
+#-------------------------------------------------------------------------
+#
+# Gramps Modules
+#
+#-------------------------------------------------------------------------
+import gen.lib
+import Utils
+import config
+import Errors
+from gen.display.name import displayer as _nd
+from PlaceUtils import conv_lat_lon
+from gui.views.pageview import PageView
+from gui.editors import EditPlace
+from gui.selectors.selectplace import SelectPlace
+from Filters.SideBar import PersonSidebarFilter
+from gui.views.navigationview import NavigationView
+import Bookmarks
+from Utils import navigation_label
+from maps.geography import GeoGraphyView
+
+#-------------------------------------------------------------------------
+#
+# Constants
+#
+#-------------------------------------------------------------------------
+
+_UI_DEF = '''\
+
+
+
+
+
+
+
+
+
+
+
+
+
+'''
+
+#-------------------------------------------------------------------------
+#
+# GeoView
+#
+#-------------------------------------------------------------------------
+class GeoPerson(GeoGraphyView):
+ """
+ The view used to render person map.
+ """
+
+ def __init__(self, pdata, dbstate, uistate, nav_group=0):
+ GeoGraphyView.__init__(self, _("Person places map"),
+ pdata, dbstate, uistate,
+ dbstate.db.get_bookmarks(),
+ Bookmarks.PersonBookmarks,
+ nav_group)
+ self.dbstate = dbstate
+ self.uistate = uistate
+ self.place_list = []
+ self.place_without_coordinates = []
+ self.minlat = self.maxlat = self.minlon = self.maxlon = 0.0
+ self.minyear = 9999
+ self.maxyear = 0
+ self.nbplaces = 0
+ self.nbmarkers = 0
+ self.sort = []
+ self.additional_uis.append(self.additional_ui())
+
+ def get_title(self):
+ """
+ Used to set the titlebar in the configuration window.
+ """
+ return _('GeoPerson')
+
+ def get_stock(self):
+ """
+ Returns the name of the stock icon to use for the display.
+ This assumes that this icon has already been registered
+ as a stock icon.
+ """
+ return 'geo-show-person'
+
+ def get_viewtype_stock(self):
+ """Type of view in category
+ """
+ return 'geo-show-person'
+
+ def additional_ui(self):
+ """
+ Specifies the UIManager XML code that defines the menus and buttons
+ associated with the interface.
+ """
+ return _UI_DEF
+
+ def navigation_type(self):
+ """
+ Indicates the navigation type. Navigation type can be the string
+ name of any of the primary objects.
+ """
+ return 'Person'
+
+ def get_bookmarks(self):
+ """
+ Return the bookmark object
+ """
+ return self.dbstate.db.get_bookmarks()
+
+ def goto_handle(self, handle=None):
+ """
+ Rebuild the tree with the given person handle as the root.
+ """
+ if handle:
+ self.change_active(handle)
+ self._createmap(handle)
+ self.uistate.modify_statusbar(self.dbstate)
+
+ def build_tree(self):
+ """
+ This is called by the parent class when the view becomes visible. Since
+ all handling of visibility is now in rebuild_trees, see that for more
+ information.
+ """
+ active = self.get_active()
+ self._createmap(active)
+
+ def animate(self, menu, marks, index, stepyear):
+ """
+ Create all movements for the people's event.
+ Yes, you can see the person moving.
+ """
+ if len(marks) == 0:
+ self.already_started = False
+ return False
+ i = int(index)
+ ni = i + 1
+ if ni == len(marks) :
+ self.already_started = False
+ return False
+ startlat = float(marks[i][3])
+ startlon = float(marks[i][4])
+ heading = 1
+ if index == 0 and stepyear == 0:
+ self.remove_all_gps()
+ self.osm.gps_add(startlat, startlon, heading)
+ endlat = float(marks[ni][3])
+ endlon = float(marks[ni][4])
+ # year format = YYYYMMDD ( for sort )
+ startyear = str(marks[i][6])[0:4]
+ endyear = str(marks[ni][6])[0:4]
+ endmov = str(marks[len(marks)-1][6])[0:4]
+ years = int(endyear) - int(startyear)
+ if years < 1:
+ years = 1
+ latstep = ( endlat - startlat ) / years
+ lonstep = ( endlon - startlon ) / years
+ stepyear = 1 if stepyear < 1 else stepyear
+ startlat += ( latstep * stepyear )
+ startlon += ( lonstep * stepyear )
+ if ( int(startyear) + stepyear ) > int(endmov) :
+ self.already_started = False
+ return False
+ self.osm.gps_add(startlat, startlon, heading)
+ stepyear += 1
+ difflat = ( startlat - endlat ) if startlat > endlat else \
+ ( endlat - startlat )
+ difflon = ( startlon - endlon ) if startlon > endlon else \
+ ( endlon - startlon )
+ if ( difflat == 0.0 and difflon == 0.0 ):
+ i += 1
+ if ( int(startyear) + stepyear ) > int(endmov) :
+ self.already_started = False
+ return False
+ stepyear = 1
+ # 100ms => 1s per 10 years.
+ # For a 100 years person, it takes 10 secondes.
+ glib.timeout_add(100, self.animate, menu, marks, i, stepyear)
+ return False
+
+ def _createmap(self,obj):
+ """
+ Create all markers for each people's event in the database which has
+ a lat/lon.
+ """
+ dbstate = self.dbstate
+ self.cal = config.get('preferences.calendar-format-report')
+ self.place_list = []
+ self.place_without_coordinates = []
+ self.minlat = self.maxlat = self.minlon = self.maxlon = 0.0
+ self.minyear = 9999
+ self.maxyear = 0
+ latitude = ""
+ longitude = ""
+ person_handle = self.uistate.get_active('Person')
+ person = dbstate.db.get_person_from_handle(person_handle)
+ if person is not None:
+ # For each event, if we have a place, set a marker.
+ for event_ref in person.get_event_ref_list():
+ if not event_ref:
+ continue
+ event = dbstate.db.get_event_from_handle(event_ref.ref)
+ eyear = str("%04d" % event.get_date_object().to_calendar(self.cal).get_year()) + \
+ str("%02d" % event.get_date_object().to_calendar(self.cal).get_month()) + \
+ str("%02d" % event.get_date_object().to_calendar(self.cal).get_day())
+ place_handle = event.get_place_handle()
+ if place_handle:
+ place = dbstate.db.get_place_from_handle(place_handle)
+ if place:
+ longitude = place.get_longitude()
+ latitude = place.get_latitude()
+ latitude, longitude = conv_lat_lon(latitude,
+ longitude, "D.D8")
+ descr = place.get_title()
+ evt = gen.lib.EventType(event.get_type())
+ descr1 = _("%(eventtype)s : %(name)s") % {
+ 'eventtype': evt,
+ 'name': _nd.display(person)}
+ # place.get_longitude and place.get_latitude return
+ # one string. We have coordinates when the two values
+ # contains non null string.
+ if ( longitude and latitude ):
+ self._append_to_places_list(descr, evt,
+ _nd.display(person),
+ latitude, longitude,
+ descr1, eyear,
+ event.get_type(),
+ person.gramps_id,
+ place.gramps_id,
+ event.gramps_id,
+ None
+ )
+ else:
+ self._append_to_places_without_coord(
+ place.gramps_id, descr)
+ family_list = person.get_family_handle_list()
+ for family_hdl in family_list:
+ family = self.dbstate.db.get_family_from_handle(family_hdl)
+ if family is not None:
+ fhandle = family_list[0] # first is primary
+ fam = dbstate.db.get_family_from_handle(fhandle)
+ handle = fam.get_father_handle()
+ father = dbstate.db.get_person_from_handle(handle)
+ if father:
+ descr1 = "%s - " % _nd.display(father)
+ handle = fam.get_mother_handle()
+ mother = dbstate.db.get_person_from_handle(handle)
+ if mother:
+ descr1 = "%s%s" % ( descr1, _nd.display(mother))
+ for event_ref in family.get_event_ref_list():
+ if event_ref:
+ event = dbstate.db.get_event_from_handle(event_ref.ref)
+ if event.get_place_handle():
+ place_handle = event.get_place_handle()
+ if place_handle:
+ place = dbstate.db.get_place_from_handle(place_handle)
+ if place:
+ longitude = place.get_longitude()
+ latitude = place.get_latitude()
+ latitude, longitude = conv_lat_lon(latitude,
+ longitude, "D.D8")
+ descr = place.get_title()
+ evt = gen.lib.EventType(event.get_type())
+ eyear = str("%04d" % event.get_date_object().to_calendar(self.cal).get_year()) + \
+ str("%02d" % event.get_date_object().to_calendar(self.cal).get_month()) + \
+ str("%02d" % event.get_date_object().to_calendar(self.cal).get_day())
+ if ( longitude and latitude ):
+ self._append_to_places_list(descr,
+ evt, _nd.display(person),
+ latitude, longitude,
+ descr1, eyear,
+ event.get_type(),
+ person.gramps_id,
+ place.gramps_id,
+ event.gramps_id,
+ None
+ )
+ else:
+ self._append_to_places_without_coord( place.gramps_id, descr)
+
+ self.sort = sorted(self.place_list,
+ key=operator.itemgetter(6)
+ )
+ self._create_markers()
+
+ def bubble_message(self, event, lat, lon, marks):
+ menu = gtk.Menu()
+ menu.set_title("person")
+ message = ""
+ oldplace = ""
+ prevmark = None
+ for mark in marks:
+ if oldplace != "":
+ add_item = gtk.MenuItem(message)
+ add_item.show()
+ menu.append(add_item)
+ itemoption = gtk.Menu()
+ itemoption.set_title(message)
+ itemoption.show()
+ add_item.set_submenu(itemoption)
+ modify = gtk.MenuItem(_("Edit event"))
+ modify.show()
+ modify.connect("activate", self.edit_event,
+ event, lat, lon, prevmark)
+ itemoption.append(modify)
+ center = gtk.MenuItem(_("Center on this place"))
+ center.show()
+ center.connect("activate", self.center_here,
+ event, lat, lon, prevmark)
+ itemoption.append(center)
+ if mark[0] != oldplace:
+ if message != "":
+ add_item = gtk.MenuItem()
+ add_item.show()
+ menu.append(add_item)
+ itemoption = gtk.Menu()
+ itemoption.set_title(message)
+ itemoption.show()
+ add_item.set_submenu(itemoption)
+ modify = gtk.MenuItem(_("Edit event"))
+ modify.show()
+ modify.connect("activate", self.edit_event,
+ event, lat, lon, mark)
+ itemoption.append(modify)
+ center = gtk.MenuItem(_("Center on this place"))
+ center.show()
+ center.connect("activate", self.center_here,
+ event, lat, lon, mark)
+ itemoption.append(center)
+ message = "%s :" % mark[0]
+ self.add_place_bubble_message(event, lat, lon,
+ marks, menu, message, mark)
+ oldplace = mark[0]
+ message = "%s : %s" % ( mark[2], mark[1] )
+ prevmark = mark
+ add_item = gtk.MenuItem(message)
+ add_item.show()
+ menu.append(add_item)
+ itemoption = gtk.Menu()
+ itemoption.set_title(message)
+ itemoption.show()
+ add_item.set_submenu(itemoption)
+ modify = gtk.MenuItem(_("Edit event"))
+ modify.show()
+ modify.connect("activate", self.edit_event, event, lat, lon, prevmark)
+ itemoption.append(modify)
+ center = gtk.MenuItem(_("Center on this place"))
+ center.show()
+ center.connect("activate", self.center_here, event, lat, lon, prevmark)
+ itemoption.append(center)
+ menu.popup(None, None, None, 0, event.time)
+ return 1
+
+ def add_specific_menu(self, menu, event, lat, lon):
+ """
+ Add specific entry to the navigation menu.
+ """
+ add_item = gtk.MenuItem()
+ add_item.show()
+ menu.append(add_item)
+ add_item = gtk.MenuItem(_("Animate"))
+ add_item.connect("activate", self.animate, self.sort, 0, 0)
+ add_item.show()
+ menu.append(add_item)
+ return
+
diff --git a/src/plugins/view/geoplaces.py b/src/plugins/view/geoplaces.py
new file mode 100644
index 000000000..b8ab76a04
--- /dev/null
+++ b/src/plugins/view/geoplaces.py
@@ -0,0 +1,327 @@
+# -*- python -*-
+# -*- coding: utf-8 -*-
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2011 Serge Noiraud
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+# $Id: $
+
+"""
+Geography for places
+"""
+#-------------------------------------------------------------------------
+#
+# Python modules
+#
+#-------------------------------------------------------------------------
+from gen.ggettext import gettext as _
+import os
+import sys
+import time
+import urlparse
+import const
+import operator
+import locale
+from gtk.keysyms import Tab as KEY_TAB
+import socket
+import gtk
+
+#-------------------------------------------------------------------------
+#
+# set up logging
+#
+#-------------------------------------------------------------------------
+import logging
+_LOG = logging.getLogger("GeoGraphy.geoplaces")
+
+#-------------------------------------------------------------------------
+#
+# Gramps Modules
+#
+#-------------------------------------------------------------------------
+import gen.lib
+import Utils
+import config
+import Errors
+from gen.display.name import displayer as _nd
+from PlaceUtils import conv_lat_lon
+from gui.views.pageview import PageView
+from gui.editors import EditPlace
+from gui.selectors.selectplace import SelectPlace
+from Filters.SideBar import PlaceSidebarFilter
+from gui.views.navigationview import NavigationView
+import Bookmarks
+from Utils import navigation_label
+from maps.geography import GeoGraphyView
+
+#-------------------------------------------------------------------------
+#
+# Constants
+#
+#-------------------------------------------------------------------------
+
+_UI_DEF = '''\
+
+
+
+
+
+
+
+
+
+
+
+
+'''
+
+#-------------------------------------------------------------------------
+#
+# GeoView
+#
+#-------------------------------------------------------------------------
+class GeoPlaces(GeoGraphyView):
+ """
+ The view used to render places map.
+ """
+
+ def __init__(self, pdata, dbstate, uistate, nav_group=0):
+ GeoGraphyView.__init__(self, _('Places places map'),
+ pdata, dbstate, uistate,
+ dbstate.db.get_place_bookmarks(),
+ Bookmarks.PlaceBookmarks,
+ nav_group)
+ self.dbstate = dbstate
+ self.uistate = uistate
+ self.place_list = []
+ self.place_without_coordinates = []
+ self.minlat = self.maxlat = self.minlon = self.maxlon = 0.0
+ self.minyear = 9999
+ self.maxyear = 0
+ self.nbplaces = 0
+ self.nbmarkers = 0
+ self.sort = []
+ self.generic_filter = None
+ self.additional_uis.append(self.additional_ui())
+
+ def get_title(self):
+ """
+ Used to set the titlebar in the configuration window.
+ """
+ return _('GeoPlaces')
+
+ def get_stock(self):
+ """
+ Returns the name of the stock icon to use for the display.
+ This assumes that this icon has already been registered
+ as a stock icon.
+ """
+ return 'geo-show-place'
+
+ def get_viewtype_stock(self):
+ """Type of view in category
+ """
+ return 'geo-show-place'
+
+ def additional_ui(self):
+ """
+ Specifies the UIManager XML code that defines the menus and buttons
+ associated with the interface.
+ """
+ return _UI_DEF
+
+ def navigation_type(self):
+ """
+ Indicates the navigation type. Navigation type can be the string
+ name of any of the primary objects.
+ """
+ return 'Place'
+
+ def get_bookmarks(self):
+ """
+ Return the bookmark object
+ """
+ return self.dbstate.db.get_place_bookmarks()
+
+ def goto_handle(self, handle=None):
+ """
+ Rebuild the tree with the given places handle as the root.
+ """
+ if handle:
+ self.change_active(handle)
+ self._createmap(handle)
+ self.uistate.modify_statusbar(self.dbstate)
+
+ def show_all_places(self, menu, event, lat, lon):
+ """
+ Ask to show all places.
+ """
+ self._createmap(None)
+
+ def build_tree(self):
+ """
+ This is called by the parent class when the view becomes visible. Since
+ all handling of visibility is now in rebuild_trees, see that for more
+ information.
+ """
+ active = self.uistate.get_active('Place')
+ if active:
+ self._createmap(active)
+ else:
+ self._createmap(None)
+
+ def _create_one_place(self,place):
+ """
+ Create one entry for one place with a lat/lon.
+ """
+ descr = place.get_title()
+ longitude = place.get_longitude()
+ latitude = place.get_latitude()
+ latitude, longitude = conv_lat_lon(latitude, longitude, "D.D8")
+ # place.get_longitude and place.get_latitude return
+ # one string. We have coordinates when the two values
+ # contains non null string.
+ if ( longitude and latitude ):
+ self._append_to_places_list(descr, None, "",
+ latitude, longitude,
+ None, None,
+ gen.lib.EventType.UNKNOWN,
+ None, # person.gramps_id
+ place.gramps_id,
+ None, # event.gramps_id
+ None # family.gramps_id
+ )
+ else:
+ self._append_to_places_without_coord(place.gramps_id, descr)
+
+ def _createmap(self,place_x):
+ """
+ Create all markers for each people's event in the database which has
+ a lat/lon.
+ """
+ dbstate = self.dbstate
+ self.cal = config.get('preferences.calendar-format-report')
+ self.place_list = []
+ self.place_without_coordinates = []
+ self.minlat = 0.0
+ self.maxlat = 0.0
+ self.minlon = 0.0
+ self.maxlon = 0.0
+ self.minyear = 9999
+ self.maxyear = 0
+ self.without = 0
+ latitude = ""
+ longitude = ""
+ # base "villes de france" : 38101 places :
+ # createmap : 8'50"; create_markers : 1'23"
+ # base "villes de france" : 38101 places :
+ # createmap : 8'50"; create_markers : 0'07" with pixbuf optimization
+ _LOG.debug("%s" % time.strftime("start createmap : "
+ "%a %d %b %Y %H:%M:%S", time.gmtime()))
+ if self.generic_filter:
+ place_list = self.generic_filter.apply(dbstate.db)
+ for place_handle in place_list:
+ place = dbstate.db.get_place_from_handle(place_handle)
+ self._create_one_place(place)
+ else:
+ if place_x is None:
+ places_handle = dbstate.db.iter_place_handles()
+ for place_hdl in places_handle:
+ place = dbstate.db.get_place_from_handle(place_hdl)
+ self._create_one_place(place)
+ else:
+ place = dbstate.db.get_place_from_handle(place_x)
+ self._create_one_place(place)
+ _LOG.debug("%s" % time.strftime(" stop createmap and\nbegin sort : "
+ "%a %d %b %Y %H:%M:%S", time.gmtime()))
+ self.sort = sorted(self.place_list,
+ key=operator.itemgetter(0)
+ )
+ _LOG.debug("%s" % time.strftime(" end sort : "
+ "%a %d %b %Y %H:%M:%S", time.gmtime()))
+ self._create_markers()
+
+ def bubble_message(self, event, lat, lon, marks):
+ menu = gtk.Menu()
+ menu.set_title("places")
+ message = ""
+ prevmark = None
+ for mark in marks:
+ if message != "":
+ add_item = gtk.MenuItem(message)
+ add_item.show()
+ menu.append(add_item)
+ itemoption = gtk.Menu()
+ itemoption.set_title(message)
+ itemoption.show()
+ add_item.set_submenu(itemoption)
+ modify = gtk.MenuItem(_("Edit place"))
+ modify.show()
+ modify.connect("activate", self.edit_place,
+ event, lat, lon, prevmark)
+ itemoption.append(modify)
+ center = gtk.MenuItem(_("Center on this place"))
+ center.show()
+ center.connect("activate", self.center_here,
+ event, lat, lon, prevmark)
+ itemoption.append(center)
+ message = "%s" % mark[0]
+ prevmark = mark
+ add_item = gtk.MenuItem(message)
+ add_item.show()
+ menu.append(add_item)
+ itemoption = gtk.Menu()
+ itemoption.set_title(message)
+ itemoption.show()
+ add_item.set_submenu(itemoption)
+ modify = gtk.MenuItem(_("Edit place"))
+ modify.show()
+ modify.connect("activate", self.edit_place, event, lat, lon, prevmark)
+ itemoption.append(modify)
+ center = gtk.MenuItem(_("Center on this place"))
+ center.show()
+ center.connect("activate", self.center_here, event, lat, lon, prevmark)
+ itemoption.append(center)
+ menu.popup(None, None, None, 0, event.time)
+ return 1
+
+ def add_specific_menu(self, menu, event, lat, lon):
+ """
+ Add specific entry to the navigation menu.
+ """
+ add_item = gtk.MenuItem()
+ add_item.show()
+ menu.append(add_item)
+ add_item = gtk.MenuItem(_("Show all places"))
+ add_item.connect("activate", self.show_all_places, event, lat , lon)
+ add_item.show()
+ menu.append(add_item)
+
+
diff --git a/src/plugins/view/geoview.py b/src/plugins/view/geoview.py
deleted file mode 100644
index 08a2f6401..000000000
--- a/src/plugins/view/geoview.py
+++ /dev/null
@@ -1,2512 +0,0 @@
-# -*- python -*-
-# -*- coding: utf-8 -*-
-#
-# Gramps - a GTK+/GNOME based genealogy program
-#
-# Copyright (C) 2007-2009 Serge Noiraud
-# Copyright (C) 2008 Benny Malengier
-# Copyright (C) 2009 Gerald Britton
-# Copyright (C) 2009 Helge GRAMPS
-# Copyright (C) 2009 Josip
-# Copyright (C) 2009 Gary Burton
-# Copyright (C) 2009 Nick Hall
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-
-# $Id$
-
-"""
-Geo View
-"""
-#-------------------------------------------------------------------------
-#
-# Python modules
-#
-#-------------------------------------------------------------------------
-from gen.ggettext import gettext as _
-import os
-import sys
-import urlparse
-import const
-import operator
-import locale
-from gtk.keysyms import Tab as KEY_TAB
-import socket
-
-#-------------------------------------------------------------------------
-#
-# set up logging
-#
-#-------------------------------------------------------------------------
-import logging
-_LOG = logging.getLogger("GeoGraphy")
-
-#-------------------------------------------------------------------------
-#
-# GTK/Gnome modules
-#
-#-------------------------------------------------------------------------
-import gtk
-import pango
-import gobject
-
-#-------------------------------------------------------------------------
-#
-# Gramps Modules
-#
-#-------------------------------------------------------------------------
-import gen.lib
-import Utils
-import config
-import Errors
-from gen.display.name import displayer as _nd
-from PlaceUtils import conv_lat_lon
-from gui.views.navigationview import NavigationView
-from gui.editors import EditPlace
-from gui.selectors.selectplace import SelectPlace
-import Bookmarks
-from Utils import navigation_label
-
-
-#-------------------------------------------------------------------------
-#
-# map icons
-#
-#-------------------------------------------------------------------------
-_ICONS = {
- gen.lib.EventType.BIRTH : 'gramps-geo-birth',
- gen.lib.EventType.DEATH : 'gramps-geo-death',
- gen.lib.EventType.MARRIAGE : 'gramps-geo-marriage',
-}
-
-#-------------------------------------------------------------------------
-#
-# regexp for html title Notes ...
-#
-#-------------------------------------------------------------------------
-import re
-ZOOMANDPOS = re.compile('zoom=([0-9]*) coord=([0-9\.\-\+]*), ([0-9\.\-\+]*):::')
-
-#-------------------------------------------------------------------------
-#
-# Web interfaces
-#
-#-------------------------------------------------------------------------
-
-URL_SEP = '/'
-
-from htmlrenderer import HtmlView
-
-#-------------------------------------------------------------------------
-#
-# Constants
-#
-#-------------------------------------------------------------------------
-#covert to unicode for better hadnling of path in Windows
-GEOVIEW_SUBPATH = Utils.get_empty_tempdir('geoview')
-
-#-------------------------------------------------------------------------
-#
-# Constants
-#
-#-------------------------------------------------------------------------
-
-_HTMLHEADER = '''\
-
-
-
-
- %(title)s
-
- %(css)s
-'''
-
-_JAVASCRIPT = '''\
-
-
-
-'''
-
-#-------------------------------------------------------------------------
-#
-# Functions
-#
-#-------------------------------------------------------------------------
-def _get_sign(value):
- """
- return 1 if we have a negative number, 0 in other case
- """
- if value < 0.0:
- return 1
- else:
- return 0
-
-def _get_zoom_lat(value):
- """
- return the zoom value for latitude depending on the distance.
- """
- zoomlat = 1
- for i, distance in enumerate([80.0, 40.0, 20.0, 10.0, 3.0,
- 2.0, 1.0, 0.5, 0.2, 0.1]):
- if value < distance:
- zoomlat = i+1
- return zoomlat + 2
-
-def _get_zoom_long(value):
- """
- return the zoom value for longitude depending on the distance.
- """
- zoomlong = 1
- for i, distance in enumerate([120.0, 60.0, 30.0, 15.0, 7.0,
- 4.0, 2.0, 1.0, .5, .2, .1]):
- if value < distance:
- zoomlong = i+1
- return zoomlong + 2
-
-def _make_callback(func, val):
- """
- return a function
- """
- return lambda x: func(val)
-
-def _escape(text):
- """
- return the text with some characters translated : " &
- """
- text = text.replace('&','\\&')
- text = text.replace('"','\\"')
- return text
-
-#-------------------------------------------------------------------------
-#
-# GeoView
-#
-#-------------------------------------------------------------------------
-class GeoView(HtmlView):
- """
- The view used to render html pages.
- """
- CONFIGSETTINGS = (
- ('preferences.alternate-provider', False),
- ('preferences.timeperiod-before-range', 10),
- ('preferences.timeperiod-after-range', 10),
- ('preferences.crosshair', False),
- ('preferences.markers', 200),
- ('preferences.coordinates-in-degree', False),
- ('preferences.network-test', False),
- ('preferences.network-timeout', 5),
- ('preferences.network-periodicity', 10),
- ('preferences.network-site', 'www.gramps-project.org'),
- ('preferences.webkit', False),
- )
-
- def __init__(self, pdata, dbstate, uistate):
- HtmlView.__init__(self, pdata, dbstate, uistate, title=_("GeoView"))
- self.dbstate = dbstate
- self.uistate = uistate
- self.dbstate.connect('database-changed', self._new_database)
- self.widget = None
- self.invalidpath = const.ROOT_DIR.find("(")
- if self.invalidpath != -1:
- _LOG.debug("\n\nInvalid PATH (avoid parenthesis):\n%s\n\n" %
- const.ROOT_DIR)
- self.displaytype = "person"
- self.active_filter = 'Person Filter Gramplet'
-
- self.additional_uis.append(self.additional_ui())
- self.resize_occurs = False
- self.Navigator_min_width = 0
-
- def build_widget(self):
- self.no_network = False
- self.placeslist = []
- self.nbmarkers = 0
- self.nbplaces = 0
- self.without = 0
- self.nbpages = 0
- self.last_index = 0
- self.yearinmarker = []
- self.javascript_ready = False
- self.mustcenter = False
- self.centerlat = self.centerlon = 0.0
- self.setattr = True
- self.latit = self.longt = 0.0
- self.height = self.width = 0.0
- self.zoom = 1
- self.lock_action = None
- self.realzoom = 0
- self.reallatitude = self.reallongitude = 0.0
- self.cal = 0
- if config.get('geoview.lock'):
- self.realzoom = config.get('geoview.zoom')
- self.displaytype = config.get('geoview.map')
- self.reallatitude, self.reallongitude = conv_lat_lon(
- config.get('geoview.latitude'),
- config.get('geoview.longitude'),
- "D.D8")
- if self.displaytype == "places":
- self.active_filter = 'Place Filter Gramplet'
- elif self.displaytype == "event":
- self.active_filter = 'Event Filter Gramplet'
- elif self.displaytype == "family":
- self.active_filter = 'Family Filter Gramplet'
- else:
- self.active_filter = 'Person Filter Gramplet'
-
- self.minyear = self.maxyear = 1
- self.maxbut = 10
- self.mapview = None
- self.yearint = 0
- self.centered = True
- self.center = True
- self.place_list = []
- self.htmlfile = ""
- self.places = []
- self.sort = []
- self.psort = []
- self.clear = gtk.Button("")
- self.clear.set_tooltip_text(
- _("Clear the entry field in the places selection box.")
- )
- self.savezoom = gtk.Button("")
- self.savezoom.connect("clicked", self._save_zoom)
- self.savezoom.set_tooltip_text(
- _("Save the zoom and coordinates between places "
- "map, person map, family map and event map.")
- )
- self.provider = gtk.Button("")
- self.provider.connect("clicked", self._change_provider)
- self.provider.set_tooltip_text(
- _("Select the maps provider. You can choose "
- "between OpenStreetMap and Google maps.")
- )
- self.buttons = gtk.ListStore(gobject.TYPE_STRING, # The year
- )
- self.plist = gtk.ListStore(gobject.TYPE_STRING, # The name
- gobject.TYPE_INT, # the marker index
- gobject.TYPE_INT # the marker page
- )
- # I suppress sort in the combobox for performances.
- # I tried to load a database with more than 35000 places.
- # with the sort function, its takes approximatively 20 minutes
- # to see the combobox and the map.
- # Without the sort function, it takes approximatively 4 minutes.
- #self.plist.set_sort_column_id(0, gtk.SORT_ASCENDING)
- self.without_coord_file = []
- self.place_without_coordinates = []
- self.minlat = self.maxlat = self.minlon = self.maxlon = 0.0
- self.last_year = None
- self.last_selected_year = 0
- self.header_size = 0
- self.years = gtk.HBox()
- self.ylabel = gtk.Label("")
- self.ylabel.set_alignment(1.0, 0.5)
- cell = gtk.CellRendererText()
- self.yearsbox = gtk.ComboBox(self.buttons) # pylint: disable-msg=W0201
- self.yearsbox.pack_start(cell)
- self.yearsbox.add_attribute(self.yearsbox.get_cells()[0], 'text', 0)
- self.yearsbox.connect('changed', self._ask_year_selection)
- self.yearsbox.set_tooltip_text(
- _("Select the period for which you want to see the places.")
- )
- self.years.pack_start(self.ylabel, True, True, padding=2)
- self.years.pack_start(self.yearsbox, True, True, padding=2)
- self.pages_selection = gtk.HBox()
- self.pages = []
- self.last_page = 1
- bef = gtk.Button("<<")
- bef.set_tooltip_text(_("Prior page."))
- self.pages.append(bef)
- cur = gtk.Button("1")
- cur.set_tooltip_text(_("The current page/the last page."))
- self.pages.append(cur)
- aft = gtk.Button(">>")
- aft.set_tooltip_text(_("Next page."))
- self.pages.append(aft)
- for page in self.pages:
- page.connect("clicked", self._ask_new_page)
- self.pages_selection.pack_start(page, False, False, padding=2)
- self.nocoord = gtk.Button("Unref") # don't translate
- self.nocoord.connect("clicked", self._show_places_without_coord)
- self.nocoord.set_tooltip_text(
- _("The number of places which have no coordinates."))
- self.without_coord_file = os.path.join(GEOVIEW_SUBPATH,
- "without_coord.html")
- self.endinit = False
- self.generic_filter = None
- self.signal_map = {'place-add': self._place_changed,
- 'place-update' : self._place_changed}
- self.context_id = 0
- self.active = False
- self.already_testing = False
- self.alt_provider = self._config.get('preferences.alternate-provider')
- self.usedmap = "googlev3" if self.alt_provider else "openlayers"
- fpath = os.path.join(const.WEBSTUFF_DIR, 'images', 'crosshairs.png')
- self.crosspath = urlparse.urlunsplit(
- ('file', '', URL_SEP.join(fpath.split(os.sep)), '', '')
- )
- self.side = None
- self.bottom = None
- self._manage_filter('Place Filter Gramplet')
- return HtmlView.build_widget(self)
-
- def can_configure(self):
- """
- We have a configuration window.
- """
- return True
-
- def get_title(self):
- """
- Used to set the titlebar in the configuration window.
- """
- return _('Geography')
-
- def _get_configure_page_funcs(self):
- """
- The function which is used to create the configuration window.
- """
- return [self.map_options, self.geoview_options, self.net_options]
-
- def config_connect(self):
- """
- This method will be called after the ini file is initialized,
- use it to monitor changes in the ini file
- """
- self._config.connect("preferences.crosshair",
- self.config_crosshair)
- self._config.connect("preferences.network-test",
- self.config_network_test)
-
- def config_update_int(self, obj, constant):
- """
- Try to read an int.
- """
- try:
- self._config.set(constant, int(obj.get_text()))
- except: # pylint: disable-msg=W0704
- #pass # pylint: disable-msg=W0702
- print "WARNING: ignoring invalid value for '%s'" % constant
-
- def config_update(self, obj, constant):
- # pylint: disable-msg=W0613
- """
- Some preferences changed in the configuration window.
- """
- self._change_map(self.usedmap)
- self._set_provider_icon()
- self._ask_year_selection(self.last_year)
-
- def config_crosshair(self, client, cnxn_id, entry, data):
- # pylint: disable-msg=W0613
- """
- Do we have a crosshair ?
- """
- if self.javascript_ready:
- _LOG.debug("crosshair : %d" %
- self._config.get("preferences.crosshair")
- )
- self.renderer.execute_script(
- "javascript:addcrosshair('%d','%s','geo-map')" %
- (self._config.get("preferences.crosshair"),
- self.crosspath)
- )
- self._size_request_for_map(self.box, None)
- pass
-
- def geoview_options(self, configdialog):
- """
- Function that builds the widget in the configuration dialog
- for the time period options.
- """
- table = gtk.Table(2, 2)
- table.set_border_width(12)
- table.set_col_spacings(6)
- table.set_row_spacings(6)
- configdialog.add_text(table,
- _("You can adjust the time period "
- "with the two following values."),
- 1)
- configdialog.add_pos_int_entry(table,
- _('The number of years before the first event date'),
- 2, 'preferences.timeperiod-before-range',
- self.config_update_int)
- configdialog.add_pos_int_entry(table,
- _('The number of years after the last event date'),
- 3, 'preferences.timeperiod-after-range',
- self.config_update_int)
- return _('Time period adjustment'), table
-
- def map_options(self, configdialog):
- """
- Function that builds the widget in the configuration dialog
- for the map options.
- """
- table = gtk.Table(2, 2)
- table.set_border_width(12)
- table.set_col_spacings(6)
- table.set_row_spacings(6)
- configdialog.add_checkbox(table,
- _('Crosshair on the map.'),
- 1, 'preferences.crosshair')
- configdialog.add_checkbox(table,
- _('Show the coordinates in the statusbar either in degrees\n'
- 'or in internal Gramps format ( D.D8 )'),
- 2, 'preferences.coordinates-in-degree')
- configdialog.add_pos_int_entry(table,
- _('The maximum number of markers per page.\n'
- 'If the time to load one page is too long, reduce this value'),
- 3, 'preferences.markers',
- self.config_update_int)
- if self.get_toolkit() == 3 :
- # We have mozilla ( gecko ) and webkit toolkits.
- # We propose to the user the choice between these toolkits.
- # useful when webkit crash and not gecko.
- # We need to restart Gramps.
- # In case of crash with a browser, we can change the toolkit in
- # Geography_geoview.ini :
- # webkit=False => gtkmozembed (gecko)
- # webkit=True => webkit
- configdialog.add_checkbox(table,
- _('When selected, we use webkit else we use mozilla\n'
- 'We need to restart Gramps.'),
- 4, 'preferences.webkit')
- return _('The map'), table
-
- def config_network_test(self, client, cnxn_id, entry, data):
- # pylint: disable-msg=W0613
- """
- Do we need to test the network ?
- """
- if self._config.get('preferences.network-test'):
- self._test_network()
-
- def net_options(self, configdialog):
- """
- Function that builds the widget in the configuration dialog
- for the network options.
- """
- table = gtk.Table(1, 1)
- table.set_border_width(12)
- table.set_col_spacings(6)
- table.set_row_spacings(6)
- configdialog.add_checkbox(table,
- _('Test the network '),
- 1, 'preferences.network-test')
- configdialog.add_pos_int_entry(table,
- _('Time out for the network connection test'),
- 2, 'preferences.network-timeout',
- self.config_update_int)
- configdialog.add_pos_int_entry(table,
- _('Time in seconds between two network tests.\n'
- 'Must be greater or equal to 10 seconds'),
- 3, 'preferences.network-periodicity',
- self.config_update_int)
- configdialog.add_text(table,
- _('Host to test for http. Please, change this\n'
- 'and select one of your choice'),
- 4)
- configdialog.add_entry(table, '',
- 5, 'preferences.network-site')
- return _('The network'), table
-
- def _place_changed(self, handle_list):
- # pylint: disable-msg=W0613
- """
- One place changed. need to display it.
- """
- self.displaytype = "places"
- self._set_lock_unlock(True)
- self._geo_places()
-
- def top_widget(self):
- """
- The top widget to use, for GeoView :
- - Places list search
- - Page selection if more than NB_MARKERS_PER_PAGE markers.
- - Place without coordinates if needed
- - Years selection
- """
- self.box1 = gtk.VBox(False, 1) # pylint: disable-msg=W0201
- self.clear.set_alignment(1.0, 0.5)
- self.savezoom.set_alignment(1.0, 0.5)
- cell = gtk.CellRendererText()
-
- self.placebox = gtk.ComboBoxEntry(self.plist)# pylint: disable-msg=W0201
- self.placebox.pack_start(cell)
- self.placebox.add_attribute(self.placebox.get_cells()[0], 'text', 0)
- self.placebox.set_tooltip_text(
- _("Select the place for which you want to see the info bubble."))
-
- completion = gtk.EntryCompletion()
- completion.set_model(self.plist)
- completion.set_minimum_key_length(1)
- completion.set_text_column(0)
- completion.set_inline_completion(True)
- completion.set_match_func(self._match_string)
-
- self.placebox.child.connect('changed', self._entry_selected_place)
- self.placebox.child.connect('key-press-event', self._entry_key_event)
- self.clear.connect('clicked', self._erase_placebox_selection)
- self.placebox.child.set_completion(completion)
-
- box = gtk.HBox()
- box.pack_start(self.clear, False, False, padding=2)
- box.pack_start(self.placebox, True, True, padding=2)
- box.pack_start(self.pages_selection, False, False, padding=2)
- box.pack_start(self.nocoord, False, False, padding=2)
- box.pack_start(self.years, False, False, padding=2)
- box.pack_start(self.savezoom, False, False, padding=2)
- box.pack_start(self.provider, False, False, padding=2)
- box.show_all()
-
- self.heading = gtk.Label('')
- self.heading.set_single_line_mode(True)
- font = pango.FontDescription("monospace")
- font.set_weight(pango.WEIGHT_HEAVY)
- font.set_style(pango.STYLE_NORMAL)
- self.heading.modify_font(font)
-
- self.box1.pack_start(box, True, True, padding=2)
- self.box1.pack_start(self.heading, True, True, padding=2)
- self.box1.show_all()
- return self.box1
-
- def _entry_key_event(self, widget, event):
- """
- The user enter characters. If he enter tab, I'll try to complete.
- This is used when the completion doen't start at the beginning
- of the word or sentence.
- i.e : If we have in our place list :
- ...
- "town of london, England"
- "in the town of londonderry"
- "ville de londres"
- ...
- in the entrybox, if you select "londr", then enter tab,
- the selected item will be "ville de londres"
- """
- prefix = widget.get_text().lower()
- count = 0
- found = _("Unknown")
- if event.keyval == KEY_TAB:
- for place in self.plist:
- if prefix in place[0].lower():
- count += 1
- found = place[0]
- if count == 1:
- self.placebox.child.set_text(found)
-
- def _match_string(self, compl, key, fiter): # pylint: disable-msg=W0613
- """
- Used to select places in the combobox.
- """
- model = compl.get_model()
- text = model.get_value(fiter, 0)
- # the key passed to this function is not unicode! bug ?
- # ie : in french, when you enter Ă©, key is equal to e
- ukey = compl.get_entry().get_text()
- if ukey is None or text is None:
- return False
- if ukey.lower() in text.lower():
- return True
- return False
-
- def _set_years_selection(self, yearbase, step, maxyear):
- """
- Creation of the years list for the years comboBox.
- """
- base = 0
- self.ylabel.set_text("%s : %d %s" % ( _("Time period"),
- step, _("years")) )
- self.yearsbox.hide()
- self.yearsbox.freeze_child_notify()
- self.yearsbox.set_model(None)
- self.buttons.clear()
- self.buttons.append([""])
- self.buttons.append([_("All")])
- for but in range(0, self.maxbut + 1): # pylint: disable-msg=W0612
- newyear = yearbase + base
- if newyear <= maxyear:
- self.buttons.append([str(newyear)])
- base += step
- self.yearsbox.set_model(self.buttons)
- self.yearsbox.set_active(1)
- self.yearsbox.show()
- self.yearsbox.thaw_child_notify()
-
- def _ask_year_selection(self, widget, data=None):
- # pylint: disable-msg=W0613
- """
- Ask to the renderer to apply the selected year
- """
- if widget and widget.get_active():
- self.last_year = widget
- self._set_markers_and_crosshair_on_page(widget)
-
- def _ask_new_page(self, widget, data=None): # pylint: disable-msg=W0613
- """
- Ask to select a new page when we are in a multi-pages map.
- """
- if widget is None:
- return
- page = widget.get_label()
- (current, maxp ) = self.pages[1].get_label().split('/', 1)
- if ( page == "<<" and int(current) > 1):
- cpage = -1
- elif ( page == ">>" and int(current) < int(maxp)):
- cpage = +1
- else:
- cpage = 0
- cpage += int(current)
- self.last_page = cpage
- ftype = {"places":'P', "event":'E', "family":'F', "person":'I'}.get(
- self.displaytype, 'X')
- url = os.path.join(GEOVIEW_SUBPATH,
- "GeoV-%c-%05d.html" % (ftype, cpage)
- )
- url = urlparse.urlunsplit(
- ('file', '', URL_SEP.join(url.split(os.sep)), '', '')
- )
- url += '?map=%s' % self.usedmap
- url += '&zoom=%d' % int(self.realzoom)
- url += '&lat=%s' % str(self.reallatitude)
- url += '&lon=%s' % str(self.reallongitude)
- url += '&cross=%s' % int(self._config.get("preferences.crosshair"))
- self._openurl(url)
- self._create_pages_selection(cpage, int(maxp))
- self._savezoomandposition()
- # Need to wait the page is loaded to show the markers.
- gobject.timeout_add(1500, self._show_selected_places)
- self.placebox.child.set_text("")
-
- def _show_selected_places(self):
- """
- Here, we synchronize the years combobox with the renderer
- except when we are in the places view.
- """
- if self.displaytype != "places":
- for index, r_year in enumerate(self.buttons):
- if self.last_selected_year == r_year[0]:
- self.yearsbox.set_active(index)
- self._call_js_selectmarkers(r_year[0])
- self._size_request_for_map(self.box, None)
-
- def _show_places_without_coord(self, widget): # pylint: disable-msg=W0613
- """
- Show the page which contains the list of all places without coordinates.
- """
- url = urlparse.urlunsplit(
- ('file', '', URL_SEP.join(self.without_coord_file.split(os.sep)),
- '', '')
- )
- self._openurl(url)
-
- def _entry_selected_place(self, combobox): # pylint: disable-msg=W0612
- """
- Ask to the renderer to show the info bubble.
- """
- place = combobox.get_text()
- for entry in self.placebox.get_model():
- if entry[0] == place:
- # Is this entry in the current page ?
- if self.last_page == int(entry[2]):
- # Yes, we don't need to load another page.
- self._show_place_info_bubble(entry[1])
- self._show_selected_places()
- else:
- # No, we need to load the correct page
- self.last_page = int(entry[2])
- ftype = {"places":'P',
- "event":'E',
- "family":'F',
- "person":'I'}.get( self.displaytype, 'X')
- url = os.path.join(GEOVIEW_SUBPATH,
- "GeoV-%c-%05d.html" % (ftype, entry[2])
- )
- url = urlparse.urlunsplit(
- ('file', '', URL_SEP.join(url.split(os.sep)), '', '')
- )
- url += '?map=%s' % self.usedmap
- url += '&zoom=%d' % int(self.realzoom)
- url += '&lat=%s' % str(self.reallatitude)
- url += '&lon=%s' % str(self.reallongitude)
- url += '&cross=%s' % int(
- self._config.get("preferences.crosshair")
- )
- self._openurl(url)
- (current, maxp ) = self.pages[1].get_label().split('/', 1)
- self._create_pages_selection(entry[2], int(maxp))
- self._savezoomandposition()
- # Need to wait the page is loaded to show the info bubble.
- gobject.timeout_add(1500, self._show_place_info_bubble,
- entry[1])
- # Need to wait the page is loaded to show the markers.
- gobject.timeout_add(1600, self._show_selected_places)
- return
-
- def _show_place_info_bubble(self, marker_index):
- """
- We need to call javascript to show the info bubble.
- """
- if self.javascript_ready:
- self.renderer.execute_script("javascript:placeclick('%d')" %
- marker_index)
- self._size_request_for_map(self.box, None)
-
- def _erase_placebox_selection(self, arg):
- # pylint: disable-msg=W0613
- """
- We erase the place name in the entrybox after 1 second.
- """
- self.placebox.child.set_text("")
-
- def on_delete(self):
- """
- We need to suppress temporary files here.
- Save the zoom, latitude, longitude and lock
- """
- self.javascript_ready = False
- self._savezoomandposition()
- if config.get('geoview.lock'):
- config.set('geoview.zoom', int(self.realzoom))
- config.set('geoview.latitude', str(self.reallatitude))
- config.set('geoview.longitude', str(self.reallongitude))
- config.set('geoview.map', self.displaytype)
- else:
- config.set('geoview.zoom', 0)
- config.set('geoview.latitude', "0.0")
- config.set('geoview.longitude', "0.0")
- config.set('geoview.map', "person")
- NavigationView.on_delete(self)
-
- def init_parent_signals_for_map(self, widget, event):
- """
- Required to properly bootstrap the signal handlers.
- This handler is connected by build_widget.
- After the outside ViewManager has placed this widget we are
- able to access the parent container.
- """
- self.box.disconnect(self.bootstrap_handler)
- self.years.hide()
- self.pages_selection.hide()
- self.nocoord.hide()
- self.box.connect("size-allocate", self._size_request_for_map)
-
- def _size_request_for_bars(self, widget, event, data, data1):
- if self.widget is not None:
- self._size_request_for_map(self.widget,None,None)
-
- def _size_request_for_map(self, widget, event, data=None):
- # pylint: disable-msg=W0613
- """
- We need to resize the map
- """
- if not self.javascript_ready:
- return
- if not self.resize_occurs:
- self.resize_occurs = True
- gobject.timeout_add(300, self._really_resize_the_map,
- widget, event, data)
-
- def _really_resize_the_map(self, widget, event, data=None):
- # VPane -> Hpane -> NoteBook -> HPaned -> VBox -> Window
- # We need to get the HPaned size and the VPaned size.
- if not self.javascript_ready:
- # Two reason for this :
- # 1 - we are quitting gramps
- # 2 - the renderer is not ready to accept javascript
- return
- self.resize_occurs = False
- self.box1_size = self.box1.get_allocation()
- self.header_size = self.box1_size.height
- self.height = (widget.parent.get_allocation().height - self.header_size -
- widget.parent.get_child2().get_allocation().height - 30)
- self.width = (widget.parent.parent.get_allocation().width -
- widget.parent.parent.get_child2().get_allocation().width - 30)
- # We need to know what is the min width of the Navigation bar.
- Navigator_width = widget.parent.parent.parent.parent.get_child1().get_allocation().width
- if self.Navigator_min_width == 0:
- self.Navigator_min_width = Navigator_width
- if Navigator_width < self.Navigator_min_width:
- widget.parent.parent.parent.parent.set_position(self.Navigator_min_width)
- _LOG.debug("Navigator width = %d" % Navigator_width)
-
- if not self.sidebar.get_property('visible'):
- if self.side is not None:
- self.width = widget.parent.parent.get_allocation().width - 24
- else:
- self.side = widget
- self.width = widget.parent.parent.get_allocation().width - 300
- _LOG.debug("No sidebar : map width=%d" % self.width)
- else:
- _LOG.debug("Sidebar : map width=%d" % self.width)
-
- if not self.bottombar.get_property('visible'):
- if self.bottom is not None:
- self.height = (widget.parent.get_allocation().height
- - self.header_size - 24)
- else:
- self.bottom = widget
- self.height = (widget.parent.get_allocation().height
- - self.header_size - 400)
- _LOG.debug("No bottombar : map height=%d" % self.height )
- else:
- _LOG.debug("bottombar : map height=%d" % self.height )
- self.widget = widget
- self.height = 10 if self.height < 10 else self.height
- self.width = 10 if self.width < 10 else self.width
- if Navigator_width < self.Navigator_min_width:
- self.width -= ( self.Navigator_min_width - Navigator_width )
- self.box1_size.width = self.width
- self.box1_size.height = self.height
- #self.box1.set_allocation(self.box1_size)
- if self.javascript_ready:
- _LOG.debug("New size : width=%d and height=%d" %
- (self.width, self.height)
- )
- self.renderer.execute_script(
- "javascript:mapstraction.resizeTo('%dpx','%dpx');" %
- (self.width, self.height)
- )
- self.renderer.execute_script(
- "javascript:setcenterandzoom(mapstraction,uzoom,ulat,ulon)"
- )
- self.frames.set_size_request(self.width+4, self.height+4)
- if not self.uistate.get_active('Person'):
- return
- self.external_uri()
-
- def set_active(self):
- """
- Set view active when we enter into this view.
- """
- self.key_active_changed = self.dbstate.connect(
- 'active-changed', self._goto_active_person)
- hobj = self.get_history()
- self.active_signal = hobj.connect(
- 'active-changed', self._goto_active_person)
- self._goto_active_person()
- self.active = True
- NavigationView.set_active(self)
- self._test_network()
-
- def set_inactive(self):
- """
- Set view inactive when switching to another view.
- """
- action = self.uistate.uimanager.get_action('/MenuBar/ViewMenu/Navigator')
- action.set_sensitive(True)
- action = self.uistate.uimanager.get_action('/MenuBar/ViewMenu/Toolbar')
- action.set_sensitive(True)
- HtmlView.set_inactive(self)
- self.dbstate.disconnect(self.key_active_changed)
- self.active = False
-
- def get_stock(self):
- """
- Returns the name of the stock icon to use for the display.
- This assumes that this icon has already been registered
- as a stock icon.
- """
- return 'gramps-geo'
-
- def get_viewtype_stock(self):
- """Type of view in category
- """
- return 'gramps-geo'
-
- def _savezoomandposition(self, timeloop=None):
- """
- The only way we have to save the zoom and position is to change the
- title of the html page then to get this title.
- When the title change, we receive a 'changed-title' signal.
- Then we can get the new title with the new values.
- """
- res = self.dbstate.db.get_researcher()
- title = None
- if res: # Don't modify the current values if no db is loaded.
- start = 0
- try:
- title = ZOOMANDPOS.search(self.renderer.title, start)
- if title:
- if self.realzoom != title.group(1):
- self.realzoom = title.group(1) if int(title.group(1)) < 17 else 17
- if self.reallatitude != title.group(2):
- self.reallatitude = title.group(2)
- if self.reallongitude != title.group(3):
- self.reallongitude = title.group(3)
- except: # pylint: disable-msg=W0704
- pass # pylint: disable-msg=W0702
- if timeloop:
- if self.active:
- if title is not None:
- self.uistate.status.pop(self.context_id)
- if self._config.get('preferences.coordinates-in-degree'):
- latitude, longitude = conv_lat_lon(
- self.reallatitude, self.reallongitude, "DEG")
- else:
- latitude, longitude = conv_lat_lon(
- self.reallatitude, self.reallongitude, "D.D8")
- mess = "%s= %s\t%s= %s\t%s= %s" % (
- _("Latitude"), latitude,
- _("Longitude"), longitude,
- _("Zoom"), self.realzoom
- )
- self.context_id = self.uistate.status.push(1, mess)
- gobject.timeout_add(timeloop,
- self._savezoomandposition, timeloop)
-
- def _do_we_need_to_zoom_between_map(self):
- """
- Look if we need to use the lasts zoom, latitude and longitude retrieved
- from the renderer, or if we must use the last ones we just created.
- """
- if self.reallatitude is None:
- self.reallatitude = 0.0
- if self.reallongitude is None:
- self.reallongitude = 0.0
- if not config.get('geoview.lock'):
- self.reallatitude = self.latit
- self.reallongitude = self.longt
- self.realzoom = self.zoom if self.zoom < 17 else 17
-
- def _change_map(self, usedmap):
- """
- Tell the browser to change the current map.
- """
- self.uistate.clear_filter_results()
- self._do_we_need_to_zoom_between_map()
- if self.last_page != 1:
- ftype = {"places":'P',
- "event":'E',
- "family":'F',
- "person":'I'}.get(self.displaytype, 'X')
- url = os.path.join(GEOVIEW_SUBPATH, "GeoV-%c-%05d.html" %
- (ftype, self.last_page))
- url = urlparse.urlunsplit( ('file', '',
- URL_SEP.join(url.split(os.sep)),
- '', ''))
- else:
- if not self.htmlfile:
- self.htmlfile = os.path.join(GEOVIEW_SUBPATH, "geography.html")
- url = urlparse.urlunsplit( ('file', '',
- URL_SEP.join(self.htmlfile.split(os.sep)),
- '', ''))
- url += '?map=%s' % usedmap
- url += '&zoom=%d' % int(self.realzoom)
- url += '&lat=%s' % str(self.reallatitude)
- url += '&lon=%s' % str(self.reallongitude)
- url += '&cross=%s' % int(self._config.get("preferences.crosshair"))
- self._openurl(url)
- self._savezoomandposition()
- if self.displaytype != "places":
- # Need to wait the page is loaded to set the markers and crosshair.
- gobject.timeout_add(1500, self._set_markers_and_crosshair_on_page,
- self.last_year)
- else:
- # Need to wait the page is loaded to set the crosshair.
- gobject.timeout_add(1500, self.config_crosshair,
- False, False, False, False)
-
- def _set_markers_and_crosshair_on_page(self, widget):
- """
- get the year to select then call javascript
- """
- if not self.endinit:
- return
- if widget:
- model = widget.get_model()
- if model:
- year = "no"
- try:
- year = model.get_value(widget.get_active_iter(), 0)
- except: # pylint: disable-msg=W0704
- pass # pylint: disable-msg=W0702
- if self.last_selected_year == 0:
- self.last_selected_year = year
- elif year != "no":
- self.last_selected_year = year
- self._call_js_selectmarkers(year)
- self.config_crosshair(False, False, False, False)
-
- def _call_js_selectmarkers(self, year):
- """
- Ask to the renderer to show All or specific markers.
- """
- if self.javascript_ready:
- if year == _("All"):
- self.renderer.execute_script(
- "javascript:selectmarkers('All')")
- else:
- self.renderer.execute_script(
- "javascript:selectmarkers('%s')" % year )
-
- def additional_ui(self):
- """
- Specifies the UIManager XML code that defines the menus and buttons
- associated with the interface.
- """
- return '''
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- '''
-
- def define_actions(self):
- """
- Required define_actions function for NavigationView. Builds the action
- group information required.
- """
- NavigationView.define_actions(self)
-
- # geoview actions
- self._add_action('AddPlace', 'geo-place-add',
- _('_Add Place'),
- callback=self._add_place,
- tip=_("Add the location centred on the map as a new place in "
- "Gramps. Double click the location to centre on the map."))
- self._add_action('LinkPlace', 'geo-place-link',
- _('_Link Place'),
- callback=self._link_place,
- tip=_("Link the location centred on the map to a place in "
- "Gramps. Double click the location to centre on the map."))
- self._add_action('AddPlaceMenu', 'geo-place-add',
- _('_Add Place'),
- callback=self._add_place,
- tip=_("Add the location centred on the map as a new place in "
- "Gramps. Double click the location to centre on the map."))
- self._add_action('LinkPlaceMenu', 'geo-place-link',
- _('_Link Place'),
- callback=self._link_place,
- tip=_("Link the location centred on the map to a place in "
- "Gramps. Double click the location to centre on the map."))
- self._add_action('AllPlacesMaps', 'geo-show-place', _('_All Places'),
- callback=self._all_places, tip=_("Attempt to view all places in "
- "the family tree."))
- self._add_action('PersonMaps', 'geo-show-person', _('_Person'),
- callback=self._person_places,
- tip=_("Attempt to view all the places "
- "where the selected people lived."))
- self._add_action('FamilyMaps', 'geo-show-family', _('_Family'),
- callback=self._family_places,
- tip=_("Attempt to view places of the selected people's family."))
- self._add_action('EventMaps', 'geo-show-event', _('_Event'),
- callback=self._event_places,
- tip=_("Attempt to view places connected to all events."))
- self._add_action('AllPlacesMapsMenu', 'geo-show-place',
- _('_All Places'), callback=self._all_places,
- tip=_("Attempt to view all places in "
- "the family tree."))
- self._add_action('PersonMapsMenu', 'geo-show-person', _('_Person'),
- callback=self._person_places,
- tip=_("Attempt to view all the places "
- "where the selected people lived."))
- self._add_action('FamilyMapsMenu', 'geo-show-family', _('_Family'),
- callback=self._family_places,
- tip=_("Attempt to view places of the selected people's family."))
- self._add_action('EventMapsMenu', 'geo-show-event', _('_Event'),
- callback=self._event_places,
- tip=_("Attempt to view places connected to all events."))
-
- def change_page(self):
- """
- Called by viewmanager at end of realization when arriving on the page
- At this point the Toolbar is created. We need to:
- 1. get the menutoolbutton
- 2. add all possible css styles sheet available
- 3. add the actions that correspond to clicking in this drop down menu
- 4. set icon and label of the menutoolbutton now that it is realized
- 5. store label so it can be changed when selection changes
- """
- action = self.uistate.uimanager.get_action('/MenuBar/ViewMenu/Bars/Sidebar')
- action.set_sensitive(False)
- action = self.uistate.uimanager.get_action('/MenuBar/ViewMenu/Bars/Bottombar')
- action.set_sensitive(False)
- action = self.uistate.uimanager.get_action('/MenuBar/ViewMenu/Navigator')
- action.set_sensitive(False)
- action.set_active(True)
- action = self.uistate.uimanager.get_action('/MenuBar/ViewMenu/Toolbar')
- action.set_sensitive(False)
- hobj = self.get_history()
- self.fwd_action.set_sensitive(not hobj.at_end())
- self.back_action.set_sensitive(not hobj.at_front())
- self.other_action.set_sensitive(not self.dbstate.db.readonly)
- self.uistate.modify_statusbar(self.dbstate)
- self.uistate.clear_filter_results()
- self._set_lock_unlock(config.get('geoview.lock'))
- self._savezoomandposition(500) # every 500 millisecondes
- self.endinit = True
- self.uistate.clear_filter_results()
- self._set_provider_icon()
- self._geo_places()
-
- def _goto_active_person(self, handle=None): # pylint: disable-msg=W0613
- """
- Here when the GeoView page is loaded
- """
- if not self.uistate.get_active('Person'):
- return
- self._geo_places()
-
- def _manage_filter(self, filter): # pylint: disable-msg=W0613
- """
- Remove the old filter and add the new one
- """
- if self.sidebar.has_gramplet(self.active_filter):
- self.sidebar.remove_gramplet(self.active_filter)
- self.sidebar.add_gramplet(filter)
- elif self.bottombar.has_gramplet(self.active_filter):
- self.bottombar.remove_gramplet(self.active_filter)
- self.bottombar.add_gramplet(filter)
- else:
- self.sidebar.add_gramplet(filter)
- self.active_filter = filter
-
- def _all_places(self, hanle=None): # pylint: disable-msg=W0613
- """
- Specifies the place for the home person to display with mapstraction.
- """
- self.displaytype = "places"
- self._manage_filter('Place Filter Gramplet')
- self._geo_places()
-
- def _person_places(self, handle=None): # pylint: disable-msg=W0613
- """
- Specifies the person places.
- """
- self.displaytype = "person"
- if not self.uistate.get_active('Person'):
- return
- self._manage_filter('Person Filter Gramplet')
- self._geo_places()
-
- def _family_places(self, hanle=None): # pylint: disable-msg=W0613
- """
- Specifies the family places to display with mapstraction.
- """
- self.displaytype = "family"
- if not self.uistate.get_active('Person'):
- return
- self._manage_filter('Family Filter Gramplet')
- self._geo_places()
-
- def _event_places(self, hanle=None): # pylint: disable-msg=W0613
- """
- Specifies all event places to display with mapstraction.
- """
- self.displaytype = "event"
- self._manage_filter('Event Filter Gramplet')
- self._geo_places()
-
- def _new_database(self, database):
- """
- We just change the database.
- Restore the initial config. Is it good ?
- """
- if config.get('geoview.lock'):
- self.realzoom = config.get('geoview.zoom')
- self.displaytype = config.get('geoview.map')
- self.reallatitude, self.reallongitude = conv_lat_lon(
- config.get('geoview.latitude'),
- config.get('geoview.longitude'),
- "D.D8")
- self._change_db(database)
- for sig in self.signal_map:
- self.callman.add_db_signal(sig, self.signal_map[sig])
-
- def _geo_places(self):
- """
- Specifies the places to display with mapstraction.
- """
- if not self.endinit:
- return
- if not self.dbstate.db.is_open():
- return
- if self.nbmarkers > 0 :
- # While the db is not loaded, we have 0 markers.
- self._savezoomandposition()
- self._test_network()
- self.nbmarkers = 0
- self.nbplaces = 0
- self.without = 0
- self.javascript_ready = False
- self._createmapstraction(self.displaytype)
-
- def _set_lock_unlock(self, state):
- """
- Change the lock/unlock state.
- """
- config.set('geoview.lock', state)
- self._set_lock_unlock_icon()
-
- def _set_lock_unlock_icon(self):
- """
- Change the lock/unlock icon depending on the button state.
- """
- child = self.savezoom.child
- if child:
- self.savezoom.remove(child)
- image = gtk.Image()
- if config.get('geoview.lock'):
- image.set_from_stock('geo-fixed-zoom', gtk.ICON_SIZE_MENU)
- else:
- image.set_from_stock('geo-free-zoom', gtk.ICON_SIZE_MENU)
- image.show()
- self.savezoom.add(image)
- self._geo_places()
-
- def _save_zoom(self, button): # pylint: disable-msg=W0613
- """
- Do we change the zoom between maps ?
- It's not between maps providers, but between people, family,
- events or places map.
- When we unlock, we reload the page with our values.
- """
- if config.get('geoview.lock'):
- config.set('geoview.lock', False)
- self._set_lock_unlock(False)
- else:
- config.set('geoview.lock', True)
- self._set_lock_unlock(True)
-
- def _change_provider(self, button): # pylint: disable-msg=W0613
- """
- Toogle between the two maps providers.
- Inactive ( the default ) is openstreetmap.
- As openstreetmap has no api, we now use openlayers.
- Active means Google maps.
- """
- if self._config.get('preferences.alternate-provider'):
- self.usedmap = "openlayers"
- self._config.set('preferences.alternate-provider', False)
- else:
- self.usedmap = "googlev3"
- self._config.set('preferences.alternate-provider', True)
- self._set_provider_icon()
- self._change_map(self.usedmap)
- self._ask_year_selection(self.last_year)
-
- def _set_provider_icon(self):
- """
- Change the provider icon depending on the button state.
- """
- child = self.provider.child
- if child:
- self.provider.remove(child)
- image = gtk.Image()
- if self._config.get('preferences.alternate-provider'):
- image.set_from_stock('gramps-geo-altmap', gtk.ICON_SIZE_MENU)
- else:
- image.set_from_stock('gramps-geo-mainmap', gtk.ICON_SIZE_MENU)
- image.show()
- self.provider.add(image)
-
- def _createpageplaceswithoutcoord(self):
- """
- Create a page with the list of all places without coordinates
- page.
- """
- ufd = open(self.without_coord_file, "w+")
- ufd.write(
- _HTMLHEADER %
- { 'title' : _('List of places without coordinates'),
- 'lang' : 'en',
- 'css' : self._add_stylesheet(),
- }
- + ' \n'
- )
- ufd.write(
- ' \n' +
- ' '
- % (_('Here is the list of all places in the family tree'
- ' for which we have no coordinates.
'
- ' This means no longitude or latitude.'),
- _('Back to prior page')
- )
- )
- self.places = sorted(self.place_without_coordinates)
- ufd.write(
- "
NB | " +
- "Gramps ID | Place | "
- )
- #for p, place in enumerate(self.places):
- # ufd.write(" %d | %s | %s |
\n"
- # % (p + 1, place[0], place[1]))
-
- ufd.writelines(
- " %d | %s | %s |
\n"
- % (p + 1, gid, place)
- for p, (gid, place) in enumerate(self.places)
- )
- ufd.write("
\n"
- " \n"
- "