diff --git a/data/css/narrative-maps.css b/data/css/narrative-maps.css index ae926e390..0fb9d1007 100644 --- a/data/css/narrative-maps.css +++ b/data/css/narrative-maps.css @@ -66,3 +66,48 @@ div#FamilyMapDetail div#references table.infolist { div#FamilyMapDetail div#references table.infolist tbody tr td.ColumnPlace { width: 40%; } + + +/* Subsection: popup +------------------------------------------------------ */ +.ol-popup { + position: absolute; + background-color: white; + -webkit-filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2)); + filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2)); + padding: 15px; + border-radius: 10px; + border: 2px solid #111111; + bottom: 12px; + left: -50px; + min-width: 450px; +} +.ol-popup:after, .ol-popup:before { + top: 100%; + border: solid transparent; + height: 0; + width: 0; + position: absolute; + pointer-events: none; +} +.ol-popup:after { + border-top-color: white; + border-width: 10px; + left: 48px; + margin-left: -10px; +} +.ol-popup:before { + border-top-color: #cccccc; + border-width: 11px; + left: 48px; + margin-left: -11px; +} +.ol-popup-closer { + text-decoration: none; + position: absolute; + top: 2px; + right: 8px; +} +.ol-popup-closer:after { + content: "✖"; +} diff --git a/gramps/plugins/webreport/basepage.py b/gramps/plugins/webreport/basepage.py index c7c3ac4f9..831a59ead 100644 --- a/gramps/plugins/webreport/basepage.py +++ b/gramps/plugins/webreport/basepage.py @@ -742,7 +742,7 @@ class BasePage: # pylint: disable=C1001 event_date = event.get_date_object() # 0 = latitude, 1 = longitude, 2 - placetitle, - # 3 = place handle, 4 = event date, 5 = event type + # 3 = place handle, 4 = event found = any(data[3] == place_handle and data[4] == event_date for data in place_lat_long) if not found: @@ -754,7 +754,7 @@ class BasePage: # pylint: disable=C1001 if latitude is not None: etype = event.get_type() place_lat_long.append([latitude, longitude, placetitle, - place_handle, event_date, etype]) + place_handle, event]) def _get_event_place(self, person, place_lat_long): """ diff --git a/gramps/plugins/webreport/common.py b/gramps/plugins/webreport/common.py index 1f733f940..324143e1e 100644 --- a/gramps/plugins/webreport/common.py +++ b/gramps/plugins/webreport/common.py @@ -120,16 +120,27 @@ DROPMASTERS = """ function addMarker() { var location = tracelife[iterator]; var myLatLng = new google.maps.LatLng(location[1], location[2]); + var infoWindow = new google.maps.InfoWindow; - markers.push(new google.maps.Marker({ + var marker = new google.maps.Marker({ position: myLatLng, map: map, draggable: true, title: location[0], animation: google.maps.Animation.DROP - })); + }); + markers.push(marker); iterator++; - }""" + var title = "

" + location[0] + "

" + bindInfoWindow(marker, map, infoWindow, title+location[4]); + } + function bindInfoWindow(marker, map, infoWindow, html) { + google.maps.event.addListener(marker, 'click', function() { + infoWindow.setContent(html); + infoWindow.open(map, marker); + }); + } +""" # javascript for Google's Markers... MARKERS = """ @@ -153,6 +164,7 @@ MARKERS = """ function addMarkers() { var bounds = new google.maps.LatLngBounds(); + var infoWindow = new google.maps.InfoWindow; for (var i = 0; i < tracelife.length; i++) { var location = tracelife[i]; @@ -165,10 +177,20 @@ MARKERS = """ map: map, zIndex: location[3] }); + var title = "

" + location[0] + "

