# -*- python -*- # -*- coding: utf-8 -*- # # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2007-2009 Serge Noiraud # Copyright (C) 2009 Helge GRAMPS # Copyright (C) 2009 Josip # Copyright (C) 2008 Benny Malengier # # 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 gettext import gettext as _ import os import urlparse import const import operator import locale #------------------------------------------------------------------------- # # GTK/Gnome modules # #------------------------------------------------------------------------- import gtk #------------------------------------------------------------------------- # # Gramps Modules # #------------------------------------------------------------------------- import gen.lib import Utils import Config from BasicUtils import name_displayer as _nd from PlaceUtils import conv_lat_lon #------------------------------------------------------------------------- # # 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 # #------------------------------------------------------------------------- GEOVIEW_SUBPATH = Utils.get_empty_tempdir('geoview') NB_MARKERS_PER_PAGE = 200 #------------------------------------------------------------------------- # # Javascript template # #------------------------------------------------------------------------- _JAVASCRIPT = '''\n") self.mapview.write("\n") self.mapview.write("\n") if maxpages > 1: message = _("There are %d markers to display. They are split up " "over %d pages of %d markers : " % (self.nbmarkers, maxpages, NB_MARKERS_PER_PAGE)) self.mapview.write("
%s
\n" % message) if curpage != 1: priorfile = os.path.join(GEOVIEW_SUBPATH, "GeoV-%c-%05d.html" % (ftype, curpage-1)) priorfile = urlparse.urlunsplit( ('file', '', URL_SEP.join(priorfile.split(os.sep)), '', '')) self.mapview.write("--" % priorfile) else: self.mapview.write(" --") for page in range(1, maxpages+1, 1): if page == curpage: self.mapview.write(" %d" % page) else: if ( page < curpage + 11 ) and ( page > curpage - 11 ): nextfile = os.path.join(GEOVIEW_SUBPATH, "GeoV-%c-%05d.html" % \ (ftype, page)) nextfile = urlparse.urlunsplit( ('file', '', URL_SEP.join(nextfile.split(os.sep)), '', '')) self.mapview.write("\n%d" % (nextfile, page)) if curpage != maxpages: nextfile = os.path.join(GEOVIEW_SUBPATH, "GeoV-%c-%05d.html" % (ftype, curpage+1)) nextfile = urlparse.urlunsplit( ('file', '', URL_SEP.join(nextfile.split(os.sep)), '', '')) self.mapview.write("\n++" % nextfile) else: self.mapview.write(" ++") self.mapview.write("\n
\n") if self.without != 0: self.without_coord_file = os.path.join(GEOVIEW_SUBPATH, "without_coord.html") self.mapview.write("
You have ") filename = urlparse.urlunsplit( ('file', '', URL_SEP.join(self.without_coord_file.split(os.sep)), '', '')) self.mapview.write("%d" % \ ( filename, self.without ) ) self.mapview.write(" places without coordinates
\n" ) self._createpageplaceswithoutcoord() if self.displaytype != "places": self.mapview.write("
\n") self.mapview.write(" %s\n" % _("All")) for year in range(self.minyear, self.maxyear+self.yearint, self.yearint): self.mapview.write(" %s\n" % ( year, year )) self.mapview.write("
\n") self.mapview.write("

%s

" % h3mess) if h4mess: self.mapview.write("

%s

