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' + - '

%s%s

' - % (_('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( - " " + - "" - ) - #for p, place in enumerate(self.places): - # ufd.write(" \n" - # % (p + 1, place[0], place[1])) - - ufd.writelines( - " \n" - % (p + 1, gid, place) - for p, (gid, place) in enumerate(self.places) - ) - ufd.write("
NBGramps IDPlace
%d%s%s
%d%s%s
\n" - " \n" - "\n" - ) - ufd.close() - - def _create_pages_without(self): - """ - Show or hide the page without coord button. - """ - if self.without > 0: - self._createpageplaceswithoutcoord() - self.nocoord.set_label("%d ?" % ( self.without) ) - self.nocoord.show() - else: - self.nocoord.hide() - - def _create_pages_selection(self, current, pages): - """ - Set the label text in the pages selection button - """ - self.pages[1].set_label("%d/%d" % ( current, pages ) ) - - def _createmapstractionpostheader(self, h4mess, curpage): - # disable msg=W0613 # curpage is unused - # pylint: disable-msg=W0613 - """ - This is needed to add infos to the header. - This can't be in createmapstractionheader because we need - to know something which is known only after some work. - """ - if self.maxyear == 0: - self.maxyear = 2100 - if self.minyear == 9999: - self.minyear = 1500 - adjust_before_min_year = self._config.get( - 'preferences.timeperiod-before-range') - adjust_after_max_year = self._config.get( - 'preferences.timeperiod-after-range') - self.minyear -= ( self.minyear - adjust_before_min_year ) % 10 - self.maxyear -= ( self.maxyear + adjust_after_max_year ) % 10 - self.yearint = (adjust_after_max_year + - (self.maxyear - self.minyear) / (self.maxbut - 1) - ) - self.yearint -= self.yearint % 10 - if self.yearint == 0: - self.yearint = 10 - self.mapview.write( - "\n" + - "\n"+ - "\n" - ) - self.years.hide() - if h4mess: - self.mapview.write("

%s