" + bindInfoWindow(marker, map, infoWindow, title+location[4]); bounds.extend(myLatLng); if ( i > 1 ) { map.fitBounds(bounds); }; } - }""" + } + function bindInfoWindow(marker, map, infoWindow, html) { + google.maps.event.addListener(marker, 'click', function() { + infoWindow.setContent(html); + infoWindow.open(map, marker); + }); + } + +""" # javascript for OpenStreetMap's markers... """ @@ -178,7 +200,7 @@ https://openlayers.org/en/latest/examples/ OSM_MARKERS = """ function initialize(){ var map; - var tracelife = %s; + var tracelife = %s var iconStyle = new ol.style.Style({ image: new ol.style.Icon(({ opacity: 1.0, @@ -193,6 +215,7 @@ OSM_MARKERS = """ geometry: new ol.geom.Point(ol.proj.transform([loc[0], loc[1]], 'EPSG:4326', 'EPSG:3857')), name: loc[2], + data: loc[3], }); iconFeature.setStyle(iconStyle); markerSource.addFeature(iconFeature); @@ -201,11 +224,15 @@ OSM_MARKERS = """ source: markerSource, style: iconStyle }); + tooltip = new ol.layer.Vector({ + source: markerSource, + style: iconStyle + }); var centerCoord = new ol.proj.transform([%s, %s], 'EPSG:4326', 'EPSG:3857'); map = new ol.Map({ target: 'map_canvas', layers: [new ol.layer.Tile({ source: new ol.source.OSM() }), - markerLayer], + markerLayer, tooltip], view: new ol.View({ center: centerCoord, zoom: %d }) }); """ @@ -213,85 +240,119 @@ OSM_MARKERS = """ STAMEN_MARKERS = """ function initialize(){ var map; - var tracelife = %s; + var tracelife = %s var layer = '%s'; var iconStyle = new ol.style.Style({ image: new ol.style.Icon(({ - //anchor: [0.5, 46], + anchor: [0.2, 48], anchorXUnits: 'fraction', anchorYUnits: 'pixels', opacity: 1.0, src: marker_png })) }); - var markerSource = new ol.Collection(); + var markerSource = new ol.source.Vector({ + }); for (var i = 0; i < tracelife.length; i++) { var loc = tracelife[i]; var iconFeature = new ol.Feature({ geometry: new ol.geom.Point(ol.proj.transform([loc[0], loc[1]], 'EPSG:4326', 'EPSG:3857')), name: loc[2], + data: loc[3], }); iconFeature.setStyle(iconStyle); - markerSource.push(iconFeature); + markerSource.addFeature(iconFeature); } var centerCoord = new ol.proj.transform([%s, %s], 'EPSG:4326', 'EPSG:3857'); + markerLayer = new ol.layer.Vector({ + source: markerSource, + style: iconStyle + }); + tooltip = new ol.layer.Vector({ + source: markerSource, + style: iconStyle + }); map = new ol.Map({ target: 'map_canvas', layers: [ new ol.layer.Tile({ source: new ol.source.Stamen({ - layer: layer - }) - }), - new ol.layer.Vector({ source: new ol.source.Vector({ - features: markerSource }) - }) - ], + layer: layer }) }), + markerLayer, tooltip], view: new ol.View({ center: centerCoord, zoom: %d }) }); """ OPENLAYER = """ var element = document.getElementById('popup'); + var content = document.getElementById('popup-content'); + var closer = document.getElementById('popup-closer'); + var tip = document.getElementById('tooltip'); + var tipcontent = document.getElementById('tooltip-content'); + var tooltip = new ol.Overlay({ - element: element, + element: tip, positioning: 'bottom-center', - stopEvent: false + offset: [10, 0], }); map.addOverlay(tooltip); - var displayFeatureInfo = function(pixel) { - var feature = map.forEachFeatureAtPixel(pixel, function(feature, layer) { + + var popup = new ol.Overlay({ + element: element, + positioning: 'bottom-center', + autoPan: true, + autoPanAnimation: { duration: 500 }, + stopEvent: false + }); + map.addOverlay(popup); + + /** + * Add a click handler to hide the popup. + * @return {boolean} Don't follow the href. + */ + closer.onclick = function() { + popup.setPosition(undefined); + closer.blur(); + return false; + }; + + map.on('pointermove', function(evt) { + evt.preventDefault() + var feature = this.forEachFeatureAtPixel(evt.pixel, + function(feature, layer) { return feature; }); - var info = document.getElementById('popup'); - if (feature) { - var geometry = feature.getGeometry(); - var coord = geometry.getCoordinates(); - tooltip.setPosition(coord); - $(element).siblings('.popover').css({ width: '250px' }); - $(element).siblings('.popover').css({ background: '#aaa' }); - $(info).popover({ - 'placement': 'auto', - 'html': true, - 'content': feature.get('name') - }); - $(info).popover('show'); - } else { - // TODO : some warning with firebug here - $(info).popover('destroy'); - $('.popover').remove(); - } - }; - map.on('pointermove', function(evt) { + map.getTargetElement().style.cursor = feature ? 'pointer' : ''; if (evt.dragging) { + popup.setPosition(undefined); + tooltip.setPosition(undefined); return; } - var pixel = map.getEventPixel(evt.originalEvent); - displayFeatureInfo(pixel); - }); - map.on('click', function(evt) { - displayFeatureInfo(evt.pixel); + if (feature) { + var coordinate = evt.coordinate; + tipcontent.innerHTML = feature.get('name'); + tooltip.setPosition(coordinate); + } else { + tooltip.setPosition(undefined); + } + }); + map.on('singleclick', function(evt) { + evt.preventDefault() + var feature = map.forEachFeatureAtPixel(evt.pixel, + function(feature, layer) { + return feature; + }); + if (feature) { + var coordinate = evt.coordinate; + var title = '

