2
# -*- coding: utf-8 -*-
4
# Gramps - a GTK+/GNOME based genealogy program
6
# Copyright (C) 2011-2012 Serge Noiraud
8
# This program is free software; you can redistribute it and/or modify
9
# it under the terms of the GNU General Public License as published by
10
# the Free Software Foundation; either version 2 of the License, or
11
# (at your option) any later version.
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
# GNU General Public License for more details.
18
# You should have received a copy of the GNU General Public License
19
# along with this program; if not, write to the Free Software
20
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23
#-------------------------------------------------------------------------
27
#-------------------------------------------------------------------------
28
from gramps.gen.const import GRAMPS_LOCALE as glocale
29
_ = glocale.translation.sgettext
33
from gi.repository import GObject
35
from gi.repository import GLib
38
#-------------------------------------------------------------------------
42
#-------------------------------------------------------------------------
43
from gi.repository import Gtk
44
from gi.repository import GdkPixbuf
47
#-------------------------------------------------------------------------
51
#-------------------------------------------------------------------------
52
from gramps.gen.lib import EventType, Place, PlaceType, PlaceRef
53
from gramps.gen.display.name import displayer as _nd
54
from gramps.gen.display.place import displayer as _pd
55
from gramps.gui.views.navigationview import NavigationView
56
from gramps.gen.utils.libformatting import FormattingHelper
57
from gramps.gen.errors import WindowActiveError
58
from gramps.gen.const import HOME_DIR, IMAGE_DIR
59
from gramps.gui.managedwindow import ManagedWindow
60
from gramps.gen.config import config
61
from gramps.gui.editors import EditPlace, EditEvent, EditFamily, EditPerson
62
from gramps.gui.selectors.selectplace import SelectPlace
63
from gramps.gen.constfunc import conv_to_unicode
65
from gi.repository import OsmGpsMap as osmgpsmap
66
from . import constants
67
from .osmgps import OsmGps
68
from .selectionlayer import SelectionLayer
69
from .placeselection import PlaceSelection
70
from .cairoprint import CairoPrintSave
72
#------------------------------------------------------------------------
76
#------------------------------------------------------------------------
78
_LOG = logging.getLogger("maps.geography")
80
#-------------------------------------------------------------------------
84
#-------------------------------------------------------------------------
85
GEOGRAPHY_PATH = os.path.join(HOME_DIR, "maps")
87
#-------------------------------------------------------------------------
89
# Functions and variables
91
#-------------------------------------------------------------------------
92
PLACE_REGEXP = re.compile('<span background="green">(.*)</span>')
93
PLACE_STRING = '<span background="green">%s</span>'
97
return 1 if we have a negative number, 0 in other case
104
#-------------------------------------------------------------------------
108
#-------------------------------------------------------------------------
109
class GeoGraphyView(OsmGps, NavigationView):
111
View for pedigree tree.
112
Displays the ancestors of a selected individual.
114
#settings in the config file
116
('geography.path', GEOGRAPHY_PATH),
118
('geography.zoom', 10),
119
('geography.zoom_when_center', 12),
120
('geography.show_cross', True),
121
('geography.lock', False),
122
('geography.center-lat', 0.0),
123
('geography.center-lon', 0.0),
125
('geography.map_service', constants.OPENSTREETMAP),
126
('geography.max_places', 5000),
127
('geography.use-keypad', True),
130
def __init__(self, title, pdata, dbstate, uistate,
132
NavigationView.__init__(self, title, pdata, dbstate, uistate,
135
self.dbstate = dbstate
136
self.dbstate.connect('database-changed', self.change_db)
137
self.default_text = "Enter location here!"
138
self.centerlon = config.get("geography.center-lon")
139
self.centerlat = config.get("geography.center-lat")
140
self.zoom = config.get("geography.zoom")
141
self.lock = config.get("geography.lock")
142
if config.get('geography.path') == "" :
143
config.set('geography.path', GEOGRAPHY_PATH )
144
OsmGps.__init__(self)
146
self.format_helper = FormattingHelper(self.dbstate)
147
self.centerlat = self.centerlon = 0.0
148
self.cross_map = None
149
self.current_map = None
152
self.places_found = []
153
self.select_fct = None
154
self.geo_mainmap = None
155
path = os.path.join(IMAGE_DIR, "48x48",
156
('gramps-geo-mainmap' + '.png' ))
157
with open(path, 'rb') as fh: # this is to avoid bug with cairo 1.8.8
158
self.geo_mainmap = cairo.ImageSurface.create_from_png(fh)
159
#self.geo_mainmap = cairo.ImageSurface.create_from_png(path)
160
path = os.path.join(IMAGE_DIR, "48x48",
161
('gramps-geo-altmap' + '.png' ))
162
with open(path, 'rb') as fh: # this is to avoid bug with cairo 1.8.8
163
self.geo_altmap = cairo.ImageSurface.create_from_png(fh)
164
#self.geo_altmap = cairo.ImageSurface.create_from_png(path)
165
if ( config.get('geography.map_service') in
166
( constants.OPENSTREETMAP,
167
constants.MAPS_FOR_FREE,
168
constants.OPENCYCLEMAP,
169
constants.OSM_PUBLIC_TRANSPORT,
171
default_image = self.geo_mainmap
173
default_image = self.geo_altmap
174
self.geo_othermap = {}
175
for ident in ( EventType.BIRTH,
177
EventType.MARRIAGE ):
178
path = os.path.join(IMAGE_DIR, "48x48",
179
(constants.ICONS.get(int(ident), default_image) + '.png' ))
180
with open(path, 'rb') as fh: # this is to avoid bug with cairo 1.8.8
181
self.geo_othermap[ident] = cairo.ImageSurface.create_from_png(fh)
182
#self.geo_othermap[ident] = cairo.ImageSurface.create_from_png(path)
184
def add_bookmark(self, menu):
185
mlist = self.selected_handles()
187
self.bookmarks.add(mlist[0])
189
from gramps.gui.dialog import WarningDialog
191
_("Could Not Set a Bookmark"),
192
_("A bookmark could not be set because "
193
"no one was selected."))
196
def add_bookmark_from_popup(self, menu, handle):
198
self.uistate.set_active(handle, self.navigation_type())
199
self.bookmarks.add(handle)
200
self.bookmarks.redraw()
202
from gramps.gui.dialog import WarningDialog
204
_("Could Not Set a Bookmark"),
205
_("A bookmark could not be set because "
206
"no one was selected."))
208
def change_page(self):
210
Called when the page changes.
212
NavigationView.change_page(self)
213
self.uistate.clear_filter_results()
214
self.end_selection = None
215
self.osm.grab_focus()
216
self.set_crosshair(config.get("geography.show_cross"))
218
def do_size_request(self, requisition):
220
Overridden method to handle size request events.
222
requisition.width = 400
223
requisition.height = 300
225
def do_get_preferred_width(self):
226
""" GTK3 uses width for height sizing model. This method will
227
override the virtual method
229
req = Gtk.Requisition()
230
self.do_size_request(req)
231
return req.width, req.width
233
def do_get_preferred_height(self):
234
""" GTK3 uses width for height sizing model. This method will
235
override the virtual method
237
req = Gtk.Requisition()
238
self.do_size_request(req)
239
return req.height, req.height
243
Save all modified environment
245
NavigationView.on_delete(self)
248
def change_db(self, dbse):
250
Callback associated with DbState. Whenever the database
251
changes, this task is called. In this case, we rebuild the
252
columns, and connect signals to the connected database. Tree
253
is no need to store the database, since we will get the value
257
self.bookmarks.redraw()
259
self.osm.grab_focus()
260
self.set_crosshair(config.get("geography.show_cross"))
262
def can_configure(self):
264
See :class:`~gui.views.pageview.PageView
269
def define_actions(self):
271
Required define_actions function for PageView. Builds the action
272
group information required.
273
As this function is overriden in some plugins, we need to call
276
NavigationView.define_actions(self)
277
self.define_print_actions()
279
def define_print_actions(self):
281
Associate the print button to the PrintView action.
283
self._add_action('PrintView', Gtk.STOCK_PRINT, _("_Print..."),
285
tip=_("Print or save the Map"),
286
callback=self.printview)
288
def config_connect(self):
290
Overwriten from :class:`~gui.views.pageview.PageView method
291
This method will be called after the ini file is initialized,
292
use it to monitor changes in the ini file
294
self._config.connect("geography.path",
296
self._config.connect("geography.zoom_when_center",
297
self.set_zoom_when_center)
299
def set_path(self, client, cnxn_id, entry, data):
301
All geography views must have the same path for maps
303
config.set("geography.path", entry)
305
def set_zoom_when_center(self, client, cnxn_id, entry, data):
307
All geography views must have the same zoom_when_center for maps
309
config.set("geography.zoom_when_center", int(entry))
311
#-------------------------------------------------------------------------
315
#-------------------------------------------------------------------------
316
def build_nav_menu(self, obj, event, lat, lon):
318
Builds the menu for actions on the map.
320
self.menu = Gtk.Menu()
322
menu.set_title(_('Map Menu'))
324
if config.get("geography.show_cross"):
325
title = _('Remove cross hair')
327
title = _('Add cross hair')
328
add_item = Gtk.MenuItem(label=title)
329
add_item.connect("activate", self.config_crosshair, event, lat , lon)
331
menu.append(add_item)
333
if config.get("geography.lock"):
334
title = _('Unlock zoom and position')
336
title = _('Lock zoom and position')
337
add_item = Gtk.MenuItem(label=title)
338
add_item.connect("activate", self.config_zoom_and_position,
341
menu.append(add_item)
343
add_item = Gtk.MenuItem(label=_("Add place"))
344
add_item.connect("activate", self.add_place, event, lat , lon)
346
menu.append(add_item)
348
add_item = Gtk.MenuItem(label=_("Link place"))
349
add_item.connect("activate", self.link_place, event, lat , lon)
351
menu.append(add_item)
353
add_item = Gtk.MenuItem(label=_("Center here"))
354
add_item.connect("activate", self.set_center, event, lat , lon)
356
menu.append(add_item)
358
# Add specific module menu
359
self.add_specific_menu(menu, event, lat, lon)
360
# Add a separator line
361
add_item = Gtk.MenuItem()
363
menu.append(add_item)
365
map_name = constants.map_title[config.get("geography.map_service")]
366
title = _("Replace '%(map)s' by =>") % {
369
add_item = Gtk.MenuItem(label=title)
371
menu.append(add_item)
373
self.changemap = Gtk.Menu()
374
changemap = self.changemap
375
changemap.set_title(title)
377
add_item.set_submenu(changemap)
378
# show in the map menu all available providers
379
for map in constants.map_type:
380
changemapitem = Gtk.MenuItem(label=constants.map_title[map])
382
changemapitem.connect("activate", self.change_map, map)
383
changemap.append(changemapitem)
385
clear_text = _("Clear the '%(map)s' tiles cache.") % {
388
self.clearmap = Gtk.MenuItem(label=clear_text)
389
clearmap = self.clearmap
390
clearmap.connect("activate", self.clear_map, constants.tiles_path[config.get("geography.map_service")])
393
menu.append(clearmap)
395
menu.popup(None, None,
396
lambda menu, data: (event.get_root_coords()[0],
397
event.get_root_coords()[1], True),
398
None, event.button, event.time)
402
def clear_map(self, menu, the_map):
404
We need to clean the tiles cache for the current map
408
path = "%s%c%s" % ( config.get('geography.path'), os.sep, the_map )
412
def add_specific_menu(self, menu, event, lat, lon):
414
Add specific entry to the navigation menu.
415
Must be done in the associated menu.
417
raise NotImplementedError
419
def set_center(self, menu, event, lat, lon):
421
Center the map at the new position then save it.
423
self.osm.set_center_and_zoom(lat, lon,
424
config.get("geography.zoom_when_center"))
425
self.save_center(lat, lon)
427
#-------------------------------------------------------------------------
431
#-------------------------------------------------------------------------
432
def is_there_a_marker_here(self, event, lat, lon):
434
Is there a marker at this position ?
438
self.uistate.set_busy_cursor(True)
439
for mark in self.sort:
440
# as we are not precise with our hand, reduce the precision
441
# depending on the zoom.
443
1 : '%3.0f', 2 : '%3.1f', 3 : '%3.1f', 4 : '%3.1f',
444
5 : '%3.2f', 6 : '%3.2f', 7 : '%3.2f', 8 : '%3.3f',
445
9 : '%3.3f', 10 : '%3.3f', 11 : '%3.3f', 12 : '%3.3f',
446
13 : '%3.3f', 14 : '%3.4f', 15 : '%3.4f', 16 : '%3.4f',
447
17 : '%3.4f', 18 : '%3.4f'
448
}.get(config.get("geography.zoom"), '%3.1f')
450
1 : 5.0, 2 : 5.0, 3 : 3.0,
451
4 : 1.0, 5 : 0.5, 6 : 0.3, 7 : 0.15,
452
8 : 0.06, 9 : 0.03, 10 : 0.015,
453
11 : 0.005, 12 : 0.003, 13 : 0.001,
454
14 : 0.0005, 15 : 0.0003, 16 : 0.0001,
455
17 : 0.0001, 18 : 0.0001
456
}.get(config.get("geography.zoom"), 5.0)
457
latp = precision % lat
458
lonp = precision % lon
459
mlatp = precision % float(mark[3])
460
mlonp = precision % float(mark[4])
461
latok = lonok = False
462
_LOG.debug(" compare latitude : %s with %s (precision = %s)"
463
" place='%s'" % (float(mark[3]), lat, precision,
465
_LOG.debug("compare longitude : %s with %s (precision = %s)"
466
" zoom=%d" % (float(mark[4]), lon, precision,
467
config.get("geography.zoom")))
468
if (float(mlatp) >= (float(latp) - shift) ) and \
469
(float(mlatp) <= (float(latp) + shift) ):
471
if (float(mlonp) >= (float(lonp) - shift) ) and \
472
(float(mlonp) <= (float(lonp) + shift) ):
475
mark_selected.append(mark)
478
self.bubble_message(event, lat, lon, mark_selected)
479
self.uistate.set_busy_cursor(False)
481
def bubble_message(self, event, lat, lon, mark):
483
Display the bubble message. depends on the view.
485
raise NotImplementedError
487
def add_selection_layer(self):
489
add the selection layer
491
selection_layer = SelectionLayer()
492
self.osm.layer_add(selection_layer)
493
return selection_layer
495
def remove_layer(self, layer):
497
Remove the specified layer
499
self.osm.remove_layer(layer)
501
def add_marker(self, menu, event, lat, lon, event_type, differtype, count):
505
mapservice = config.get('geography.map_service')
506
if ( mapservice in ( constants.OPENSTREETMAP,
507
constants.OPENSTREETMAP_RENDERER )):
508
default_image = self.geo_mainmap
510
default_image = self.geo_altmap
511
value = default_image
512
if event_type is not None:
513
value = self.geo_othermap.get(int(event_type), default_image)
514
if differtype: # in case multiple evts
515
value = default_image # we use default icon.
516
self.marker_layer.add_marker((float(lat), float(lon)), value, count)
518
def remove_all_gps(self):
520
Remove all gps points on the map
524
def remove_all_tracks(self):
526
Remove all tracks on the map
528
self.osm.track_remove_all()
530
def remove_all_markers(self):
532
Remove all markers on the map
534
self.marker_layer.clear_markers()
536
def _present_in_places_list(self, index, string):
538
Search a string in place_list depending index
540
found = any(p[index] == string for p in self.place_list)
543
def _append_to_places_list(self, place, evttype, name, lat,
544
longit, descr, year, icontype,
545
gramps_id, place_id, event_id, family_id
548
Create a list of places with coordinates.
550
found = any(p[0] == place for p in self.places_found)
551
if not found and self.nbplaces < self._config.get("geography.max_places"):
552
# We only show the first "geography.max_places".
553
# over 3000 or 4000 places, the geography become unusable.
554
# In this case, filter the places ...
556
self.places_found.append([place, lat, longit])
557
self.place_list.append([place, name, evttype, lat,
558
longit, descr, year, icontype,
559
gramps_id, place_id, event_id, family_id
567
if tfc < self.minyear:
569
if tfc > self.maxyear:
571
tfa += 0.00000001 if tfa >= 0 else -0.00000001
572
tfb += 0.00000001 if tfb >= 0 else -0.00000001
573
if self.minlat == 0.0 or tfa < self.minlat:
575
if self.maxlat == 0.0 or tfa > self.maxlat:
577
if self.minlon == 0.0 or tfb < self.minlon:
579
if self.maxlon == 0.0 or tfb > self.maxlon:
582
def _append_to_places_without_coord(self, gid, place):
584
Create a list of places without coordinates.
586
if not [gid, place] in self.place_without_coordinates:
587
self.place_without_coordinates.append([gid, place])
590
def _create_markers(self):
592
Create all markers for the specified person.
594
self.remove_all_markers()
595
self.remove_all_gps()
596
self.remove_all_tracks()
597
if ( self.current_map is not None and
598
self.current_map != config.get("geography.map_service") ):
599
self.change_map(self.osm, config.get("geography.map_service"))
608
self.uistate.set_busy_cursor(True)
609
_LOG.debug("%s" % time.strftime("start create_marker : "
610
"%a %d %b %Y %H:%M:%S", time.gmtime()))
611
for mark in self.sort:
612
current = ([mark[3], mark[4]])
622
self.add_marker(None, None, lat, lon, icon, differtype, count)
629
else: # This marker already exists. add info.
633
if ( lat != 0.0 and lon != 0.0 ):
634
self.add_marker(None, None, lat, lon, icon, differtype, count)
635
self._set_center_and_zoom()
636
_LOG.debug("%s" % time.strftime(" stop create_marker : "
637
"%a %d %b %Y %H:%M:%S", time.gmtime()))
638
self.uistate.set_busy_cursor(False)
640
def _visible_marker(self, lat, lon):
642
Is this marker in the visible area ?
644
bbox = self.osm.get_bbox()
648
s_bbox_lat1 = pt1.rlat + 10.0
649
s_bbox_lon1 = pt1.rlon + 10.0
651
s_bbox_lat2 = pt2.rlat + 10.0
652
s_bbox_lon2 = pt2.rlon + 10.0
653
result = ( s_bbox_lat1 > s_lat > s_bbox_lat2 ) and \
654
( s_bbox_lon1 < s_lon < s_bbox_lon2 )
657
def _autozoom_in(self, lvl, p1lat, p1lon, p2lat, p2lon):
659
We zoom in until at least one marker missing.
661
if ( ( self._visible_marker(p1lat, p1lon)
662
and self._visible_marker(p2lat, p2lon) )
665
self.osm.set_zoom(lvl)
666
GLib.timeout_add(int(50), self._autozoom_in, lvl,
667
p1lat, p1lon, p2lat, p2lon)
669
GLib.timeout_add(int(50), self._autozoom_out, lvl,
670
p1lat, p1lon, p2lat, p2lon)
672
def _autozoom_out(self, lvl, p1lat, p1lon, p2lat, p2lon):
674
We zoom out until all markers visible.
676
if ( not ( self._visible_marker(p1lat, p1lon)
677
and self._visible_marker(p2lat, p2lon) )
680
self.osm.set_zoom(lvl)
681
GLib.timeout_add(int(50), self._autozoom_out, lvl,
682
p1lat, p1lon, p2lat, p2lon)
684
layer = self.get_selection_layer()
686
self.osm.layer_remove(layer)
690
Try to put all markers on the map. we start at current zoom.
691
If all markers are present, continue to zoom.
692
If some markers are missing : return to the zoom - 1
693
We must use function called by timeout to force map updates.
695
level_start = self.osm.props.zoom
696
p1lat, p1lon = self.begin_selection.get_degrees()
697
p2lat, p2lon = self.end_selection.get_degrees()
698
lat = p1lat + ( p2lat - p1lat ) / 2
699
lon = p1lon + ( p2lon - p1lon ) / 2
700
# We center the map on the center of the region
701
self.osm.set_center(lat, lon)
702
self.save_center(lat, lon)
703
p1lat = self.begin_selection.rlat
704
p1lon = self.begin_selection.rlon
705
p2lat = self.end_selection.rlat
706
p2lon = self.end_selection.rlon
707
# We zoom in until at least one marker missing.
708
GLib.timeout_add(int(50), self._autozoom_in, level_start,
709
p1lat, p1lon, p2lat, p2lon)
711
def _set_center_and_zoom(self):
714
The best should be an auto zoom to have all markers on the screen.
715
need some works here.
716
we start at zoom 1 until zoom y ( for this a preference )
717
If all markers are present, continue to zoom.
718
If some markers are missing : return to the zoom - 1
719
The following is too complex. In some case, all markers are not present.
721
# Select the center of the map and the zoom
722
signminlon = _get_sign(self.minlon)
723
signminlat = _get_sign(self.minlat)
724
signmaxlon = _get_sign(self.maxlon)
725
signmaxlat = _get_sign(self.maxlat)
726
current = osmgpsmap.MapPoint.new_degrees(self.minlat, self.minlon)
727
self.end_selection = current
728
current = osmgpsmap.MapPoint.new_degrees(self.maxlat, self.maxlon)
729
self.begin_selection = current
730
if signminlon == signmaxlon:
731
maxlong = abs(abs(self.minlon) - abs(self.maxlon))
733
maxlong = abs(abs(self.minlon) + abs(self.maxlon))
734
if signminlat == signmaxlat:
735
maxlat = abs(abs(self.minlat) - abs(self.maxlat))
737
maxlat = abs(abs(self.minlat) + abs(self.maxlat))
739
for mark in self.sort:
740
if ( signminlat == signmaxlat ):
742
latit = self.minlat+self.centerlat
744
latit = self.maxlat-self.centerlat
745
elif self.maxlat > self.centerlat:
746
latit = self.maxlat-self.centerlat
748
latit = self.minlat+self.centerlat
749
if ( signminlon == signmaxlon ):
751
longt = self.minlon+self.centerlon
753
longt = self.maxlon-self.centerlon
754
elif self.maxlon > self.centerlon:
755
longt = self.maxlon-self.centerlon
757
longt = self.minlon+self.centerlon
758
# all maps: 0.0 for longitude and latitude means no location.
759
if latit == longt == 0.0:
760
latit = longt = 0.00000001
763
if config.get("geography.lock"):
764
self.osm.set_center_and_zoom(config.get("geography.center-lat"),
765
config.get("geography.center-lon"),
766
config.get("geography.zoom") )
769
self.save_center(self.latit, self.longt)
770
config.set("geography.zoom", self.osm.props.zoom)
771
self.end_selection = None
773
def _get_father_and_mother_name(self, event):
775
Return the father and mother name of a family event
777
dbstate = self.dbstate
779
dbstate.db.get_family_from_handle(ref_handle)
780
for (ref_type, ref_handle) in
781
dbstate.db.find_backlink_handles(event.handle)
782
if ref_type == 'Family'
784
fnam = mnam = _("Unknown")
786
for family in family_list:
787
handle = family.get_father_handle()
788
father = dbstate.db.get_person_from_handle(handle)
789
handle = family.get_mother_handle()
790
mother = dbstate.db.get_person_from_handle(handle)
791
fnam = _nd.display(father) if father else _("Unknown")
792
mnam = _nd.display(mother) if mother else _("Unknown")
793
return ( fnam, mnam )
795
#-------------------------------------------------------------------------
797
# Printing functionalities
799
#-------------------------------------------------------------------------
800
def printview(self, obj):
802
Print or save the view that is currently shown
804
req = self.osm.get_allocation()
806
heightpx = req.height
807
prt = CairoPrintSave(widthpx, heightpx, self.osm.do_draw, self.osm)
810
#-------------------------------------------------------------------------
812
# Specific functionalities
814
#-------------------------------------------------------------------------
815
def center_here(self, menu, event, lat, lon, mark):
817
Center the map at the marker position
819
self.set_center(menu, event, float(mark[3]), float(mark[4]))
821
def add_place_bubble_message(self, event, lat, lon, marks,
822
menu, message, mark):
824
Create the place menu of a marker
826
add_item = Gtk.MenuItem()
828
menu.append(add_item)
829
add_item = Gtk.MenuItem(label=message)
831
menu.append(add_item)
832
self.itemoption = Gtk.Menu()
833
itemoption = self.itemoption
834
itemoption.set_title(message)
836
add_item.set_submenu(itemoption)
837
modify = Gtk.MenuItem(label=_("Edit Place"))
839
modify.connect("activate", self.edit_place, event, lat, lon, mark)
840
itemoption.append(modify)
841
center = Gtk.MenuItem(label=_("Center on this place"))
843
center.connect("activate", self.center_here, event, lat, lon, mark)
844
itemoption.append(center)
845
add_item = Gtk.MenuItem()
847
menu.append(add_item)
849
def edit_place(self, menu, event, lat, lon, mark):
851
Edit the selected place at the marker position
854
place = self.dbstate.db.get_place_from_gramps_id(self.mark[9])
855
parent_list = place.get_placeref_list()
856
if len(parent_list) > 0:
857
parent = parent_list[0].ref
860
self.select_fct = PlaceSelection(self.uistate, self.dbstate, self.osm,
861
self.selection_layer, self.place_list,
862
lat, lon, self.__edit_place, parent)
864
def edit_person(self, menu, event, lat, lon, mark):
866
Edit the selected person at the marker position
868
_LOG.debug("edit_person : %s" % mark[8])
869
# need to add code here to edit the person.
870
person = self.dbstate.db.get_person_from_gramps_id(mark[8])
872
EditPerson(self.dbstate, self.uistate, [], person)
873
except WindowActiveError:
876
def edit_family(self, menu, event, lat, lon, mark):
878
Edit the selected family at the marker position
880
_LOG.debug("edit_family : %s" % mark[11])
881
family = self.dbstate.db.get_family_from_gramps_id(mark[11])
883
EditFamily(self.dbstate, self.uistate, [], family)
884
except WindowActiveError:
887
def edit_event(self, menu, event, lat, lon, mark):
889
Edit the selected event at the marker position
891
_LOG.debug("edit_event : %s" % mark[10])
892
event = self.dbstate.db.get_event_from_gramps_id(mark[10])
894
EditEvent(self.dbstate, self.uistate, [], event)
895
except WindowActiveError:
898
def add_place(self, menu, event, lat, lon):
900
Add a new place using longitude and latitude of location centered
903
self.select_fct = PlaceSelection(self.uistate, self.dbstate, self.osm,
904
self.selection_layer, self.place_list,
905
lat, lon, self.__add_place)
907
def link_place(self, menu, event, lat, lon):
909
Link an existing place using longitude and latitude of location centered
911
If we have a place history, we must show all places to avoid an empty
912
place selection in the PlaceSelection.
914
if self.uistate.get_active('Place'):
915
self._createmap(None)
916
selector = SelectPlace(self.dbstate, self.uistate, [])
917
place = selector.run()
919
parent_list = place.get_placeref_list()
920
if len(parent_list) > 0:
921
parent = parent_list[0].ref
924
places_handle = self.dbstate.db.iter_place_handles()
927
place_title = _pd.display(self.dbstate.db, place)
928
for place_hdl in places_handle:
929
plce = self.dbstate.db.get_place_from_handle(place_hdl)
930
plce_title = _pd.display(self.dbstate.db, plce)
931
if plce_title == place_title:
934
gids = plce.gramps_id
936
gids = gids + ", " + plce.gramps_id
938
from gramps.gui.dialog import WarningDialog
940
_('You have at least two places with the same title.'),
941
_("The title of the places is :\n"
943
"The following places are similar : %(gid)s\n"
944
"Eiher you rename the places either you merge them."
945
"\n\n<b>I can't proceed your request</b>.\n") % {
946
'title': place_title,
950
self.mark = [None, None, None, None, None, None, None,
951
None, None, place.gramps_id, None, None]
952
self.select_fct = PlaceSelection(self.uistate,
955
self.selection_layer,
962
def __add_place(self, parent, plat, plon):
964
Add a new place using longitude and latitude of location centered
967
self.select_fct.close()
969
new_place.set_latitude(str(plat))
970
new_place.set_longitude(str(plon))
972
placeref = PlaceRef()
973
placeref.ref = parent
974
new_place.add_placeref(placeref)
976
EditPlace(self.dbstate, self.uistate, [], new_place)
977
self.add_marker(None, None, plat, plon, None, True, 0)
978
except WindowActiveError:
981
def __edit_place(self, parent, plat, plon):
983
Edit the selected place at the marker position
985
self.select_fct.close()
986
place = self.dbstate.db.get_place_from_gramps_id(self.mark[9])
987
place.set_latitude(str(plat))
988
place.set_longitude(str(plon))
990
placeref = PlaceRef()
991
placeref.ref = parent
992
place.add_placeref(placeref)
994
EditPlace(self.dbstate, self.uistate, [], place)
995
except WindowActiveError:
998
def __link_place(self, parent, plat, plon):
1000
Link an existing place using longitude and latitude of location centered
1003
selector = SelectPlace(self.dbstate, self.uistate, [])
1004
place = selector.run()
1006
self.select_fct.close()
1007
place.set_latitude(str(plat))
1008
place.set_longitude(str(plon))
1010
placeref = PlaceRef()
1011
placeref.ref = parent
1012
place.add_placeref(placeref)
1014
EditPlace(self.dbstate, self.uistate, [], place)
1015
self.add_marker(None, None, plat, plon, None, True, 0)
1016
except WindowActiveError:
1019
#-------------------------------------------------------------------------
1021
# Geography preferences
1023
#-------------------------------------------------------------------------
1024
def _get_configure_page_funcs(self):
1026
The function which is used to create the configuration window.
1028
return [self.map_options, self.specific_options]
1030
def config_zoom_and_position(self, client, cnxn_id, entry, data):
1032
Do we need to lock the zoom and position ?
1034
if config.get("geography.lock"):
1035
config.set("geography.lock", False)
1036
self._set_center_and_zoom()
1038
config.set("geography.lock", True)
1039
self.lock = config.get("geography.lock")
1042
def config_crosshair(self, client, cnxn_id, entry, data):
1044
We asked to change the crosshair.
1046
if config.get("geography.show_cross"):
1047
config.set("geography.show_cross", False)
1049
config.set("geography.show_cross", True)
1050
self.set_crosshair(config.get("geography.show_cross"))
1053
def specific_options(self, configdialog):
1055
Add specific entry to the preference menu.
1056
Must be done in the associated view.
1058
table = Gtk.Table(n_rows=2, n_columns=2)
1059
table.set_border_width(12)
1060
table.set_col_spacings(6)
1061
table.set_row_spacings(6)
1062
configdialog.add_text(table, _('Nothing for this view.'), 0)
1063
return _('Specific parameters'), table
1065
def map_options(self, configdialog):
1067
Function that builds the widget in the configuration dialog
1068
for the map options.
1070
self._config.set('geography.path', config.get('geography.path'))
1071
self._config.set('geography.zoom_when_center',
1072
config.get('geography.zoom_when_center'))
1073
self._config.set('geography.max_places',
1074
self._config.get('geography.max_places'))
1075
table = Gtk.Table(n_rows=1, n_columns=1)
1076
table.set_border_width(12)
1077
table.set_col_spacings(6)
1078
table.set_row_spacings(6)
1079
self.path_entry = Gtk.Entry()
1080
configdialog.add_path_box(table,
1081
_('Where to save the tiles for offline mode.'),
1082
0, self.path_entry, config.get('geography.path'),
1083
self.set_tilepath, self.select_tilepath)
1084
configdialog.add_text(table,
1085
_('If you have no more space in your file system. '
1086
'You can remove all tiles placed in the above path.\n'
1087
'Be careful! If you have no internet, you\'ll get no map.'),
1089
configdialog.add_slider(table,
1090
_('Zoom used when centering'),
1091
3, 'geography.zoom_when_center',
1093
configdialog.add_slider(table,
1094
_('The maximum number of places to show'),
1095
4, 'geography.max_places',
1097
configdialog.add_checkbox(table,
1098
_('Use keypad for shortcuts :\n'
1099
'Either we choose the + and - from the keypad if we select this,\n'
1100
'or we use the characters from the keyboard.'),
1101
5, 'geography.use-keypad',
1102
extra_callback=self.update_shortcuts)
1104
return _('The map'), table
1106
def set_tilepath(self, *obj):
1107
if self.path_entry.get_text().strip():
1108
config.set('geography.path', self.path_entry.get_text())
1110
config.set('geography.path', GEOGRAPHY_PATH )
1112
def select_tilepath(self, *obj):
1113
f = Gtk.FileChooserDialog(
1114
_("Select tile cache directory for offline mode"),
1115
action=Gtk.FileChooserAction.SELECT_FOLDER,
1116
buttons=(Gtk.STOCK_CANCEL,
1117
Gtk.ResponseType.CANCEL,
1119
Gtk.ResponseType.OK))
1120
mpath = config.get('geography.path')
1123
f.set_current_folder(os.path.dirname(mpath))
1126
if status == Gtk.ResponseType.OK:
1127
val = conv_to_unicode(f.get_filename())
1129
self.path_entry.set_text(val)