\n" % h4mess) - else: - if self.displaytype != "places": - self._set_years_selection(self.minyear, - self.yearint, - self.maxyear) - self.years.show() - self.mapview.write( - '
\n' - % ( self.height, self.width ) + - '\n' + - '\n' + - '\n' % upath - ) - - def _createmapstractiontrailer(self): - """ - Add the last directives for the html page. - """ - - self.mapview.write(_HTMLTRAILER) - self.mapview.close() - - def _set_center_and_zoom(self, ptype): - """ - Calculate the zoom. - """ - # Select the center of the map and the zoom - self.centered = False - if ptype == 2: - # Sort by places and year for events - self.sort = sorted(self.place_list, - key=operator.itemgetter(3, 4, 7) - ) - else: - # Sort by date in all other cases - self.sort = sorted(self.place_list, - key=operator.itemgetter(7) - ) - signminlon = _get_sign(self.minlon) - signminlat = _get_sign(self.minlat) - signmaxlon = _get_sign(self.maxlon) - signmaxlat = _get_sign(self.maxlat) - 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.zoom = zoomlat if zoomlat < zoomlong else zoomlong - self.zoom -= 1 - if self.zoom < 2: - self.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: - cent = int(mark[6]) - if cent: - self.centered = True - 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 - if not (latit == longt == 0.0): - self.latit = latit - self.longt = longt - self.mustcenter = True - - def _create_pages(self, ptype, h3mess, h4mess): - """ - Do we need to create a multi-pages document ? - Do we have too many markers ? - """ - # disable msg=W0612 # page is unused - # pylint: disable-msg=W0612 - self.nbpages = 0 - self.last_page = 1 - self.box1.set_sensitive(True) - self.pages_selection.hide() - self.placebox.child.set_text("") - self.placebox.freeze_child_notify() - self.placebox.set_model(None) - self.plist.clear() - self.clear.set_label("%s (%d)" % ( _("Places list"), self.nbplaces )) - pages = ( self.nbplaces / self._config.get('preferences.markers') ) - if (self.nbplaces % self._config.get('preferences.markers') ) != 0: - pages += 1 - if self.nbplaces == 0: - try: - self._createmapstractiontrailer() - except: # pylint: disable-msg=W0704 - pass # pylint: disable-msg=W0702 - self._set_center_and_zoom(ptype) - self._create_pages_without() - if pages > 1: - self._create_pages_selection(1, pages) - self.pages_selection.show() - self.last_index = 0 - for page in range(0, pages, 1): - self.nbpages += 1 - ftype = {1:'P', 2:'E', 3:'F', 4:'I'}.get(ptype, 'X') - filename = os.path.join( - GEOVIEW_SUBPATH, "GeoV-%c-%05d.html" % (ftype, self.nbpages)) - if self.nbpages == 1: - self.htmlfile = filename - self._createmapstractionheader(filename) - self._create_needed_javascript() - first = ( self.nbpages - 1 ) * self._config.get('preferences.markers') - last = ( self.nbpages * self._config.get('preferences.markers') ) - 1 - self._create_markers(ptype, first, last) - self._show_heading(h3mess) - self._createmapstractionpostheader(h4mess, self.nbpages) - self._createmapstractiontrailer() - if self.nbpages == 1: - self._do_we_need_to_zoom_between_map() - url = urlparse.urlunsplit( ('file', '', - URL_SEP.join(self.htmlfile.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._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) - self.placebox.set_model(self.plist) - self.placebox.thaw_child_notify() - - def _createmapstraction(self, displaytype): - """ - Which kind of map are we going to create ? - """ - self.cal = config.get('preferences.calendar-format-report') - if displaytype == "places": - self._createmapstractionplaces(self.dbstate) - elif displaytype == "family": - self._createmapstractionfamily(self.dbstate) - elif displaytype == "person": - self._createmapstractionperson(self.dbstate) - elif displaytype == "event": - self._createmapstractionevents(self.dbstate) - else: - self._createmapstractionheader( - os.path.join(GEOVIEW_SUBPATH, "error.html")) - self._createmapnotimplemented() - self._createmapstractiontrailer() - - 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 _append_to_places_list(self, place, evttype, name, lat, - longit, descr, center, year, icontype): - """ - 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, int(center), year, icontype]) - 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 _set_icon(self, markertype, differtype, ptype): - """ - Select the good icon depending on events. - If we have different events for one place, we use the default icon. - """ - if ptype != 1: # for places, we have no event type - value = _ICONS.get(markertype.value, 'gramps-geo-default') - else: - value = 'gramps-geo-default' - if differtype: # in case multiple evts - value = 'gramps-geo-default' # we use default icon. - if ( value == "gramps-geo-default" ): - value = value.replace("default",'" + default_icon + "') - ipath = os.path.join(const.WEBSTUFF_DIR, 'images', '%s.png' % value ) - upath = urlparse.urlunsplit(('file', '', - URL_SEP.join(ipath.split(os.sep)), '', '')) - # Workaround to avoid a drift problem with openlayers. - self.mapview.write( - #'\n // workaround to avoid openlayers drift.\n' + - '\n if ( current_map != "openlayers" ) {' + - ' my_marker.setIcon("%s",[19,19],[0,19]);' % upath + - ' } else { ' + - ' my_marker.setIcon("%s",[19,19],[0,-19]);' % upath + - ' }\n' - ) - - def _show_heading(self, heading): - """ - Show the current map heading in the gtk label above the map. - """ - self.heading.set_text(heading) - - def _create_markers(self, formatype, firstm, lastm): - """ - Create all markers for the specified person. - """ - last = "" - current = "" - self.placeslist = [] - indm = 0 - divclose = True - self.yearinmarker = [] - ininterval = False - self.setattr = True - self.mapview.write(" function setcenterandzoom(map,uzoom,ulat,ulon){\n") - if self.mustcenter: - self.centered = True - self.mapview.write( - " var point = new mxn.LatLonPoint" - "(ulat,ulon);" - "map.setCenterAndZoom" - "(point, uzoom);\n" - ) - self.setattr = False - self.mapview.write( - '}\n' - ' function setmarkers(map) {\n' - ' if ( current_map != "openlayers" ) {' - ' default_icon = "altmap";' - ' } else { ' - ' default_icon = "mainmap"; }\n' - ) - differtype = False - savetype = None - index_mark = 0 - indm = firstm - for mark in self.sort: - index_mark += 1 - if index_mark < self.last_index: - continue - if lastm >= indm >= firstm: - ininterval = True - if ininterval: - current = {2 : [mark[3], mark[4]], - }.get(formatype, mark[0]) - if last != current: - if not divclose: - if ininterval: - self.mapview.write('");') - divclose = True - years = "" - if mark[2]: - for year in self.yearinmarker: - years += "%d " % year - years += "end" - self.mapview.write( - "my_marker.setAttribute" + - "('year','%s');" % years - ) - self.yearinmarker = [] - self._set_icon(savetype, differtype, formatype) - differtype = False - self.mapview.write(" map.addMarker(my_marker);") - if ( indm > lastm ): - if (indm % self._config.get('preferences.markers')) == 0: - self.last_index = index_mark - ininterval = False - last = {2 : [mark[3], mark[4]], - }.get(formatype, mark[0]) - if lastm >= indm >= firstm: - ind = indm % self._config.get('preferences.markers') - self.plist.append([ mark[0], ind, self.nbpages] ) - indm += 1 - self.mapview.write( - "\n var point = new mxn.LatLonPoint" + - - "(%s,%s);" % (mark[3], mark[4]) + - "my_marker = new mxn.Marker(point);" + - "gmarkers[%d]=my_marker;" % ind + - "my_marker.setLabel" + - '("%s");' % _escape(mark[0]) - ) - self.yearinmarker.append(mark[7]) - divclose = False - differtype = False - if mark[8] and not differtype: - savetype = mark[8] - self.mapview.write( - 'my_marker.setInfoBubble("
" + - "%s
" % _escape(mark[0]) - ) - if formatype == 1: - self.mapview.write("
%s" % _escape(mark[5])) - else: - self.mapview.write("
%s - %s" % - (mark[7], _escape(mark[5]))) - else: # This marker already exists. add info. - if ( mark[8] and savetype != mark[8] ): - differtype = True - if indm > last: - divclose = True - else: - self.mapview.write("
%s - %s" % (mark[7], - _escape(mark[5]))) - if not any(y == mark[7] for y in self.yearinmarker): - self.yearinmarker.append(mark[7]) - else: - indm += 1 - if self.nbmarkers > 0 and ininterval: - years = "" - if mark[2]: - for year in self.yearinmarker: - years += "%d " % year - years += "end" - self.mapview.write( - '
");' + - "my_marker.setAttribute('year','%s');" % years - ) - self._set_icon(savetype, differtype, formatype) - self.mapview.write(" map.addMarker(my_marker);") - if self.nbmarkers == 0: - # We have no valid geographic point to center the map. - longitude = 0.0 - latitude = 0.0 - self.mapview.write( - "\nvar point = new mxn.LatLonPoint" + - "(%s,%s);\n" % (latitude, longitude) + - " map.setCenterAndZoom" + - "(point, %d);\n" % 2 + - " my_marker = new mxn.Marker(point);\n" + - " my_marker.setLabel" + - '("%s");\n' % _("No location.") + - ' my_marker.setInfoBubble("
" + - _("You have no places in your family tree " - " with coordinates.") + - "
" + - _("You are looking at the default map.") + - '
");\n' - ) - self._set_icon(None, True, 1) - self.mapview.write(" map.addMarker(my_marker);") - self.mapview.write( - "\n}" - "\n\n" - ) - - def _createpersonmarkers(self, dbstate, person, comment): - """ - Create all markers for the specified person. - """ - latitude = longitude = "" - if person: - event_ref = person.get_birth_ref() - if event_ref: - event = dbstate.db.get_event_from_handle(event_ref.ref) - eventyear = 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") - if comment: - descr1 = _("%s : birth place.") % comment - else: - descr1 = _("birth place.") - descr = place.get_title() - # 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, - gen.lib.EventType.BIRTH, - _nd.display(person), - latitude, longitude, - descr1, - int(self.center), - eventyear, - event.get_type() - ) - self.center = False - else: - self._append_to_places_without_coord( - place.gramps_id, descr) - latitude = longitude = "" - event_ref = person.get_death_ref() - if event_ref: - event = dbstate.db.get_event_from_handle(event_ref.ref) - eventyear = 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() - if comment: - descr1 = _("%s : death place.") % comment - else: - descr1 = _("death place.") - # 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, - gen.lib.EventType.DEATH, - _nd.display(person), - latitude, longitude, - descr1, - int(self.center), - eventyear, - event.get_type() - ) - self.center = False - else: - self._append_to_places_without_coord( - place.gramps_id, descr) - - def _createmapstractionplaces(self, dbstate): - """ - Create the marker for each place in the database which has a lat/lon. - """ - 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 - latitude = "" - longitude = "" - self.center = True - - if self.generic_filter is None or not config.get('interface.filter'): - places_handle = dbstate.db.iter_place_handles() - else: - places_handle = self.generic_filter.apply( - dbstate.db, dbstate.db.iter_place_handles()) - for place_hdl in places_handle: - place = dbstate.db.get_place_from_handle(place_hdl) - descr = place.get_title() - descr1 = _("Id : %s") % place.gramps_id - 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, - descr1, self.center, None, - gen.lib.EventType.UNKNOWN) - self.center = False - else: - self._append_to_places_without_coord(place.gramps_id, descr) - self.yearsbox.hide() - self._need_to_create_pages(1, self.center, - _("All places in the family tree with " - "coordinates."), - ) - - def _createmapstractionevents(self, dbstate): - """ - Create one marker for each place associated with an 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 - latitude = "" - longitude = "" - self.center = True - - if self.generic_filter is None or not config.get('interface.filter'): - events_handle = dbstate.db.iter_event_handles() - else: - events_handle = self.generic_filter.apply( - dbstate.db, dbstate.db.iter_event_handles()) - for event_hdl in events_handle: - event = dbstate.db.get_event_from_handle(event_hdl) - 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' - ] - descr2 = "%s" % event.get_type() - if person_list: - for person in person_list: - descr2 = ("%(description)s - %(name)s") % { - 'description' : descr2, - 'name' : _nd.display(person)} - descr = ("%(eventtype)s;"+ - " %(place)s%(description)s" - ) % { 'eventtype': gen.lib.EventType( - event.get_type() - ), - 'place': place.get_title(), - 'description': descr2} - else: - descr = ("%(eventtype)s; %(place)s
") % { - 'eventtype': gen.lib.EventType( - event.get_type() - ), - 'place': place.get_title()} - self._append_to_places_list(descr1, descr, - descr, - latitude, longitude, - descr2, self.center, - eventyear, - event.get_type() - ) - self.center = False - else: - descr = place.get_title() - self._append_to_places_without_coord( - place.gramps_id, descr) - self._need_to_create_pages(2, self.center, - _("All events in the family tree with " - "coordinates."), - ) - - def _createmapstractionfamily(self, dbstate): - """ - Create all markers for each people of a family - 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 - self.center = True - person_handle = self.uistate.get_active('Person') - person = dbstate.db.get_person_from_handle(person_handle) - 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 = _("Id : Father : %s : %s") % ( father.gramps_id, - _nd.display(father) - ) - self._createpersonmarkers(dbstate, father, comment) - handle = fam.get_mother_handle() - mother = dbstate.db.get_person_from_handle(handle) - if mother: - comment = _("Id : Mother : %s : %s") % ( mother.gramps_id, - _nd.display(mother) - ) - self._createpersonmarkers(dbstate, mother, comment) - 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 = _("Id : Child : %(id)s - %(index)d " - ": %(name)s") % { - 'id' : child.gramps_id, - 'index' : index, - 'name' : _nd.display(child) - } - self._createpersonmarkers(dbstate, child, comment) - else: - comment = _("Id : Person : %(id)s %(name)s has no family.") % { - 'id' : person.gramps_id , - 'name' : _nd.display(person) - } - self._createpersonmarkers(dbstate, person, comment) - self._need_to_create_pages(3, self.center, - _("All %(name)s people's family places in" - " the family tree with coordinates.") % { - 'name' :_nd.display(person) }, - ) - - def _createmapstractionperson(self, dbstate): - """ - 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 - latitude = "" - longitude = "" - person_handle = self.uistate.get_active('Person') - person = dbstate.db.get_person_from_handle(person_handle) - self.center = True - 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) - eventyear = 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 ): - self._append_to_places_list(descr, evt, - _nd.display(person), - latitude, longitude, - descr1, self.center, - eventyear, - event.get_type() - ) - self.center = False - else: - self._append_to_places_without_coord( - place.gramps_id, descr) - self._need_to_create_pages(4, self.center, - _("All event places for") + (" %s." % - _nd.display(person) ) ) - - def _need_to_create_pages(self, ptype, center, message ): - """ - Prepare the header of the page if we have no markers. - """ - if center: - page = self._create_message_page( - _("Cannot center the map. No location with coordinates." - "That may happen for one of the following reasons :