Skip to content

Commit 91bcb2a

Browse files
committed
Merge remote-tracking branch 'origin/main' into ftr/rotate-map
2 parents 746bb4d + 1e2c634 commit 91bcb2a

File tree

6 files changed

+100
-17
lines changed

6 files changed

+100
-17
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<div id="dialog-geojson-upload" title="<%= l(:title_geojson_upload) %>">
2+
<textarea placeholder="<%= l(:placeholder_geojson_upload) %>" class="ui-widget ui-state-default ui-corner-all"></textarea>
3+
<input type="file" id="file-selector" accept=".json,.geojson">
4+
</div>

config/locales/en.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,21 @@ en:
8888

8989
project_module_gtt: "GTT"
9090
permission_manage_gtt_settings: "Manage GTT settings"
91+
92+
title_geojson_upload: "Import GeoJSON"
93+
placeholder_geojson_upload: "Please paste a GeoJSON geometry here, or import the GeoJSON data from a file."
94+
95+
# Localize for use in Javascript
96+
gtt_js:
97+
control:
98+
geocoding: "Location search"
99+
geolocation: "My location"
100+
maximize: "Zoom to all features"
101+
upload: "Upload GeoJSON"
102+
fullscreen: "Toggle full-screen"
103+
rotate: "Reset rotation"
104+
zoom-in: "Zoom in"
105+
zoom-out: "Zoom out"
106+
modal:
107+
load: "Load"
108+
cancel: "Cancel"

lib/redmine_gtt/hooks/view_layouts_base_html_head_hook.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ def view_layouts_base_body_bottom(context={})
2929
:maxzoom => Setting.plugin_redmine_gtt['default_map_maxzoom_level'],
3030
:fit_maxzoom => Setting.plugin_redmine_gtt['default_map_fit_maxzoom_level'],
3131
:geocoder => geocoder,
32-
:plugin_settings => Setting.plugin_redmine_gtt.select{ |key, value| key.to_s.match(/^(?!default).+/) }
32+
:plugin_settings => Setting.plugin_redmine_gtt.select{ |key, value| key.to_s.match(/^(?!default).+/) },
33+
:i18n => l(:gtt_js).to_json.html_safe
3334
}, :id => 'gtt-defaults', :style => 'display:none')
3435
return tags.join("\n")
3536
end

lib/redmine_gtt/view_hooks.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,9 @@ class ViewHooks < Redmine::Hook::ViewListener
1313

1414
render_on :view_issues_form_details_top,
1515
partial: 'redmine_gtt/hooks/view_issues_form_details_top'
16+
17+
render_on :view_layouts_base_body_bottom,
18+
partial: 'redmine_gtt/hooks/view_layouts_base_body_bottom'
19+
1620
end
1721
end