' + feature.get('name') + '

'; + content.innerHTML = title + feature.get('data'); + popup.setPosition(coordinate); + } else { + popup.setPosition(undefined); + } + }); + }; """ diff --git a/gramps/plugins/webreport/person.py b/gramps/plugins/webreport/person.py index 450d0afc2..3016b2c67 100644 --- a/gramps/plugins/webreport/person.py +++ b/gramps/plugins/webreport/person.py @@ -52,7 +52,7 @@ import logging #------------------------------------------------ from gramps.gen.const import GRAMPS_LOCALE as glocale from gramps.gen.lib import (ChildRefType, Date, Name, Person, EventRoleType, - EventType) + Event, EventType) from gramps.gen.lib.date import Today from gramps.gen.plug.report import Bibliography from gramps.gen.plug.report import utils @@ -121,6 +121,7 @@ class PersonPages(BasePage): self.sort_name = None self.googleopts = None self.googlemapkey = None + self.stamenopts = None self.birthorder = None self.person = None self.familymappages = None @@ -617,6 +618,35 @@ class PersonPages(BasePage): # and close the file self.xhtml_writer(indivdetpage, output_file, sio, date) + def _create_family_tracelife(self, tracelife, placetitle, + latitude, longitude, seq_, links): + """ + creates individual family tracelife map events + + @param: person -- person from database + @param: links -- used to add links in the popup html page + """ + # are we using Google? + if self.mapservice == "Google": + + # are we creating Family Links? + if self.googleopts == "FamilyLinks": + tracelife += """ + new google.maps.LatLng(%s, %s),""" % (latitude, longitude) + + # are we creating Drop Markers or Markers? + elif self.googleopts in ["Drop", "Markers"]: + tracelife += """ + ['%s', %s, %s, %d, %s],""" % (placetitle.replace("'", "\\'"), latitude, + longitude, seq_, links) + + # are we using OpenStreetMap, Stamen... + else: + tracelife += """ + [%f, %f, \'%s\', %s],""" % (float(longitude), float(latitude), + placetitle.replace("'", "\\'"), links) + return tracelife + def __create_family_map(self, person, place_lat_long): """ creates individual family map page @@ -640,7 +670,7 @@ class PersonPages(BasePage): number_markers = len(place_lat_long) if number_markers > 1: for (latitude, longitude, placetitle, handle, - date, etype) in place_lat_long: + event) in place_lat_long: xwidth.append(latitude) yheight.append(longitude) xwidth.sort() @@ -680,8 +710,8 @@ class PersonPages(BasePage): # 0 = latitude, 1 = longitude, 2 = place title, # 3 = handle, and 4 = date, 5 = event type... - # being sorted by date, latitude, and longitude... - place_lat_long = sorted(place_lat_long, key=itemgetter(4, 0, 1)) + # being sorted by place_title + place_lat_long = sorted(place_lat_long, key=itemgetter(2)) # for all plugins # if family_detail_page @@ -730,61 +760,113 @@ class PersonPages(BasePage): if number_markers > 0: tracelife = "[" - seq_ = 1 + seq_ = 0 - for index in range(0, (number_markers - 1)): - (latitude, longitude, placetitle, handle, date, - etype) = place_lat_long[index] + old_place_title = "" + oldevent = None + links = "" + ln_str = "%s" + for index in range(0, number_markers): + (latitude, longitude, placetitle, handle, + event) = place_lat_long[index] + # Do we have several events for this place? + if placetitle == old_place_title: + evthdle = event.get_handle() + bkref_list = self.report.bkref_dict[Event][evthdle] + url_fct = self.report.build_url_fname_html + if bkref_list: + for ref in bkref_list: + (bkref_class, bkref_hdle, role) = ref + if role == "Primary": + url = url_fct(bkref_hdle, + "ppl", self.uplink) + ppl_fct = self.r_db.get_person_from_handle + person = ppl_fct(bkref_hdle) + ppl_lnk = ln_str % (url, + person.get_gramps_id(), + self.get_name(person)) + url = self.report.build_url_fname_html(event.get_handle(), + "evt", self.uplink) + evt_type = self._(str(event.get_type())) + evt_date = self.rlocale.get_date(event.get_date_object()) + evt_lnk = ln_str % (url, evt_date, evt_type) - # are we using Google? - if self.mapservice == "Google": + links += ' + "
%s"' % (ppl_lnk + self._(":") + evt_lnk) + if index == number_markers - 1: + tracelife = self._create_family_tracelife(tracelife, + placetitle, + latitude, + longitude, + seq_, + links) + break + continue + elif old_place_title != "" and index != 0: + (lat, lng, plcetitle, handle_, + event_) = place_lat_long[index-1] + tracelife = self._create_family_tracelife(tracelife, + plcetitle, + lat, + lng, + seq_, + links) + if old_place_title != placetitle: + old_place_title = placetitle + evthdle = event.get_handle() + bkref_list = self.report.bkref_dict[Event][evthdle] + url_fct = self.report.build_url_fname_html + if bkref_list: + for ref in bkref_list: + (bkref_class, bkref_hdle, role) = ref + if role == "Primary": + url = url_fct(bkref_hdle, + "ppl", self.uplink) + ppl_fct = self.r_db.get_person_from_handle + person = ppl_fct(bkref_hdle) + ppl_lnk = ln_str % (url, + person.get_gramps_id(), + self.get_name(person)) + url = self.report.build_url_fname_html(event.handle, + "evt", + self.uplink) + evt_type = self._(str(event.get_type())) + evt_date = self.rlocale.get_date(event.get_date_object()) + evt_lnk = ln_str % (url, evt_date, evt_type) - # are we creating Family Links? - if self.googleopts == "FamilyLinks": - tracelife += """ - new google.maps.LatLng(%s, %s),""" % (latitude, longitude) - - # are we creating Drop Markers or Markers? - elif self.googleopts in ["Drop", "Markers"]: - tracelife += """ - ['%s', %s, %s, %d],""" % (placetitle.replace("'", "\\'"), latitude, - longitude, seq_) - - # are we using OpenStreetMap? + links = '"
%s"' % (ppl_lnk + self._(":") + + evt_lnk) + elif index == number_markers: + tracelife = self._create_family_tracelife(tracelife, + placetitle, + latitude, + longitude, + seq_, + links) else: - tracelife += """ - [%f, %f, \'%s\'],""" % (float(longitude), float(latitude), - placetitle.replace("'", "\\'")) - + evthdle = event.get_handle() + bkref_list = self.report.bkref_dict[Event][evthdle] + url_fct = self.report.build_url_fname_html + if bkref_list: + for ref in bkref_list: + (bkref_class, bkref_hdle, role) = ref + if role == "Primary": + url = url_fct(bkref_hdle, + "ppl", self.uplink) + ppl_fct = self.r_db.get_person_from_handle + person = ppl_fct(bkref_hdle) + ppl_lnk = ln_str % (url, + person.get_gramps_id(), + self.get_name(person)) + url = self.report.build_url_fname_html(event.handle, + "evt", + self.uplink) + evt_type = self._(str(event.get_type())) + evt_lnk = ln_str % (url, evt_type, evt_type) + links = '"

%s"' % (ppl_lnk + self._(":") + evt_lnk) + old_place_title = placetitle seq_ += 1 - # FIXME: The last element in the place_lat_long list is treated - # specially, and the code above is apparently repeated so as to - # avoid a comma at the end, and get the right closing. This is very - # ugly. - (latitude, longitude, placetitle, handle, date, - etype) = place_lat_long[-1] - - # are we using Google? - if self.mapservice == "Google": - - # are we creating Family Links? - if self.googleopts == "FamilyLinks": - tracelife += """ - new google.maps.LatLng(%s, %s) - ];""" % (latitude, longitude) - - # are we creating Drop Markers or Markers? - elif self.googleopts in ["Drop", "Markers"]: - tracelife += """ - ['%s', %s, %s, %d] - ];""" % (placetitle.replace("'", "\\'"), latitude, longitude, seq_) - - # we are using OpenStreetMap, Stamen... - else: - tracelife += """ - [%f, %f, \'%s\'] - ];""" % (float(longitude), float(latitude), placetitle.replace("'", "\\'")) + tracelife += "];" # begin MapDetail division... with Html("div", class_="content", id="FamilyMapDetail") as mapdetail: outerwrapper += mapdetail @@ -872,13 +954,13 @@ class PersonPages(BasePage): longitude, latitude, 10, - ) + ) else: jsc += STAMEN_MARKERS % (tracelife, self.stamenopts, midy_, midx_, zoomlevel, - ) + ) jsc += OPENLAYER # if Google and Drop Markers are selected, @@ -888,8 +970,20 @@ class PersonPages(BasePage): id="drop", onclick="drop()", inline=True) # add div for popups. - with Html("div", id="popup", inline=True) as popup: - mapdetail += popup + if self.mapservice == "Google": + with Html("div", id="popup", inline=True) as popup: + mapdetail += popup + else: + with Html("div", id="popup", class_="ol-popup", + inline=True) as popup: + mapdetail += popup + popup += Html("a", href="#", id="popup-closer", + class_="ol-popup-closer") + popup += Html("div", id="popup-content") + with Html("div", id="tooltip", class_="ol-popup", + inline=True) as tooltip: + mapdetail += tooltip + tooltip += Html("div", id="tooltip-content") # begin place reference section and its table... with Html("div", class_="subsection", id="references") as section: @@ -917,11 +1011,16 @@ class PersonPages(BasePage): tbody = Html("tbody") table += tbody - for (latitude, longitude, placetitle, handle, date, - etype) in place_lat_long: + # being sorted by date + place_lat_long = sorted(place_lat_long, + key=lambda evt: + evt[4].get_date_object()) + for (latitude, longitude, placetitle, handle, + event) in place_lat_long: trow = Html("tr") tbody += trow + date = event.get_date_object() trow.extend( Html("td", data, class_=colclass, inline=True) for data, colclass in [ @@ -929,7 +1028,7 @@ class PersonPages(BasePage): (self.place_link(handle, placetitle, uplink=True), "ColumnPlace"), - (str(etype), "ColumnType") + (str(event.get_type()), "ColumnType") ] ) diff --git a/gramps/plugins/webreport/place.py b/gramps/plugins/webreport/place.py index a95e63632..56850be09 100644 --- a/gramps/plugins/webreport/place.py +++ b/gramps/plugins/webreport/place.py @@ -426,7 +426,7 @@ class PlacePages(BasePage): jsc += MARKERS % ([[plce, latitude, longitude, - 1]], + 1,""]], latitude, longitude, 10) @@ -436,7 +436,7 @@ class PlacePages(BasePage): jsc += MARKER_PATH % marker_path jsc += OSM_MARKERS % ([[float(longitude), float(latitude), - placetitle]], + placetitle,""]], longitude, latitude, 10) jsc += OPENLAYER else: # STAMEN @@ -445,7 +445,7 @@ class PlacePages(BasePage): jsc += MARKER_PATH % marker_path jsc += STAMEN_MARKERS % ([[float(longitude), float(latitude), - placetitle]], + placetitle,""]], self.stamenopts, longitude, latitude, 10) jsc += OPENLAYER