If you click on a marker in the family map page, you get a popup. In this popup associated with a place, if you have several events, for each event you see the person and the event type. If you click on the person, you go to the related page for this person. If you click on the event type, you go to the related page for this event. Fixes #11150
478 lines
21 KiB
Python
478 lines
21 KiB
Python
# -*- coding: utf-8 -*-
|
|
#!/usr/bin/env python
|
|
#
|
|
# Gramps - a GTK+/GNOME based genealogy program
|
|
#
|
|
# Copyright (C) 2000-2007 Donald N. Allingham
|
|
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
|
|
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
|
|
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
|
|
# Copyright (C) 2008-2009 Brian G. Matherly
|
|
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
|
|
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
|
|
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
|
# Copyright (C) 2010 Jakim Friant
|
|
# Copyright (C) 2010- Serge Noiraud
|
|
# Copyright (C) 2011 Tim G L Lyons
|
|
# Copyright (C) 2013 Benny Malengier
|
|
# Copyright (C) 2016 Allen Crider
|
|
#
|
|
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
#
|
|
|
|
"""
|
|
Narrative Web Page generator.
|
|
|
|
Classe:
|
|
PlacePage - Place index page and individual Place pages
|
|
"""
|
|
#------------------------------------------------
|
|
# python modules
|
|
#------------------------------------------------
|
|
from collections import defaultdict
|
|
from decimal import getcontext
|
|
import logging
|
|
|
|
#------------------------------------------------
|
|
# Gramps module
|
|
#------------------------------------------------
|
|
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
|
from gramps.gen.lib import (PlaceType, Place)
|
|
from gramps.gen.plug.report import Bibliography
|
|
from gramps.plugins.lib.libhtml import Html
|
|
from gramps.gen.utils.place import conv_lat_lon
|
|
from gramps.gen.utils.location import get_main_location
|
|
|
|
#------------------------------------------------
|
|
# specific narrative web import
|
|
#------------------------------------------------
|
|
from gramps.plugins.webreport.basepage import BasePage
|
|
from gramps.plugins.webreport.common import (get_first_letters, first_letter,
|
|
alphabet_navigation, GOOGLE_MAPS,
|
|
primary_difference, _KEYPLACE,
|
|
get_index_letter, FULLCLEAR,
|
|
MARKER_PATH, OPENLAYER,
|
|
OSM_MARKERS, STAMEN_MARKERS,
|
|
MARKERS, html_escape)
|
|
|
|
_ = glocale.translation.sgettext
|
|
LOG = logging.getLogger(".NarrativeWeb")
|
|
getcontext().prec = 8
|
|
|
|
######################################################
|
|
# #
|
|
# Place Pages #
|
|
# #
|
|
######################################################
|
|
class PlacePages(BasePage):
|
|
"""
|
|
This class is responsible for displaying information about the 'Person'
|
|
database objects. It displays this information under the 'Events'
|
|
tab. It is told by the 'add_instances' call which 'Person's to display,
|
|
and remembers the list of persons. A single call to 'display_pages'
|
|
displays both the Event List (Index) page and all the Event
|
|
pages.
|
|
|
|
The base class 'BasePage' is initialised once for each page that is
|
|
displayed.
|
|
"""
|
|
def __init__(self, report):
|
|
"""
|
|
@param: report -- The instance of the main report class for
|
|
this report
|
|
"""
|
|
BasePage.__init__(self, report, title="")
|
|
self.place_dict = defaultdict(set)
|
|
self.placemappages = None
|
|
self.mapservice = None
|
|
self.person = None
|
|
self.familymappages = None
|
|
self.googlemapkey = None
|
|
|
|
# Place needs to display coordinates?
|
|
self.display_coordinates = report.options["coordinates"]
|
|
|
|
def display_pages(self, title):
|
|
"""
|
|
Generate and output the pages under the Place tab, namely the place
|
|
index and the individual place pages.
|
|
|
|
@param: title -- Is the title of the web page
|
|
"""
|
|
LOG.debug("obj_dict[Place]")
|
|
for item in self.report.obj_dict[Place].items():
|
|
LOG.debug(" %s", str(item))
|
|
message = _("Creating place pages")
|
|
with self.r_user.progress(_("Narrated Web Site Report"), message,
|
|
len(self.report.obj_dict[Place]) + 1
|
|
) as step:
|
|
index = 1
|
|
for place_handle in self.report.obj_dict[Place]:
|
|
step()
|
|
index += 1
|
|
self.placepage(self.report, title, place_handle)
|
|
step()
|
|
self.placelistpage(self.report, title,
|
|
self.report.obj_dict[Place].keys())
|
|
|
|
def placelistpage(self, report, title, place_handles):
|
|
"""
|
|
Create a place index
|
|
|
|
@param: report -- The instance of the main report class for
|
|
this report
|
|
@param: title -- Is the title of the web page
|
|
@param: place_handles -- The handle for the place to add
|
|
"""
|
|
BasePage.__init__(self, report, title)
|
|
|
|
output_file, sio = self.report.create_file("places")
|
|
result = self.write_header(self._("Places"))
|
|
placelistpage, dummy_head, dummy_body, outerwrapper = result
|
|
ldatec = 0
|
|
prev_letter = " "
|
|
|
|
# begin places division
|
|
with Html("div", class_="content", id="Places") as placelist:
|
|
outerwrapper += placelist
|
|
|
|
# place list page message
|
|
msg = self._("This page contains an index of all the places in the "
|
|
"database, sorted by their title. "
|
|
"Clicking on a place’s "
|
|
"title will take you to that place’s page.")
|
|
placelist += Html("p", msg, id="description")
|
|
|
|
# begin alphabet navigation
|
|
index_list = get_first_letters(self.r_db, place_handles,
|
|
_KEYPLACE, rlocale=self.rlocale)
|
|
alpha_nav = alphabet_navigation(index_list, self.rlocale)
|
|
if alpha_nav is not None:
|
|
placelist += alpha_nav
|
|
|
|
# begin places table and table head
|
|
with Html("table",
|
|
class_="infolist primobjlist placelist") as table:
|
|
placelist += table
|
|
|
|
# begin table head
|
|
thead = Html("thead")
|
|
table += thead
|
|
|
|
trow = Html("tr")
|
|
thead += trow
|
|
|
|
if self.display_coordinates:
|
|
trow.extend(
|
|
Html("th", label, class_=colclass, inline=True)
|
|
for (label, colclass) in [
|
|
[self._("Letter"), "ColumnLetter"],
|
|
[self._("Place Name | Name"), "ColumnName"],
|
|
[self._("State/ Province"), "ColumnState"],
|
|
[self._("Country"), "ColumnCountry"],
|
|
[self._("Latitude"), "ColumnLatitude"],
|
|
[self._("Longitude"), "ColumnLongitude"]
|
|
]
|
|
)
|
|
else:
|
|
trow.extend(
|
|
Html("th", label, class_=colclass, inline=True)
|
|
for (label, colclass) in [
|
|
[self._("Letter"), "ColumnLetter"],
|
|
[self._("Place Name | Name"), "ColumnName"],
|
|
[self._("State/ Province"), "ColumnState"],
|
|
[self._("Country"), "ColumnCountry"]
|
|
]
|
|
)
|
|
|
|
# bug 9495 : incomplete display of place hierarchy labels
|
|
def sort_by_place_name(obj):
|
|
""" sort by lower case place name. """
|
|
name = self.report.obj_dict[Place][obj][1]
|
|
return name.lower()
|
|
|
|
handle_list = sorted(place_handles,
|
|
key=lambda x: sort_by_place_name(x))
|
|
first = True
|
|
|
|
# begin table body
|
|
tbody = Html("tbody")
|
|
table += tbody
|
|
|
|
for place_handle in handle_list:
|
|
place = self.r_db.get_place_from_handle(place_handle)
|
|
if place:
|
|
if place.get_change_time() > ldatec:
|
|
ldatec = place.get_change_time()
|
|
plc_title = self.report.obj_dict[Place][place_handle][1]
|
|
main_location = get_main_location(self.r_db, place)
|
|
|
|
if plc_title and plc_title != " ":
|
|
letter = get_index_letter(first_letter(plc_title),
|
|
index_list,
|
|
self.rlocale)
|
|
else:
|
|
letter = ' '
|
|
|
|
trow = Html("tr")
|
|
tbody += trow
|
|
|
|
tcell = Html("td", class_="ColumnLetter", inline=True)
|
|
trow += tcell
|
|
if first or primary_difference(letter, prev_letter,
|
|
self.rlocale):
|
|
first = False
|
|
prev_letter = letter
|
|
trow.attr = 'class = "BeginLetter"'
|
|
|
|
ttle = self._("Places beginning "
|
|
"with letter %s") % letter
|
|
tcell += Html("a", letter, name=letter, title=ttle)
|
|
else:
|
|
tcell += " "
|
|
|
|
trow += Html("td",
|
|
self.place_link(
|
|
place.get_handle(),
|
|
plc_title, place.get_gramps_id()),
|
|
class_="ColumnName")
|
|
|
|
trow.extend(
|
|
Html("td", data or " ", class_=colclass,
|
|
inline=True)
|
|
for (colclass, data) in [
|
|
["ColumnState",
|
|
main_location.get(PlaceType.STATE, '')],
|
|
["ColumnCountry",
|
|
main_location.get(PlaceType.COUNTRY, '')]
|
|
]
|
|
)
|
|
|
|
if self.display_coordinates:
|
|
tcell1 = Html("td", class_="ColumnLatitude",
|
|
inline=True)
|
|
tcell2 = Html("td", class_="ColumnLongitude",
|
|
inline=True)
|
|
trow += (tcell1, tcell2)
|
|
|
|
if place.lat and place.long:
|
|
latitude, longitude = conv_lat_lon(place.lat,
|
|
place.long,
|
|
"DEG")
|
|
tcell1 += latitude
|
|
tcell2 += longitude
|
|
else:
|
|
tcell1 += ' '
|
|
tcell2 += ' '
|
|
|
|
# add clearline for proper styling
|
|
# add footer section
|
|
footer = self.write_footer(ldatec)
|
|
outerwrapper += (FULLCLEAR, footer)
|
|
|
|
# send page out for processing
|
|
# and close the file
|
|
self.xhtml_writer(placelistpage, output_file, sio, ldatec)
|
|
|
|
def placepage(self, report, title, place_handle):
|
|
"""
|
|
Create a place page
|
|
|
|
@param: report -- The instance of the main report class for
|
|
this report
|
|
@param: title -- Is the title of the web page
|
|
@param: place_handle -- The handle for the place to add
|
|
"""
|
|
place = report.database.get_place_from_handle(place_handle)
|
|
if not place:
|
|
return
|
|
BasePage.__init__(self, report, title, place.get_gramps_id())
|
|
self.bibli = Bibliography()
|
|
place_name = self.report.obj_dict[Place][place_handle][1]
|
|
ldatec = place.get_change_time()
|
|
|
|
output_file, sio = self.report.create_file(place_handle, "plc")
|
|
self.uplink = True
|
|
self.page_title = place_name
|
|
placepage, head, body, outerwrapper = self.write_header(_("Places"))
|
|
|
|
self.placemappages = self.report.options['placemappages']
|
|
self.mapservice = self.report.options['mapservice']
|
|
self.googlemapkey = self.report.options['googlemapkey']
|
|
self.stamenopts = self.report.options['stamenopts']
|
|
|
|
# begin PlaceDetail Division
|
|
with Html("div", class_="content", id="PlaceDetail") as placedetail:
|
|
outerwrapper += placedetail
|
|
|
|
if self.create_media:
|
|
media_list = place.get_media_list()
|
|
thumbnail = self.disp_first_img_as_thumbnail(media_list,
|
|
place)
|
|
if thumbnail is not None:
|
|
placedetail += thumbnail
|
|
|
|
# add section title
|
|
placedetail += Html("h3",
|
|
html_escape(place_name),
|
|
inline=True)
|
|
|
|
# begin summaryarea division and places table
|
|
with Html("div", id='summaryarea') as summaryarea:
|
|
placedetail += summaryarea
|
|
|
|
with Html("table", class_="infolist place") as table:
|
|
summaryarea += table
|
|
|
|
# list the place fields
|
|
self.dump_place(place, table)
|
|
|
|
# place gallery
|
|
if self.create_media:
|
|
placegallery = self.disp_add_img_as_gallery(media_list, place)
|
|
if placegallery is not None:
|
|
placedetail += placegallery
|
|
|
|
# place notes
|
|
notelist = self.display_note_list(place.get_note_list())
|
|
if notelist is not None:
|
|
placedetail += notelist
|
|
|
|
# place urls
|
|
urllinks = self.display_url_list(place.get_url_list())
|
|
if urllinks is not None:
|
|
placedetail += urllinks
|
|
|
|
# add place map here
|
|
# Link to Gramps marker
|
|
fname = "/".join(['images', 'marker.png'])
|
|
marker_path = self.report.build_url_image("marker.png",
|
|
"images", self.uplink)
|
|
|
|
if self.placemappages:
|
|
if place and (place.lat and place.long):
|
|
latitude, longitude = conv_lat_lon(place.get_latitude(),
|
|
place.get_longitude(),
|
|
"D.D8")
|
|
placetitle = place_name
|
|
|
|
# add narrative-maps CSS...
|
|
fname = "/".join(["css", "narrative-maps.css"])
|
|
url = self.report.build_url_fname(fname, None, self.uplink)
|
|
head += Html("link", href=url, type="text/css",
|
|
media="screen", rel="stylesheet")
|
|
|
|
# add MapService specific javascript code
|
|
src_js = GOOGLE_MAPS + "api/js?sensor=false"
|
|
if self.mapservice == "Google":
|
|
if self.googlemapkey:
|
|
src_js += "&key=" + self.googlemapkey
|
|
head += Html("script", type="text/javascript",
|
|
src=src_js, inline=True)
|
|
else: # OpenStreetMap, Stamen...
|
|
url = self.secure_mode
|
|
url += ("maxcdn.bootstrapcdn.com/bootstrap/3.3.7/"
|
|
"css/bootstrap.min.css")
|
|
head += Html("link", href=url, type="text/javascript",
|
|
rel="stylesheet")
|
|
src_js = self.secure_mode
|
|
src_js += ("ajax.googleapis.com/ajax/libs/jquery/1.9.1/"
|
|
"jquery.min.js")
|
|
head += Html("script", type="text/javascript",
|
|
src=src_js, inline=True)
|
|
src_js = self.secure_mode
|
|
src_js += "openlayers.org/en/latest/build/ol.js"
|
|
head += Html("script", type="text/javascript",
|
|
src=src_js, inline=True)
|
|
url = self.secure_mode
|
|
url += "openlayers.org/en/latest/css/ol.css"
|
|
head += Html("link", href=url, type="text/javascript",
|
|
rel="stylesheet")
|
|
src_js = self.secure_mode
|
|
src_js += ("maxcdn.bootstrapcdn.com/bootstrap/3.3.7/"
|
|
"js/bootstrap.min.js")
|
|
head += Html("script", type="text/javascript",
|
|
src=src_js, inline=True)
|
|
|
|
# section title
|
|
placedetail += Html("h4", self._("Place Map"), inline=True)
|
|
|
|
# begin map_canvas division
|
|
with Html("div", id="map_canvas", inline=True) as canvas:
|
|
placedetail += canvas
|
|
|
|
# Begin inline javascript code because jsc is a
|
|
# docstring, it does NOT have to be properly indented
|
|
if self.mapservice == "Google":
|
|
with Html("script", type="text/javascript",
|
|
indent=False) as jsc:
|
|
head += jsc
|
|
|
|
# Google adds Latitude/ Longitude to its maps...
|
|
plce = placetitle.replace("'", "\\'")
|
|
jsc += MARKER_PATH % marker_path
|
|
jsc += MARKERS % ([[plce,
|
|
latitude,
|
|
longitude,
|
|
1,""]],
|
|
latitude, longitude,
|
|
10)
|
|
|
|
elif self.mapservice == "OpenStreetMap":
|
|
with Html("script", type="text/javascript") as jsc:
|
|
canvas += jsc
|
|
jsc += MARKER_PATH % marker_path
|
|
jsc += OSM_MARKERS % ([[float(longitude),
|
|
float(latitude),
|
|
placetitle,""]],
|
|
longitude, latitude, 10)
|
|
jsc += OPENLAYER
|
|
else: # STAMEN
|
|
with Html("script", type="text/javascript") as jsc:
|
|
canvas += jsc
|
|
jsc += MARKER_PATH % marker_path
|
|
jsc += STAMEN_MARKERS % ([[float(longitude),
|
|
float(latitude),
|
|
placetitle,""]],
|
|
self.stamenopts,
|
|
longitude, latitude, 10)
|
|
jsc += OPENLAYER
|
|
|
|
# add javascript function call to body element
|
|
body.attr += ' onload = "initialize();" '
|
|
|
|
# add div for popups.
|
|
with Html("div", id="popup", inline=True) as popup:
|
|
placedetail += popup
|
|
|
|
# source references
|
|
srcrefs = self.display_ind_sources(place)
|
|
if srcrefs is not None:
|
|
placedetail += srcrefs
|
|
|
|
# References list
|
|
ref_list = self.display_bkref_list(Place, place_handle)
|
|
if ref_list is not None:
|
|
placedetail += ref_list
|
|
|
|
# add clearline for proper styling
|
|
# add footer section
|
|
footer = self.write_footer(ldatec)
|
|
outerwrapper += (FULLCLEAR, footer)
|
|
|
|
# send page out for processing
|
|
# and close the file
|
|
self.xhtml_writer(placepage, output_file, sio, ldatec)
|