" % h4mess) margin = 10 self.mapview.write("\n
\n" % (self.height * 0.74)) self.mapview.write("
\n" % \ ((self.width - margin*4), (self.height * 0.74 ))) self.mapview.write("\n") self.mapview.write("\n") if _alternate_map() == "microsoft": self.mapview.write("\n") elif _alternate_map() == "yahoo": self.mapview.write("\n") elif _alternate_map() == "openlayers": self.mapview.write("\n") def _createmapstractiontrailer(self): """ Add the last directives for the html page. """ self.mapview.write(" setcenterandzoom(mapstraction);\n") self.mapview.write(" setmarkers(mapstraction);\n") self.mapview.write(" savezoomandposition(mapstraction);\n") self.mapview.write(" if ( current_map != \"openstreetmap\") {") self.mapview.write(" swap_map(current_map,current_map);") self.mapview.write(" };\n") self.mapview.write(" mapstraction.enableScrollWheelZoom();\n") self.mapview.write("\n") self.mapview.write("\n") self.mapview.write("\n") 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 year for events self.sort = sorted(self.place_list, key=operator.itemgetter(7)) else: # Sort by place self.sort = sorted(self.place_list) 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 ? """ nbmarkers = 0 self.nbpages = 0 pages = ( self.nbmarkers / NB_MARKERS_PER_PAGE ) + 1 if (nbmarkers % NB_MARKERS_PER_PAGE) == 0: try: self._createmapstractiontrailer() except: pass self._set_center_and_zoom(ptype) 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 ) * NB_MARKERS_PER_PAGE last = ( self.nbpages * NB_MARKERS_PER_PAGE ) - 1 self._create_markers(ptype, first, last) self._createmapstractionpostheader(h3mess, h4mess, pages, self.nbpages, ftype) self._createmapstractiontrailer() if self.nbpages == 1: self.open(self.htmlfile) def _createmapstraction(self, displaytype): """ Which kind of map are we going to create ? """ 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. """ self.place_without_coordinates.append([gid, place]) self.without += 1 def _append_to_places_list(self, place, evttype, name, lat, longit, descr, center, year): """ Create a list of places with coordinates. """ self.place_list.append([place, name, evttype, lat, longit, descr, int(center), year]) 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 0.0 > tfa < self.minlat: self.minlat = tfa if self.maxlat == 0.0 or 0.0 < tfa > self.maxlat: self.maxlat = tfa if self.minlon == 0.0 or 0.0 > tfb < self.minlon: self.minlon = tfb if self.maxlon == 0.0 or 0.0 < tfb > self.maxlon: self.maxlon = tfb def _create_markers(self, format, firstm, lastm): """ Create all markers for the specified person. """ last = "" indm = 0 divclose = True self.yearinmarker = [] ininterval = False self.setattr = True self.mapview.write(" function setcenterandzoom(mapstraction) {\n") if self.mustcenter: self.centered = True self.mapview.write(" var point = new LatLonPoint") if self.lock_action.get_action('SaveZoom').get_active(): self.mapview.write("(%s,%s);" % (self.reallatitude, self.reallongitude)) else: self.mapview.write("(%s,%s);" % (self.latit, self.longt)) self.mapview.write("mapstraction.setCenterAndZoom") if self.lock_action.get_action('SaveZoom').get_active(): self.mapview.write("(point, %s);\n" % self.realzoom) else: self.mapview.write("(point, %s);\n" % self.zoom) self.setattr = False self.mapview.write("}\n") self.mapview.write(" function setmarkers(mapstraction) {\n") for mark in self.sort: if ( indm >= firstm ) and ( indm <= lastm ): ininterval = True if ininterval: if last != mark[0]: 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") self.mapview.write("('year','%s');" % years) self.yearinmarker = [] self.mapview.write("mapstraction.addMarker(my_marker);") indm += 1 if ( indm > lastm ): if (indm % NB_MARKERS_PER_PAGE) == 0: ininterval = False last = mark[0] if ( indm >= firstm ) and ( indm <= lastm ): self.mapview.write("\n var point = new LatLonPoint") self.mapview.write("(%s,%s);" % (mark[3], mark[4])) self.mapview.write("my_marker = new Marker(point);") self.mapview.write("gmarkers[%d]=my_marker;" % \ (( indm - 1 ) % NB_MARKERS_PER_PAGE)) self.mapview.write("my_marker.setLabel") self.mapview.write("(\"%s\");" % mark[0]) self.yearinmarker.append(mark[7]) divclose = False self.mapview.write("my_marker.setInfoBubble(\"
") if format == 1: self.mapview.write("%s
____________
" % \ mark[0]) self.mapview.write("
%s" % mark[5]) elif format == 2: self.mapview.write("%s____________
" % mark[1]) self.mapview.write("
%s - %s" % (mark[7], mark[5])) elif format == 3: self.mapview.write("%s
____________
" % \ mark[0]) self.mapview.write("
%s - %s" % (mark[7], mark[5])) elif format == 4: self.mapview.write("%s
____________
" % \ mark[0]) self.mapview.write("
%s - %s" % (mark[7], mark[5])) else: # This marker already exists. add info. self.mapview.write("
%s - %s" % (mark[7], mark[5])) ret = 1 for year in self.yearinmarker: if year == mark[7]: ret = 0 if (ret): 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("
\");") self.mapview.write("my_marker.setAttribute('year','%s');" % years) self.mapview.write("mapstraction.addMarker(my_marker);") if self.nbmarkers == 0: # We have no valid geographic point to center the map. # So you'll see the street where I live. # I think another place should be better : # Where is the place where the gramps project began ? # # I think we should put here all gramps developpers. # not only me ... # longitude = 0.0 latitude = 0.0 self.mapview.write("\nvar point = new LatLonPoint") self.mapview.write("(%s,%s);\n" % (latitude, longitude)) self.mapview.write(" mapstraction.setCenterAndZoom") self.mapview.write("(point, %d);\n" % 2) self.mapview.write(" my_marker = new Marker(point);\n") self.mapview.write(" my_marker.setLabel") self.mapview.write("(\"%s\");\n" % _("No location.")) self.mapview.write(" my_marker.setInfoBubble(\"
") self.mapview.write(_("You have no places in your family tree " " with coordinates.")) self.mapview.write("
") self.mapview.write(_("You are looking at the default map.")) self.mapview.write("
\");\n") self.mapview.write(" mapstraction.addMarker(my_marker);") self.mapview.write("\n}") self.mapview.write("\n\n") def _createpersonmarkers(self, dbstate, person, comment): """ Create all markers for the specified person. """ latitude = "" longitude = "" if person: birth_ref = person.get_birth_ref() if birth_ref: birth = dbstate.db.get_event_from_handle(birth_ref.ref) birthdate = birth.get_date_object() birthyear = birthdate.get_year() bplace_handle = birth.get_place_handle() if bplace_handle: place = dbstate.db.get_place_from_handle(bplace_handle) if place: longitude = place.get_longitude() latitude = place.get_latitude() latitude, longitude = conv_lat_lon(latitude, longitude, "D.D8") if comment: descr1 = _("%(comment)s : birth place.") % { 'comment': 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), birthyear) self.center = False else: self._append_to_places_without_coord( place.gramps_id, descr) latitude = "" longitude = "" death_ref = person.get_death_ref() if death_ref: death = dbstate.db.get_event_from_handle(death_ref.ref) deathdate = death.get_date_object() deathyear = deathdate.get_year() dplace_handle = death.get_place_handle() if dplace_handle: place = dbstate.db.get_place_from_handle(dplace_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 = _("%(comment)s : death place.") % { 'comment': 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), deathyear) 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 for place in dbstate.db.iter_places(): 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) self.center = False else: self._append_to_places_without_coord(place.gramps_id, descr) if self.center: mess = _("Cannot center the map. No location with coordinates.") else: mess = "" self._create_pages(1, _("All places in the family tree with coordinates."), mess) 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 = 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 for event in dbstate.db.iter_events(): place_handle = event.get_place_handle() eventdate = event.get_date_object() eventyear = eventdate.get_year() descr1 = _("Id : %(id)s (%(year)s)") % { 'id' : event.gramps_id, 'year' : eventyear} 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") city = place.get_main_location().get_city() country = place.get_main_location().get_country() descr2 = "%s; %s" % (city, country) # 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: descr = "
" for person in person_list: descr = ("%(description)s%(name)s
") % { 'description' : descr, 'name' : _nd.display(person)} #) % { 'eventtype': gen.lib.EventType( descr = ("%(eventtype)s;"+ " %(place)s%(description)s" ) % { 'eventtype': gen.lib.EventType( event.get_type() ), 'place': place.get_title(), 'description': descr} 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) self.center = False else: descr = place.get_title() self._append_to_places_without_coord( place.gramps_id, descr) if self.center: mess = _("Cannot center the map. No location with coordinates.") else: mess = "" self._create_pages(2, _("All events in the family tree with coordinates."), mess) 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 = 0.0 self.maxlat = 0.0 self.minlon = 0.0 self.maxlon = 0.0 self.minyear = 9999 self.maxyear = 0 self.center = True person = None if dbstate.active: person = dbstate.active 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") % father.gramps_id self._createpersonmarkers(dbstate, father, comment) handle = fam.get_mother_handle() mother = dbstate.db.get_person_from_handle(handle) if mother: comment = _("Id : Mother : %s") % mother.gramps_id 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") % { 'id' : child.gramps_id, 'index': index} self._createpersonmarkers(dbstate, child, comment) else: comment = _("Id : Child : %(id)s has no parents.") % { 'id' : person.gramps_id } self._createpersonmarkers(dbstate, person, comment) if self.center: mess = _("Cannot center the map. No location with coordinates.") if person is not None: self._create_pages(3, _("The active person's family members " "have no places with coordinates."), mess) else: self._create_pages(3, _("No active person set."), mess) else: mess = "" self._create_pages(3, ( _("All %(name)s people's family places in the " "family tree with coordinates.") % { 'name' :_nd.display(person) }), mess) 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 = 0.0 self.maxlat = 0.0 self.minlon = 0.0 self.maxlon = 0.0 self.minyear = 9999 self.maxyear = 0 latitude = "" longitude = "" person = None if dbstate.active: person = dbstate.active 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 if event_ref.role != gen.lib.EventRoleType.PRIMARY: # Only match primaries, no witnesses continue event = dbstate.db.get_event_from_handle(event_ref.ref) eventdate = event.get_date_object() eventyear = eventdate.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) self.center = False else: self._append_to_places_without_coord( place.gramps_id, descr) if self.center: mess = _("Cannot center the map. No location with coordinates.") if person is not None: self._create_pages(4, _("The active person has no places with coordinates."), mess) else: self._create_pages(4, _("No active person set."), mess) else: mess = "" self._create_pages(4, ( _("All event places for %s.") % _nd.display(person) ), mess) def _createmapnotimplemented(self): """ Inform the user this work is not implemented. """ self.mapview.write("

%s

" % _("Not yet implemented ..."))