src/components/gtt-client.ts

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export class GttClient {
7878
layerArray: Layer[]
7979
defaults: DOMStringMap
8080
contents: DOMStringMap
81+
i18n: any
8182
toolbar: Bar
8283
filters: FilterOption
8384
vector: VectorLayer<VectorSource<Geometry>>
@@ -124,6 +125,7 @@ export class GttClient {
124125
}
125126

126127
this.contents = options.target.dataset
128+
this.i18n = JSON.parse(this.defaults.i18n)
127129

128130
// create map at first
129131
this.map = new Map({
@@ -311,13 +313,17 @@ export class GttClient {
311313
this.setGeolocation(this.map)
312314
this.parseHistory()
313315

314-
this.map.addControl (new FullScreen())
315-
this.map.addControl (new Rotate())
316+
this.map.addControl (new FullScreen({
317+
tipLabel: this.i18n.control.fullscreen
318+
}))
319+
this.map.addControl (new Rotate({
320+
tipLabel: this.i18n.control.rotate
321+
}))
316322

317323
// Control button
318324
const maximizeCtrl = new Button({
319325
html: '<i class="material-icons" >zoom_out_map</i>',
320-
title: "Maximize",
326+
title: this.i18n.control.maximize,
321327
handleClick: () => {
322328
this.zoomToExtent(true);
323329
}
@@ -499,24 +505,61 @@ export class GttClient {
499505
editbar.addControl(control)
500506
})
501507

502-
// Upload button
503-
if (this.contents.upload === "true") {
504-
editbar.addControl(new Button({
505-
html: '<i class="material-icons">file_upload</i>',
506-
title: 'Upload GeoJSON',
507-
handleClick: () => {
508-
const data = prompt("Please paste a GeoJSON geometry here")
508+
// Uses jQuery UI for GeoJSON Upload modal window
509+
const mapObj = this
510+
const dialog = $("#dialog-geojson-upload").dialog({
511+
autoOpen: false,
512+
resizable: true,
513+
height: 'auto',
514+
width: 380,
515+
modal: true,
516+
buttons: {
517+
[mapObj.i18n.modal.load]: function() {
518+
const geojson_input = document.querySelector('#dialog-geojson-upload textarea') as HTMLInputElement
519+
const data = geojson_input.value
509520
if (data !== null) {
510521
const features = new GeoJSON().readFeatures(
511522
JSON.parse(data), {
512523
featureProjection: 'EPSG:3857'
513524
}
514525
)
515-
this.vector.getSource().clear()
516-
this.vector.getSource().addFeatures(features)
517-
this.updateForm(features)
518-
this.zoomToExtent()
526+
mapObj.vector.getSource().clear()
527+
mapObj.vector.getSource().addFeatures(features)
528+
mapObj.updateForm(features)
529+
mapObj.zoomToExtent()
519530
}
531+
$(this).dialog('close')
532+
},
533+
[mapObj.i18n.modal.cancel]: function() {
534+
$(this).dialog('close')
535+
}
536+
}
537+
});
538+
539+
// Upload button
540+
if (this.contents.upload === "true") {
541+
542+
const fileSelector = document.getElementById('file-selector')
543+
fileSelector.addEventListener('change', (event: any) => {
544+
const file = event.target.files[0]
545+
// Check if the file is GeoJSON.
546+
if (file.type && !file.type.startsWith('application/geo')) {
547+
console.log('File is not a GeoJSON document.', file.type, file);
548+
return;
549+
}
550+
const fileReader = new FileReader();
551+
fileReader.addEventListener('load', (event: any) => {
552+
const geojson_input = document.querySelector('#dialog-geojson-upload textarea') as HTMLInputElement
553+
geojson_input.value = JSON.stringify(event.target.result, null, 2)
554+
});
555+
fileReader.readAsText(file);
556+
});
557+
558+
editbar.addControl(new Button({
559+
html: '<i class="material-icons">file_upload</i>',
560+
title: this.i18n.control.geojson,
561+
handleClick: () => {
562+
dialog.dialog('open')
520563
}
521564
}))
522565
}
@@ -988,7 +1031,7 @@ export class GttClient {
9881031
// Control button
9891032
const geolocationCtrl = new Toggle({
9901033
html: '<i class="material-icons">my_location</i>',
991-
title: "Geolocation",
1034+
title: this.i18n.control.geolocation,
9921035
active: false,
9931036
onToggle: (active: boolean) => {
9941037
geolocation.setTracking(active)
@@ -1177,7 +1220,7 @@ export class GttClient {
11771220
// Control button
11781221
const geocodingCtrl = new Toggle({
11791222
html: '<i class="material-icons">manage_search</i>',
1180-
title: "Geocoding",
1223+
title: this.i18n.control.geocoding,
11811224
className: "ctl-geocoding",
11821225
onToggle: (active: boolean) => {
11831226
const text = (document.querySelector("div#" + mapId + " .ctl-geocoding div input") as HTMLInputElement)

src/stylesheets/app.scss

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,16 @@ i[id^='icon_settings_tracker_'] {
106106
max-width: 510px;
107107
padding: 0.5em;
108108
}
109+
110+
.ui-dialog .ui-dialog-buttonpane button {
111+
height: inherit;
112+
}
113+
114+
.ui-dialog .ui-dialog-content {
115+
padding: 0.5em 0 0 0;
116+
}
117+
118+
.ui-dialog .ui-dialog-content textarea {
119+
width: 100%;
120+
height: 300px;
121+
}

0 commit comments

Comments
 (0)