gb_mapfish_appserver 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -29,7 +29,7 @@ Mapfish Appserver comes with the following out-of-the box features:
29
29
  Documentation
30
30
  -------------
31
31
 
32
- For more documentation see the [github.io/gb_mapfish_appserver](http://pka.github.io/gb_mapfish_appserver/)
32
+ For more documentation see the [mapfish-appserver.github.io](http://mapfish-appserver.github.io/)
33
33
 
34
34
  Authors and License
35
35
  -------------------
@@ -52,7 +52,7 @@ class AppsController < ApplicationController
52
52
 
53
53
  session[:map_ts] = Time.now # Used for checking access to non-public WMS
54
54
 
55
- render :layout => false
55
+ render :action => @app, :layout => false
56
56
  end
57
57
 
58
58
  end
@@ -177,7 +177,7 @@ class PrintController < ApplicationController
177
177
  end
178
178
 
179
179
  def baseCmd
180
- "java -cp #{File.dirname(__FILE__)}/../../lib/print/print-standalone.jar org.mapfish.print.ShellMapPrinter --config=#{@configFile}"
180
+ "java -cp #{GbMapfishPrint::PRINT_JAR} org.mapfish.print.ShellMapPrinter --config=#{@configFile}"
181
181
  end
182
182
 
183
183
  def cleanupTempFiles
@@ -0,0 +1,243 @@
1
+ #Mapfish print controller with access control and servlet call
2
+
3
+ class PrintController < ApplicationController
4
+ require 'popen4'
5
+ begin
6
+ require 'RMagick'
7
+ rescue LoadError
8
+ ActionController::Base.logger.info "Couldn't find RMagick. Image export not supported"
9
+ end
10
+
11
+ skip_before_filter :verify_authenticity_token, :only => :create # allow /print/create with POST
12
+
13
+ caches_action :info
14
+
15
+ class JavaError < Exception
16
+ def initialize(cmd, message)
17
+ super(cmd+"\n"+message)
18
+ end
19
+ end
20
+
21
+ def initialize
22
+ @configFile = "#{Rails.root}/config/print.yml"
23
+ end
24
+
25
+ TMP_PREFIX = "/opt/geodata/tmp/mfPrintTempFile" #WARNING: use NFS for multi-node setups! TODO: external config
26
+ TMP_SUFFIX = ".pdf"
27
+ TMP_PURGE_SECONDS = 600
28
+
29
+ OUTPUT_FORMATS = ["pdf", "png", "jpg", "tif", "gif"]
30
+
31
+ def info
32
+ #if PRINT_URL.present?
33
+ #TODO: call_servlet(request)
34
+ cmd = baseCmd + " --clientConfig"
35
+ result = ""
36
+ errors = ""
37
+ status = POpen4::popen4(cmd) do |stdout, stderr, stdin, pid|
38
+
39
+ result = stdout.readlines.join("\n")
40
+ errors = stderr.readlines.join("\n")
41
+ end
42
+ if status.nil? || status.exitstatus != 0
43
+ raise JavaError.new(cmd, errors)
44
+ else
45
+ info = ActiveSupport::JSON.decode(result)
46
+ info['createURL'] = url_for(:protocol => request.protocol, :action=>'create') + '.json'
47
+ # add output formats
48
+ info['outputFormats'] = []
49
+ OUTPUT_FORMATS.each do |output_format|
50
+ info['outputFormats'] << {:name => output_format}
51
+ end
52
+
53
+ respond_to do |format|
54
+ format.json do
55
+ if params[:var]
56
+ render :text=>"var "+params[:var]+"="+result+";"
57
+ else
58
+ render :json=>info
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ def create
66
+ cleanupTempFiles
67
+
68
+ accessible_topics = Topic.accessible_by(current_ability).collect{ |topic| topic.name }
69
+ layers_to_delete = []
70
+ request.parameters["layers"].each do |layer|
71
+ if layer["baseURL"] # WMS layers
72
+ topic = File.basename(URI.parse(layer["baseURL"]).path)
73
+ if accessible_topics.include?(topic)
74
+ # rewrite URL for local WMS, use CGI if layer filter is used
75
+ use_cgi = !layer["customParams"].nil? && layer["customParams"].any? { |param, value| param =~ LAYER_FILTER_REGEX }
76
+ layer["baseURL"] = rewrite_wms_uri(layer["baseURL"], use_cgi)
77
+ if layer["customParams"] #Set map_resolution for mapserver (MapFish print bug?)
78
+ layer["customParams"].delete("DPI")
79
+ layer["customParams"]["map_resolution"] = request.parameters["dpi"]
80
+ end
81
+ # For permission check in WMS controller: pass session as WMS request parameter
82
+ #layer["customParams"]["session"] =
83
+ else
84
+ # collect inaccessible layers for later removal
85
+ layers_to_delete << layer
86
+ end
87
+ end
88
+
89
+ if layer["baseURL"].nil? && layer["styles"] #Vector layers
90
+ layer["styles"].each_value do |style| #NoMethodError (undefined method `each_value' for [""]:Array):
91
+ if style["externalGraphic"]
92
+ style["externalGraphic"].gsub!(LOCAL_GRAPHICS_HOST, '127.0.0.1')
93
+ style["externalGraphic"].gsub!(/^https:/, 'http:')
94
+ end
95
+ end
96
+ end
97
+ end
98
+ # remove inaccessible layers
99
+ request.parameters["layers"] -= layers_to_delete
100
+
101
+ request.parameters["pages"].each do |page|
102
+ # round center coordinates
103
+ page["center"].collect! {|coord| (coord * 100.0).round / 100.0 }
104
+ # add blank user strings if missing
105
+ page["user_title"] = " " if page["user_title"].blank?
106
+ page["user_comment"] = " " if page["user_comment"].blank?
107
+ # base url
108
+ page["base_url"] = "#{request.protocol}#{request.host}"
109
+ # disclaimer
110
+ topic = Topic.accessible_by(current_ability).where(:name => page["topic"]).first
111
+ page["disclaimer"] = topic.nil? ? Topic.default_print_disclaimer : topic.print_disclaimer
112
+ end
113
+
114
+ logger.info request.parameters.to_yaml
115
+
116
+ if PRINT_URL.present?
117
+ call_servlet(request)
118
+ else
119
+ #print-standalone
120
+ tempId = SecureRandom.random_number(2**31)
121
+ temp = TMP_PREFIX + tempId.to_s + TMP_SUFFIX
122
+ cmd = baseCmd + " --output=" + temp
123
+ result = ""
124
+ errors = ""
125
+ status = POpen4::popen4(cmd) do |stdout, stderr, stdin, pid|
126
+ stdin.puts request.parameters.to_json
127
+ #body = request.body
128
+ #FileUtils.copy_stream(body, stdin)
129
+ #body.close
130
+ stdin.close
131
+ result = stdout.readlines.join("\n")
132
+ errors = stderr.readlines.join("\n")
133
+ end
134
+ if status.nil? || status.exitstatus != 0
135
+ raise JavaError.new(cmd, errors)
136
+ else
137
+ convert_and_send_link(temp, tempId, request.parameters["dpi"], request.parameters["outputFormat"])
138
+ end
139
+ end
140
+ end
141
+
142
+ def show
143
+ output_format = params[:format]
144
+ if OUTPUT_FORMATS.include?(output_format)
145
+ temp = TMP_PREFIX + params[:id] + ".#{output_format}"
146
+ case output_format
147
+ when "pdf"
148
+ type = 'application/x-pdf'
149
+ when "png"
150
+ type = 'image/png'
151
+ when "jpg"
152
+ type = 'image/jpeg'
153
+ when "tif"
154
+ type = 'image/tiff'
155
+ when "gif"
156
+ type = 'image/gif'
157
+ end
158
+ send_file temp, :type => type, :disposition => 'attachment', :filename => params[:id] + ".#{output_format}"
159
+ end
160
+ end
161
+
162
+ protected
163
+
164
+ def rewrite_wms_uri(url, use_cgi)
165
+ #http://wms.zh.ch/basis -> http://127.0.0.1/cgi-bin/mapserv.fcgi?MAP=/opt/geodata/mapserver/maps/intranet/basis.map&
166
+ out = url
167
+ # get topic from layer URL
168
+ uri = URI.parse(url)
169
+ localwms = LOCAL_WMS.any? { |ref| uri.host =~ ref }
170
+ if localwms
171
+ topic = File.basename(uri.path)
172
+ localhost = (@zone == ZONE_INTRANET) ? '127.0.0.1' : 'localhost'
173
+ out = "http://#{localhost}#{use_cgi ? MAPSERV_CGI_URL : MAPSERV_URL}?MAP=#{MAPPATH}/#{@zone}/#{topic}.map&"
174
+ #out = "http://#{localhost}:#{request.port}/wms/#{topic}"
175
+ end
176
+ out
177
+ end
178
+
179
+ def baseCmd
180
+ "java -cp #{GbMapfishPrint::PRINT_JAR} org.mapfish.print.ShellMapPrinter --config=#{@configFile}"
181
+ end
182
+
183
+ def cleanupTempFiles
184
+ minTime = Time.now - TMP_PURGE_SECONDS;
185
+ OUTPUT_FORMATS.each do |output_format|
186
+ Dir.glob(TMP_PREFIX + "*." + output_format).each do |path|
187
+ if File.mtime(path) < minTime
188
+ File.delete(path)
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+ def call_servlet(request)
195
+ url = URI.parse(URI.decode(PRINT_URL))
196
+ logger.info "Forward request: #{PRINT_URL}"
197
+ printspec = request.parameters.to_json
198
+
199
+ response = nil
200
+ begin
201
+ http = Net::HTTP.new(url.host, url.port)
202
+ http.start do
203
+ case request.method.to_s
204
+ when 'GET' then response = http.get(url.path) #, request.headers
205
+ #when 'POST' then response = http.post(url.path, printspec)
206
+ when 'POST' then response = http.get("#{url.path}?spec=#{CGI.escape(printspec)}") #-> GET print.pdf
207
+ else
208
+ raise Exception.new("unsupported method `#{request.method}'.")
209
+ end
210
+ end
211
+ rescue => err
212
+ logger.info("#{err.class}: #{err.message}")
213
+ render :nothing => true, :status => 500
214
+ return
215
+ end
216
+ #send_data response.body, :status => response.code, :type=>'application/x-pdf', :disposition=>'attachment', :filename=>'map.pdf'
217
+ tempId = SecureRandom.random_number(2**31)
218
+ temp = TMP_PREFIX + tempId.to_s + TMP_SUFFIX
219
+ File.open(temp, 'w') {|f| f.write(response.body) }
220
+ convert_and_send_link(temp, tempId, request.parameters["dpi"], request.parameters["outputFormat"])
221
+ end
222
+
223
+ # optionally convert PDF to image and send link to print result
224
+ def convert_and_send_link(temp_pdf, temp_id, dpi, output_format)
225
+ temp_suffix = ".pdf"
226
+
227
+ if output_format != "pdf" && OUTPUT_FORMATS.include?(output_format)
228
+ # convert PDF to image
229
+ pdf = Magick::Image.read(temp_pdf) { self.density = dpi }.first
230
+ temp_suffix = ".#{output_format}"
231
+ temp_img = TMP_PREFIX + temp_id.to_s + temp_suffix
232
+ pdf.write(temp_img)
233
+ File.delete(temp_pdf)
234
+ end
235
+
236
+ respond_to do |format|
237
+ format.json do
238
+ render :json=>{ 'getURL' => url_for(:action=>'show', :id=>temp_id) + temp_suffix }
239
+ end
240
+ end
241
+ end
242
+
243
+ end
@@ -6,8 +6,12 @@ class TopicsController < ApplicationController
6
6
  }
7
7
 
8
8
  def index
9
- params[:gbapp] = 'gbzh' if params[:gbapp] == 'default' #FIXME
10
9
  @app = Gbapplication.find_by_name(params[:gbapp]) if params[:gbapp]
10
+ if @app.nil?
11
+ logger.error "Gbapplication '#{params[:gbapp]}' not found"
12
+ head :bad_request
13
+ return
14
+ end
11
15
 
12
16
  respond_to do |format|
13
17
  format.json do
@@ -41,7 +41,7 @@ class GeoModel < ActiveRecord::Base
41
41
  end
42
42
 
43
43
  def bbox
44
- envelope = GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(extent).envelope
44
+ envelope = GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(extent).envelope #TODO: replace with rgeo
45
45
  [envelope.lower_corner.x, envelope.lower_corner.y, envelope.upper_corner.x, envelope.upper_corner.y]
46
46
  end
47
47
 
@@ -12,6 +12,10 @@ class Layer < ActiveRecord::Base
12
12
  scope :defaultorder, order(:topic_name, :name)
13
13
  scope :unused, includes(:topics_layers).where('topics_layers.layer_id IS NULL')
14
14
 
15
+ #Namespace for run-time geo classes
16
+ module Geo
17
+ end
18
+
15
19
  # Enum for RailsAdmin form (causes exception in name search)
16
20
  #def sublayer_group_enum
17
21
  # SublayerGroup.all.collect {|p| [ p.name, p.id ] }
@@ -0,0 +1,283 @@
1
+ class Layer < ActiveRecord::Base
2
+ has_many :topics_layers, :dependent => :destroy
3
+ has_many :topics, :through => :topics_layers
4
+ belongs_to :sublayer_group
5
+
6
+ attr_protected []
7
+
8
+ validates :name, :presence => true, :uniqueness => {:scope => :topic_name}
9
+ validates_format_of :name, :with => /\A[A-Za-z][\w-]*\Z/
10
+ validates_format_of :topic_name, :with => /\A[\w-]+\Z/
11
+
12
+ scope :defaultorder, order(:topic_name, :name)
13
+ scope :unused, includes(:topics_layers).where('topics_layers.layer_id IS NULL')
14
+
15
+ #Namespace for run-time geo classes
16
+ module Geo
17
+ end
18
+
19
+ # Enum for RailsAdmin form (causes exception in name search)
20
+ #def sublayer_group_enum
21
+ # SublayerGroup.all.collect {|p| [ p.name, p.id ] }
22
+ #end
23
+
24
+ #Structure for Topic selection
25
+ def self.list(ability, layer_type, topic_name)
26
+ topic = Topic.accessible_by(ability).includes(:layers).where(:name => topic_name).first
27
+ layers = topic.nil? ? [] : topic.layers.accessible_by(ability).all
28
+ topic_layers = topic.nil? ? [] : topic.topics_layers.select {|tl| layers.include?(tl.layer) }
29
+ wms_layer_list(ability, topic, topic_layers)
30
+ end
31
+
32
+ def self.wms_layer_list(ability, topic, topic_layers)
33
+ wms_layers = topic_layers.collect do |topic_layer|
34
+ layer = topic_layer.layer
35
+ {
36
+ "id" => topic_layer.id,
37
+ "layername"=> layer.name,
38
+ "topic"=> topic.name,
39
+ "groupname" => layer.sublayer_group.try(:name),
40
+ "toclayertitle"=> layer.title,
41
+ "leglayertitle"=> layer.title,
42
+ "showscale"=> "true",
43
+ "minscale"=> layer.minscale,
44
+ "maxscale"=> layer.maxscale,
45
+ "wms_sort"=> topic_layer.wms_sort, # MapServer layer order
46
+ #"leg_sort"=> topic_layer.leg_sort, # Not used client side (Legend sort is in defined in HTML). Query result order.
47
+ #"query_sort"=> topic_layer.leg_sort, # deprecated
48
+ "toc_sort"=> topic_layer.toc_sort, # Layer tree order
49
+ "wms"=> "false",
50
+ "visini"=> topic_layer.visini,
51
+ "visuser"=> topic_layer.visini, #User visibility is in request_state
52
+ "showtoc"=> "true",
53
+ "editeable"=> ability.can?(:edit, layer)
54
+ }
55
+ end
56
+ {
57
+ "success" => true,
58
+ "messageProperty"=> {"topic"=> topic.name, "legendtitle"=> "Legende", "legendraster"=> "true"},
59
+ "results"=> wms_layers.size,
60
+ "wmslayers"=> wms_layers
61
+ }
62
+ end
63
+
64
+ def full_name
65
+ "#{topic_name}-#{name}".downcase
66
+ end
67
+
68
+ def feature_class
69
+ fc = "Geo::#{feature_class_name}".constantize rescue nil #Geo.const_defined?(feature_class_name) seems not to work here
70
+ fc ||= Geo.module_eval <<EOS
71
+ class #{feature_class_name} < GeoModel
72
+ self.table_name = '#{table}'
73
+ self.primary_key = '#{pkey}'
74
+
75
+ self
76
+ end
77
+ EOS
78
+ end
79
+
80
+ def feature_class_name
81
+ table.camelize.singularize
82
+ end
83
+
84
+ def geometry_column
85
+ feature_class.try(:geometry_column)
86
+ end
87
+
88
+ def attribute(name)
89
+ if feature_class.nil?
90
+ ::Attribute.new(self, name)
91
+ else
92
+ @attrs ||= feature_class.columns.inject({}) do |h, c|
93
+ #logger.info "************************* feature_class column c #{c.inspect}"
94
+ h[c.name] = ::Attribute.new(self, c.name)
95
+ h
96
+ end
97
+ @attrs[name] ||= ::Attribute.new(self, name) #Add ad-hoc Attr. for calculated columns (e.g. custom SQL in query fields)
98
+ end
99
+ #logger.info "************************* Attribute for name '#{name}': #{@attrs[name].inspect}"
100
+ end
101
+
102
+ #def filtered(ability)
103
+ # feature_class.where(ability.resource_access_filter(self))
104
+ #end
105
+
106
+ def query_fields(ability)
107
+ return '' if feature_class.nil?
108
+ area_field = "ST_Area(#{geometry_column.name}) AS area"
109
+ #TODO: measure -> Measure(geometry_column, lat, lon)
110
+ ([pkey]+ident_fields_for(ability)+[feature_class.extent_field, area_field]).join(',')
111
+ end
112
+
113
+ def ident_fields_for(ability)
114
+ #attributes.accessible_by(ability) & fields
115
+ #logger.info "************************* fields layer #{name}: #{ident_fields}"
116
+ #logger.info "************************* roles: #{ability.roles.collect(&:name).join(',')}"
117
+ allowed_fields = ident_fields.split(',').select { |f| ability.can?(:show, attribute(f)) }
118
+ #logger.info "************************* ident_fields layer #{name}: #{allowed_fields.inspect}"
119
+ allowed_fields
120
+ end
121
+
122
+ def query(ability, query_topic, searchbbox)
123
+ if feature_class
124
+ begin
125
+ #query_topic: {... customQueries: {<layername>: <query_method> }
126
+ #e.g.
127
+ #{"queryTopics":[{
128
+ # "level":"main","topic":"Lageklassen2011ZH","divCls":"legmain","layers":"seen,lageklassen-2011-flaechen,grenzen,gemeindegrenzen,bezirkslabels"
129
+ # customQueries: {'seen': 'tiefen_statistik'},
130
+ # customParams: {'tiefe': 25}
131
+ # }]}
132
+ custom_query_method = query_topic['customQueries'][name] rescue nil
133
+ logger.debug "******** #{feature_class} ***************************************************"
134
+ features = if custom_query_method
135
+ logger.debug "Custom query on layer #{name}: #{query_topic.inspect}"
136
+ feature_class.send(custom_query_method, self, query_topic, searchbbox)
137
+ else
138
+ logger.debug "Identify on layer #{name} with query fields #{query_fields(ability)} at #{searchbbox.inspect}"
139
+ feature_class.identify_filter(searchbbox, searchdistance).select(query_fields(ability)).all
140
+ end
141
+ logger.debug "Number of features: #{features.size}"
142
+ # calculate bbox of all features
143
+ unless features.empty?
144
+ envelope = GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(features.first['extent']).envelope
145
+ features.each do |feature|
146
+ next if feature == features.first
147
+ envelope.extend!(GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(feature['extent']).envelope)
148
+ end
149
+ bbox = [envelope.lower_corner.x, envelope.lower_corner.y, envelope.upper_corner.x, envelope.upper_corner.y]
150
+ end
151
+ rescue Exception => e
152
+ features = "Table: <b>#{table}</b><br/>Exception: #{e}<br/>query fields: #{query_fields(ability)}<br/>db fields: #{feature_class.column_names.join(',')}<br/>missing: <font color='red'>#{(query_fields(ability).split(',') - feature_class.column_names).join(', ')}</font><br/><br/>"
153
+ logger.info "Identfy error on layer #{name} #{features}"
154
+ end
155
+ [self, features, bbox]
156
+ else
157
+ logger.warn "Table for layer #{name} not found. (Table name: '#{table}')"
158
+ nil
159
+ end
160
+ end
161
+
162
+ # Partial for identify result
163
+ def info_fname
164
+ "_#{name}_info.html.erb"
165
+ end
166
+
167
+ def info_file
168
+ File.join(Rails.root, 'app', 'views', 'layers', 'custom', topic_name.downcase, info_fname)
169
+ end
170
+
171
+ def info_file_auto
172
+ File.join(Rails.root, 'app', 'views', 'layers', 'custom', topic_name.downcase, 'auto', info_fname)
173
+ end
174
+
175
+ # ignore auto file if empty file exists
176
+ def info_file_empty
177
+ File.join(Rails.root, 'app', 'views', 'layers', 'custom', topic_name.downcase, "_#{name}_info_leer.html.erb")
178
+ end
179
+
180
+ def info
181
+ @info ||= begin
182
+ if File.exist?(info_file)
183
+ "layers/custom/#{topic_name.downcase}/#{info_fname[1..-10]}"
184
+ elsif !File.exist?(info_file_empty) && File.exist?(info_file_auto)
185
+ "layers/custom/#{topic_name.downcase}/auto/#{info_fname[1..-10]}"
186
+ else
187
+ nil
188
+ end
189
+ end
190
+ end
191
+
192
+ def infotext(count)
193
+ count > 0 ? "resultcount_p" : "resultcount_s"
194
+ end
195
+
196
+ def infotab
197
+ # INFOLAYOUT=0^1^2^3^4^5^6^7 *** Wenn der Parameter = 1 ist, wird ein leerer String �bergeben.
198
+ # *** Wenn der Parameter = 0 ist, wird der entsprechende Teil weggelassen wenn m�glich
199
+ #
200
+ # LayoutString(0) = "Im Umkreis von <EM>xUmkreisx</EM> Meter(n) wurde <EM>xAnzahlx</EM> Datensatz gefunden.<br><br>"
201
+ # LayoutString(1) = "Im Umkreis von <EM>xUmkreisx</EM> Meter(n) wurden <EM>xAnzahlx</EM> Datens&aumltze gefunden.<br><br>"
202
+ # LayoutString(2) = "layer"
203
+ # LayoutString(3) = "infotext"
204
+ # LayoutString(4) = "infotab"
205
+ # LayoutString(5) = "tabtitle"
206
+ # LayoutString(6) = "tabcell"
207
+ # LayoutString(7) = "2" ( If = "" Then 1 Tabellenzeile pro Record [z.B. "1" oder ""], else [z.B. "2"] 1 Zeile pro Feld)
208
+
209
+ "infotable_horizontal"
210
+ end
211
+
212
+ def legend_fname
213
+ "_#{name}_legend.html.erb"
214
+ end
215
+
216
+ def legend_file
217
+ File.join(Rails.root, 'app', 'views', 'layers', 'custom', topic_name.downcase, legend_fname)
218
+ end
219
+
220
+ def legend_file_auto
221
+ File.join(Rails.root, 'app', 'views', 'layers', 'custom', topic_name.downcase, 'auto', legend_fname)
222
+ end
223
+
224
+ def legend
225
+ @legend ||= begin
226
+ if File.exist?(legend_file)
227
+ "layers/custom/#{topic_name.downcase}/#{legend_fname[1..-10]}"
228
+ elsif File.exist?(legend_file_auto)
229
+ "layers/custom/#{topic_name.downcase}/auto/#{legend_fname[1..-10]}"
230
+ else
231
+ nil
232
+ end
233
+ end
234
+ end
235
+
236
+ def quoted_wms_layers
237
+ wms_layers.split(',').collect {|l| %Q<"#{l}"> }.join(',')
238
+ end
239
+
240
+ DEFAULT_SELECTION_STYLE = {
241
+ 'POLYGON' =>
242
+ '<PolygonSymbolizer>'+
243
+ '<Fill>'+
244
+ '<CssParameter name="fill">#ff0090</CssParameter>'+
245
+ '<CssParameter name="fill-opacity">0.6</CssParameter>'+
246
+ '</Fill>'+
247
+ '<Stroke>'+
248
+ '<CssParameter name="stroke">#ff0090</CssParameter>'+
249
+ '<CssParameter name="stroke-width">2.00</CssParameter>'+
250
+ '</Stroke>'+
251
+ '</PolygonSymbolizer>',
252
+ 'LINESTRING' =>
253
+ '<LineSymbolizer>'+
254
+ '<Stroke>'+
255
+ '<CssParameter name="stroke">#ff0090</CssParameter>'+
256
+ '<CssParameter name="stroke-width">10.00</CssParameter>'+
257
+ '</Stroke>'+
258
+ '</LineSymbolizer>',
259
+ 'POINT' =>
260
+ '<PointSymbolizer>'+
261
+ '<Graphic>'+
262
+ '<Mark>'+
263
+ '<WellKnownName>circle</WellKnownName>'+
264
+ '<Fill>'+
265
+ '<CssParameter name="fill">#ff0090</CssParameter>'+
266
+ '</Fill>'+
267
+ '</Mark>'+
268
+ '<Size>45.0</Size>'+
269
+ '</Graphic>'+
270
+ '</PointSymbolizer>'
271
+ }
272
+
273
+ def selection_symbolizer
274
+ if selection_style.blank?
275
+ gtyp = feature_class.geometry_type.sub(/^MULTI/, '').sub(/M$/, '') #MULTIPOINTM -> POINT
276
+ logger.error "Unsupported selection geometry type #{feature_class.geometry_type}" unless DEFAULT_SELECTION_STYLE.has_key?(gtyp)
277
+ DEFAULT_SELECTION_STYLE[gtyp] || ''
278
+ else
279
+ selection_style
280
+ end
281
+ end
282
+
283
+ end
@@ -1 +1,3 @@
1
- #::RGeo::ActiveRecord::GeometryMixin.set_json_generator(:geojson)
1
+ require 'rgeo-activerecord' # required by activerecord-postgis-adapter
2
+
3
+ ::RGeo::ActiveRecord::GeometryMixin.set_json_generator(:geojson)
@@ -7,3 +7,8 @@ module GbMapfishAppserver
7
7
  engine_name "mapfish"
8
8
  end
9
9
  end
10
+
11
+ SearchRule = Struct.new(:model, :fields, :alias_fields)
12
+
13
+ LocateRule = Struct.new(:model, :layer, :search_field)
14
+
@@ -1,3 +1,3 @@
1
1
  module GbMapfishAppserver
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -37,7 +37,7 @@ module Mapfish
37
37
  ENV['DEFAULT_SITE'] = options["default-site-name"]
38
38
  ENV['ADMIN_PWD'] = SecureRandom.hex(4)
39
39
  begin
40
- MapfishAppserver::Engine.load_seed
40
+ GbMapfishAppserver::Engine.load_seed
41
41
  puts "Generated user for administraton"
42
42
  puts "User: 'admin' - Password: '#{ENV['ADMIN_PWD']}'"
43
43
  rescue
@@ -56,6 +56,16 @@ module Mapfish
56
56
  end
57
57
  end
58
58
 
59
+ def add_search_rules_initializer
60
+ path = "#{Rails.root}/config/initializers/search_rules.rb"
61
+ if File.exists?(path)
62
+ puts "Skipping config/initializers/search_rules.rb creation, as file already exists!"
63
+ else
64
+ puts "Adding search_rules initializer (config/initializers/search_rules.rb)..."
65
+ template "search_rules.rb", path
66
+ end
67
+ end
68
+
59
69
  def rm_application_controller
60
70
  remove_file "#{Rails.root}/app/controllers/application_controller.rb"
61
71
  end
@@ -23,8 +23,3 @@ DEFAULT_Y = 252000
23
23
 
24
24
  # regex for WMS parameters to detect if layer filter is used
25
25
  LAYER_FILTER_REGEX = /^filter_.+$/i
26
-
27
- #Load models in models/geo/ (why not automatically loaded?)
28
- Dir[File.join(Rails.root, "app", "models", "geo", "*.rb")].each do |geomodel|
29
- require_dependency geomodel
30
- end
@@ -0,0 +1,5 @@
1
+ SEARCHRULES = {
2
+ }
3
+
4
+ LOCATERULES = {
5
+ }
@@ -3,15 +3,16 @@ namespace :mapfile do
3
3
  require 'rubygems'
4
4
  require 'iconv'
5
5
 
6
- desc "Create a topic from a mapfile. MAPFILE=file SITE=host"
6
+ desc "Create a topic from a mapfile. MAPFILE=file SITE=host DBONLY=false"
7
7
  task :import_topic => :environment do
8
8
  mapfile = ENV['MAPFILE']
9
9
  site = ENV['SITE'] || SITE_DEFAULT #Use empty string for unpublished map
10
+ dbonly = ENV['DBONLY'] || false
10
11
  if mapfile.nil?
11
12
  puts "Error: Missing argument MAPFILE"
12
13
  else
13
14
  import = MapfileImport.new(mapfile, site)
14
- import.import
15
+ import.import(dbonly)
15
16
  end
16
17
  end
17
18
 
@@ -40,7 +41,7 @@ namespace :mapfile do
40
41
  @ic = Iconv.new('UTF-8','LATIN1')
41
42
  end
42
43
 
43
- def import
44
+ def import(dbonly = false)
44
45
  topic = Topic.find_or_create_by_name(@map.name)
45
46
  topic.title = @map.web.metadata['ows_title'] || @map.web.metadata['wms_title']
46
47
  puts "Importing topic '#{topic.name}' with title '#{topic.title}'"
@@ -133,73 +134,75 @@ namespace :mapfile do
133
134
 
134
135
  layer.save!
135
136
 
136
- #Info & Legend
137
- mkdir_p File.dirname(layer.info_file_auto)
137
+ unless dbonly
138
+ #Info & Legend
139
+ mkdir_p File.dirname(layer.info_file_auto)
138
140
 
139
- if queryable
140
- if mlayer.type == MS_LAYER_RASTER || mlayer.type == MS_LAYER_ANNOTATION
141
- puts "Warning: Raster/Anntotation layer '#{layer.name}' is queryable (TEMPLATE #{mlayer.template})"
142
- end
143
- #puts "Creating #{layer.info_file_auto}"
144
- fields = layer.ident_fields.split(',') rescue []
145
- aliases = if fields.empty?
146
- puts "Warning: Query layer '#{layer.name}' has no attribute definition (wms_include_items)"
147
- []
148
- else
149
- layer.alias_fields.split(',')
150
- end
151
- infotab_template = "_infotable_auto"
152
- template = File.open(File.join(File.dirname(__FILE__), 'templates', "#{infotab_template}.html.erb")).read
153
- template = ERB.new(template, nil, '<>')
154
- File.open(layer.info_file_auto, 'w') do |file|
155
- file << template.result(binding)
141
+ if queryable
142
+ if mlayer.type == MS_LAYER_RASTER || mlayer.type == MS_LAYER_ANNOTATION
143
+ puts "Warning: Raster/Anntotation layer '#{layer.name}' is queryable (TEMPLATE #{mlayer.template})"
144
+ end
145
+ #puts "Creating #{layer.info_file_auto}"
146
+ fields = layer.ident_fields.split(',') rescue []
147
+ aliases = if fields.empty?
148
+ puts "Warning: Query layer '#{layer.name}' has no attribute definition (wms_include_items)"
149
+ []
150
+ else
151
+ layer.alias_fields.split(',')
152
+ end
153
+ infotab_template = "_infotable_auto"
154
+ template = File.open(File.join(File.dirname(__FILE__), 'templates', "#{infotab_template}.html.erb")).read
155
+ template = ERB.new(template, nil, '<>')
156
+ File.open(layer.info_file_auto, 'w') do |file|
157
+ file << template.result(binding)
158
+ end
156
159
  end
157
- end
158
160
 
159
- # Layer legend
160
- legend_icon_path = File.join(Rails.root, 'public', 'images', 'custom', topic_name.downcase)
161
- mkdir_p legend_icon_path
162
- if mlayer.type != MS_LAYER_RASTER && mlayer.type != MS_LAYER_ANNOTATION
163
- #puts "Creating #{layer.legend_file_auto}"
164
- File.open(layer.legend_file_auto, 'w') do |file|
165
- file << "<table class='legtab'>\n"
166
- mlayer.classes.each_with_index do |lclass, i|
167
- #Symbol size for Polygon and Line layers
168
- width = 23
169
- height = 13
170
- filename = nil
171
- if mlayer.type == MS_LAYER_POINT
172
- style = lclass.styles.first
173
- if style.nil?
174
- puts "Warning: Missing style in layer '#{layer.name}'"
175
- else
176
- symbol = @map.symbolset.symbols[style.symbol]
177
- if style.size > 0
178
- width = height = style.size.to_i+1
179
- end
180
- if symbol.imagepath
181
- filename = symbol.imagepath
161
+ # Layer legend
162
+ legend_icon_path = File.join(Rails.root, 'public', 'images', 'custom', topic_name.downcase)
163
+ mkdir_p legend_icon_path
164
+ if mlayer.type != MS_LAYER_RASTER && mlayer.type != MS_LAYER_ANNOTATION
165
+ #puts "Creating #{layer.legend_file_auto}"
166
+ File.open(layer.legend_file_auto, 'w') do |file|
167
+ file << "<table class='legtab'>\n"
168
+ mlayer.classes.each_with_index do |lclass, i|
169
+ #Symbol size for Polygon and Line layers
170
+ width = 23
171
+ height = 13
172
+ filename = nil
173
+ if mlayer.type == MS_LAYER_POINT
174
+ style = lclass.styles.first
175
+ if style.nil?
176
+ puts "Warning: Missing style in layer '#{layer.name}'"
177
+ else
178
+ symbol = @map.symbolset.symbols[style.symbol]
179
+ if style.size > 0
180
+ width = height = style.size.to_i+1
181
+ end
182
+ if symbol.imagepath
183
+ filename = symbol.imagepath
184
+ end
182
185
  end
183
186
  end
184
- end
185
- if filename.nil?
186
- begin
187
- icon = lclass.createLegendIcon(@map, mlayer, width, height)
188
- filename = "#{layer.name}#{i}.png"
189
- icon.save(File.join(legend_icon_path, filename))
190
- rescue Exception => e
191
- puts "Warning: createLegendIcon for class '#{lclass.name}' failed: #{e}"
187
+ if filename.nil?
188
+ begin
189
+ icon = lclass.createLegendIcon(@map, mlayer, width, height)
190
+ filename = "#{layer.name}#{i}.png"
191
+ icon.save(File.join(legend_icon_path, filename))
192
+ rescue Exception => e
193
+ puts "Warning: createLegendIcon for class '#{lclass.name}' failed: #{e}"
194
+ end
195
+ else
196
+ symfile = File.join(@map.mappath, filename)
197
+ filename = File.basename(filename)
198
+ cp(symfile, File.join(legend_icon_path, filename))
192
199
  end
193
- else
194
- symfile = File.join(@map.mappath, filename)
195
- filename = File.basename(filename)
196
- cp(symfile, File.join(legend_icon_path, filename))
200
+ sympath ="/images/custom/#{topic_name.downcase}/#{filename}"
201
+ legtext = lclass.name.force_encoding('utf-8').encode || lclass.getExpressionString
202
+ file << " <%= leg_tab_row('#{legtext}', '#{sympath}') %>\n"
197
203
  end
198
- sympath ="/images/custom/#{topic_name.downcase}/#{filename}"
199
- legtext = lclass.name || lclass.getExpressionString
200
- file << " <%= leg_tab_row('#{legtext}', '#{sympath}') %>\n"
204
+ file << "</table>\n"
201
205
  end
202
- file << "</table>\n"
203
206
  end
204
207
  end
205
208
 
@@ -213,20 +216,22 @@ namespace :mapfile do
213
216
  tl.save!
214
217
  end
215
218
 
216
- #Legend Template
217
- mkdir_p File.dirname(topic.legend_file_auto)
218
- MapfileImport.gen_topic_legend(topic)
219
+ unless dbonly
220
+ #Legend Template
221
+ mkdir_p File.dirname(topic.legend_file_auto)
222
+ MapfileImport.gen_topic_legend(topic)
219
223
 
220
- #Info Header Template
221
- #puts "Creating #{topic.info_header_file}"
222
- touch(topic.info_header_file)
224
+ #Info Header Template
225
+ #puts "Creating #{topic.info_header_file}"
226
+ touch(topic.info_header_file)
223
227
 
224
- #Topic icon
225
- topic_icon = File.join(Rails.root, 'public', 'images', 'custom', topic.icon_fname)
226
- unless File.exist?(topic_icon)
227
- mkdir_p File.dirname(topic_icon)
228
- cp(File.join(File.dirname(__FILE__), 'templates', 'themekl-default.gif'),
229
- topic_icon)
228
+ #Topic icon
229
+ topic_icon = File.join(Rails.root, 'public', 'images', 'custom', topic.icon_fname)
230
+ unless File.exist?(topic_icon)
231
+ mkdir_p File.dirname(topic_icon)
232
+ cp(File.join(File.dirname(__FILE__), 'templates', 'themekl-default.gif'),
233
+ topic_icon)
234
+ end
230
235
  end
231
236
 
232
237
  unless @site.empty?
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gb_mapfish_appserver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-16 00:00:00.000000000 Z
12
+ date: 2013-08-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
- requirement: &17509680 !ruby/object:Gem::Requirement
16
+ requirement: &23143380 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 3.2.13
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *17509680
24
+ version_requirements: *23143380
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: json
27
- requirement: &17509260 !ruby/object:Gem::Requirement
27
+ requirement: &23142780 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *17509260
35
+ version_requirements: *23142780
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: acts_as_tree
38
- requirement: &17508720 !ruby/object:Gem::Requirement
38
+ requirement: &23139880 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - =
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 0.2.0
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *17508720
46
+ version_requirements: *23139880
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: devise
49
- requirement: &17508220 !ruby/object:Gem::Requirement
49
+ requirement: &23139360 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - =
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 2.0.4
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *17508220
57
+ version_requirements: *23139360
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: cancan
60
- requirement: &17507760 !ruby/object:Gem::Requirement
60
+ requirement: &23138860 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - =
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: 1.6.7
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *17507760
68
+ version_requirements: *23138860
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rails_admin
71
- requirement: &17523680 !ruby/object:Gem::Requirement
71
+ requirement: &23154640 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - =
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: 0.0.5
77
77
  type: :runtime
78
78
  prerelease: false
79
- version_requirements: *17523680
79
+ version_requirements: *23154640
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: fastercsv
82
- requirement: &17523300 !ruby/object:Gem::Requirement
82
+ requirement: &23154220 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,21 @@ dependencies:
87
87
  version: '0'
88
88
  type: :runtime
89
89
  prerelease: false
90
- version_requirements: *17523300
90
+ version_requirements: *23154220
91
+ - !ruby/object:Gem::Dependency
92
+ name: GeoRuby
93
+ requirement: &23153320 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: 0.1.4
99
+ type: :runtime
100
+ prerelease: false
101
+ version_requirements: *23153320
91
102
  - !ruby/object:Gem::Dependency
92
103
  name: rgeo
93
- requirement: &17522760 !ruby/object:Gem::Requirement
104
+ requirement: &23152540 !ruby/object:Gem::Requirement
94
105
  none: false
95
106
  requirements:
96
107
  - - =
@@ -98,10 +109,10 @@ dependencies:
98
109
  version: 0.3.20
99
110
  type: :runtime
100
111
  prerelease: false
101
- version_requirements: *17522760
112
+ version_requirements: *23152540
102
113
  - !ruby/object:Gem::Dependency
103
114
  name: rgeo-geojson
104
- requirement: &17522260 !ruby/object:Gem::Requirement
115
+ requirement: &23152040 !ruby/object:Gem::Requirement
105
116
  none: false
106
117
  requirements:
107
118
  - - =
@@ -109,10 +120,10 @@ dependencies:
109
120
  version: 0.2.1
110
121
  type: :runtime
111
122
  prerelease: false
112
- version_requirements: *17522260
123
+ version_requirements: *23152040
113
124
  - !ruby/object:Gem::Dependency
114
125
  name: pg
115
- requirement: &17521800 !ruby/object:Gem::Requirement
126
+ requirement: &23151580 !ruby/object:Gem::Requirement
116
127
  none: false
117
128
  requirements:
118
129
  - - =
@@ -120,10 +131,10 @@ dependencies:
120
131
  version: 0.14.0
121
132
  type: :runtime
122
133
  prerelease: false
123
- version_requirements: *17521800
134
+ version_requirements: *23151580
124
135
  - !ruby/object:Gem::Dependency
125
136
  name: activerecord-postgis-adapter
126
- requirement: &17521340 !ruby/object:Gem::Requirement
137
+ requirement: &23151060 !ruby/object:Gem::Requirement
127
138
  none: false
128
139
  requirements:
129
140
  - - =
@@ -131,7 +142,7 @@ dependencies:
131
142
  version: 0.4.1
132
143
  type: :runtime
133
144
  prerelease: false
134
- version_requirements: *17521340
145
+ version_requirements: *23151060
135
146
  description: Mapfish Appserver is a framework for web mapping applications using OGC
136
147
  standards and the Mapfish protocol.
137
148
  email:
@@ -168,9 +179,9 @@ files:
168
179
  - app/views/groups_users/_form.html.erb
169
180
  - app/views/groups_users/show.html.erb
170
181
  - app/views/apps/_request_state.html.erb
171
- - app/views/apps/show.html.erb
172
182
  - app/views/layouts/embedded.html.erb
173
183
  - app/views/layouts/application.html.erb
184
+ - app/controllers/print_controller.rb~
174
185
  - app/controllers/registrations_controller.rb
175
186
  - app/controllers/categories_controller.rb
176
187
  - app/controllers/application_controller.rb
@@ -196,6 +207,7 @@ files:
196
207
  - app/models/gbapplication.rb
197
208
  - app/models/geo_model.rb
198
209
  - app/models/category.rb
210
+ - app/models/layer.rb~
199
211
  - app/models/wfs.rb
200
212
  - app/models/group.rb
201
213
  - app/models/user.rb
@@ -222,7 +234,6 @@ files:
222
234
  - config/initializers/devise.rb
223
235
  - config/initializers/geodb.rb
224
236
  - config/initializers/rails_admin.rb
225
- - config/initializers/search_rules.rb
226
237
  - config/initializers/rgeo.rb
227
238
  - config/routes.rb
228
239
  - db/seeds.rb
@@ -240,6 +251,7 @@ files:
240
251
  - lib/generators/mapfish/viewer/templates/viewer.html.erb
241
252
  - lib/generators/mapfish/install/install_generator.rb
242
253
  - lib/generators/mapfish/install/templates/print.yml
254
+ - lib/generators/mapfish/install/templates/search_rules.rb
243
255
  - lib/generators/mapfish/install/templates/sencha/workspace/sencha.cfg
244
256
  - lib/generators/mapfish/install/templates/sencha/workspace/plugin.xml
245
257
  - lib/generators/mapfish/install/templates/geodatabase.yml
@@ -4656,12 +4668,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
4656
4668
  - - ! '>='
4657
4669
  - !ruby/object:Gem::Version
4658
4670
  version: '0'
4671
+ segments:
4672
+ - 0
4673
+ hash: 1722996203916667069
4659
4674
  required_rubygems_version: !ruby/object:Gem::Requirement
4660
4675
  none: false
4661
4676
  requirements:
4662
4677
  - - ! '>='
4663
4678
  - !ruby/object:Gem::Version
4664
4679
  version: '0'
4680
+ segments:
4681
+ - 0
4682
+ hash: 1722996203916667069
4665
4683
  requirements: []
4666
4684
  rubyforge_project:
4667
4685
  rubygems_version: 1.8.11
@@ -1,23 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5
- <title>GIS-Browser</title>
6
- <base href="<%= request.protocol %><%= request.host %><%= request.port_string %>/apps/<%= @app %>/" />
7
- <link rel="stylesheet" type="text/css" href="resources/css/cdzh/app.css"/>
8
- <link rel="stylesheet" type="text/css" href="/lib/GbZh/css/gbzh.css"/>
9
- <link rel="stylesheet" type="text/css" href="/lib/GbZh/css/general.css"/>
10
- <script type="text/javascript" src="lib/openlayers/OpenLayers.js"></script>
11
- <script type="text/javascript" src="all-classes.js"></script>
12
- <%= render :partial => 'request_state' %>
13
- <script type="text/javascript" src="/lib/openlayers/lib/OpenLayers/Control/ArcIntersection.js"></script>
14
- <script type="text/javascript" src="/lib/openlayers/lib/OpenLayers/Control/OrthogonalLines.js"></script>
15
- <script type="text/javascript" src="/lib/proj4js/lib/proj4js-compressed.js"></script>
16
- <script type="text/javascript" src="/lib/proj4js/lib/defs/EPSG21781.js"></script>
17
- <script type="text/javascript" src="locale/gb41-lang-<%= I18n.locale %>.js"></script>
18
- <%= csrf_meta_tags %>
19
- </head>
20
- <body>
21
- <noscript>Bitte aktivieren Sie Javascript und Cookies, um diese Applikation zu nutzen!</noscript>
22
- </body>
23
- </html>
@@ -1,3 +0,0 @@
1
- SearchRule = Struct.new(:model, :fields, :alias_fields)
2
-
3
- LocateRule = Struct.new(:model, :layer, :search_field)