Skip to content

Commit 6b19b5c

Browse files
committed
Merge pull request #193 from cga-harvard/arcgis_rest
Display ArcGIS REST layers
2 parents d10ba7e + c1f66e7 commit 6b19b5c

File tree

3 files changed

+365
-3
lines changed

3 files changed

+365
-3
lines changed

src/script/plugins/AddLayers.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* @requires plugins/GeoNodeCatalogueSource.js
1414
* @requires widgets/CatalogueSearchPanel.js
1515
* @requires plugins/TMSSource.js
16+
* @requires plugins/ArcRestSource.js
1617
*/
1718

1819
/** api: (define)
@@ -559,7 +560,17 @@ gxp.plugins.AddLayers = Ext.extend(gxp.plugins.Tool, {
559560
},
560561
"urlselected": function(newSourceDialog, url, type) {
561562
newSourceDialog.setLoading();
562-
var ptype = (type === 'TMS') ? 'gxp_tmssource' : 'gxp_wmscsource';
563+
var ptype;
564+
switch (type) {
565+
case 'TMS':
566+
ptype = "gxp_tmssource";
567+
break;
568+
case 'REST':
569+
ptype = 'gxp_arcrestsource';
570+
break;
571+
default:
572+
ptype = 'gxp_wmscsource';
573+
}
563574
this.target.addLayerSource({
564575
config: {url: url, ptype: ptype},
565576
callback: function(id) {

src/script/plugins/ArcRestSource.js

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
/**
2+
* Copyright (c) 2008-2011 The Open Planning Project
3+
*
4+
* Published under the GPL license.
5+
* See https://github.com/opengeo/gxp/raw/master/license.txt for the full text
6+
* of the license.
7+
*/
8+
9+
/**
10+
* @requires plugins/LayerSource.js
11+
* @requires OpenLayers/Layer/ArcGIS93Rest.js
12+
*/
13+
14+
/** api: (define)
15+
* module = gxp.plugins
16+
* class = ArcRestSource
17+
*/
18+
19+
/** api: (extends)
20+
* plugins/LayerSource.js
21+
*/
22+
Ext.namespace("gxp.plugins");
23+
24+
/** api: constructor
25+
* .. class:: ArcRestSource(config)
26+
*
27+
* Plugin for using ArcGIS REST MapServer layers with :class:`gxp.Viewer` instances.
28+
*
29+
*/
30+
/** api: example
31+
* The configuration in the ``sources`` property of the :class:`gxp.Viewer` is
32+
* straightforward:
33+
*
34+
* .. code-block:: javascript
35+
*
36+
* defaultSourceType: "gxp_arcrestsource",
37+
* sources: {
38+
* "arcgisrest": {
39+
* url: "http://cga6.cga.harvard.edu/arcgis/rest/services/darmc/roman/MapServer"
40+
* }
41+
* }
42+
*
43+
* A typical configuration for a layer from this source (in the ``layers``
44+
* array of the viewer's ``map`` config option would look like this:
45+
*
46+
* .. code-block:: javascript
47+
*
48+
* {
49+
* source: "arcgisrest",
50+
* name: "Routes",
51+
* layerid: "show:74"
52+
* }
53+
*
54+
*/
55+
56+
gxp.plugins.ArcRestSource = Ext.extend(gxp.plugins.LayerSource, {
57+
58+
/** api: ptype = gxp_arcrestsource */
59+
ptype:"gxp_arcrestsource",
60+
61+
requiredProperties: ["name"],
62+
63+
constructor:function (config) {
64+
this.config = config;
65+
gxp.plugins.ArcRestSource.superclass.constructor.apply(this, arguments);
66+
},
67+
68+
69+
/** private: method[createStore]
70+
*
71+
* Creates a store of layers. This requires that the API script has already
72+
* loaded. Fires the "ready" event when the store is loaded.
73+
*/
74+
createStore:function () {
75+
var baseUrl = this.url.split("?")[0];
76+
var source = this;
77+
78+
var processResult = function (response) {
79+
var json = Ext.decode(response.responseText);
80+
81+
var layerProjection = source.getArcProjection(json.spatialReference.wkid);
82+
83+
var layers = [];
84+
if (layerProjection != null) {
85+
for (var l = 0; l < json.layers.length; l++) {
86+
var layer = json.layers[l];
87+
var layerShow = "show:" + layer.id;
88+
layers.push(new OpenLayers.Layer.ArcGIS93Rest(layer.name, baseUrl + "/export",
89+
{
90+
layers:layerShow,
91+
TRANSPARENT:true
92+
},
93+
{
94+
isBaseLayer:false,
95+
ratio: 1,
96+
displayInLayerSwitcher:true,
97+
visibility:true,
98+
projection:layerProjection,
99+
queryable:json.capabilities && json.capabilities["Identify"]}
100+
));
101+
}
102+
} else {
103+
processFailure(response);
104+
}
105+
106+
source.title = json.documentInfo.Title;
107+
108+
source.store = new GeoExt.data.LayerStore({
109+
layers:layers,
110+
fields:[
111+
{name:"source", type:"string"},
112+
{name:"name", type:"string", mapping:"name"},
113+
{name:"layerid", type:"string"},
114+
{name:"group", type:"string", defaultValue:this.title},
115+
{name:"fixed", type:"boolean", defaultValue:true},
116+
{name:"tiled", type:"boolean", defaultValue:true},
117+
{name:"queryable", type:"boolean", defaultValue:true},
118+
{name:"selected", type:"boolean"}
119+
]
120+
});
121+
122+
123+
source.fireEvent("ready", source);
124+
};
125+
126+
var processFailure = function (response) {
127+
Ext.Msg.alert("No ArcGIS Layers", "Could not find any compatible layers at " + source.config.url);
128+
source.fireEvent("failure", source);
129+
};
130+
131+
132+
this.lazy = this.isLazy();
133+
134+
if (!this.lazy) {
135+
Ext.Ajax.request({
136+
url:baseUrl,
137+
params:{'f':'json', 'pretty':'false', 'keepPostParams':'true'},
138+
method:'POST',
139+
success:processResult,
140+
failure:processFailure
141+
});
142+
} else {
143+
this.fireEvent("ready");
144+
}
145+
},
146+
147+
148+
/** private: method[isLazy]
149+
* :returns: ``Boolean``
150+
*
151+
* The store for a lazy source will not be loaded upon creation. A source
152+
* determines whether or not it is lazy given the configured layers for
153+
* the target. If the layer configs have all the information needed to
154+
* construct layer records, the source can be lazy.
155+
*/
156+
isLazy: function() {
157+
var lazy = true;
158+
var sourceFound = false;
159+
var mapConfig = this.target.initialConfig.map;
160+
if (mapConfig && mapConfig.layers) {
161+
var layerConfig;
162+
for (var i=0, ii=mapConfig.layers.length; i<ii; ++i) {
163+
layerConfig = mapConfig.layers[i];
164+
if (layerConfig.source === this.id) {
165+
sourceFound = true;
166+
lazy = this.layerConfigComplete(layerConfig);
167+
if (lazy === false) {
168+
break;
169+
}
170+
}
171+
}
172+
}
173+
return (lazy && sourceFound);
174+
},
175+
176+
177+
/** private: method[layerConfigComplete]
178+
* :returns: ``Boolean``
179+
*
180+
* A layer configuration is considered complete if it has a title and a
181+
* bbox.
182+
*/
183+
layerConfigComplete: function(config) {
184+
var lazy = true;
185+
var props = this.requiredProperties;
186+
for (var i=props.length-1; i>=0; --i) {
187+
lazy = !!config[props[i]];
188+
if (lazy === false) {
189+
break;
190+
}
191+
}
192+
193+
return lazy;
194+
},
195+
196+
/** api: method[getConfigForRecord]
197+
* :arg record: :class:`GeoExt.data.LayerRecord`
198+
* :returns: ``Object``
199+
*
200+
* Create a config object that can be used to recreate the given record.
201+
*/
202+
createLayerRecord:function (config) {
203+
var record, layer;
204+
var cmp = function (l) {
205+
return l.get("name") === config.name;
206+
};
207+
208+
var recordExists = this.lazy || (this.store && this.store.findBy(cmp) > -1);
209+
210+
// only return layer if app does not have it already
211+
if (this.target.mapPanel.layers.findBy(cmp) == -1 && recordExists) {
212+
// records can be in only one store
213+
214+
if (!this.lazy && this.store.findBy(cmp) > -1 ) {
215+
record = this.store.getAt(this.store.findBy(cmp)).clone();
216+
} else {
217+
record = this.createLazyLayerRecord(config);
218+
}
219+
layer = record.getLayer();
220+
221+
// set layer title from config
222+
if (config.title) {
223+
layer.setName(config.title);
224+
record.set("title", config.title);
225+
}
226+
// set visibility from config
227+
if ("visibility" in config) {
228+
layer.visibility = config.visibility;
229+
}
230+
231+
if ("opacity" in config) {
232+
layer.opacity = config.opacity;
233+
}
234+
235+
if ("format" in config) {
236+
layer.params.FORMAT = config.format;
237+
record.set("format", config.format);
238+
}
239+
240+
var singleTile = false;
241+
if ("tiled" in config) {
242+
singleTile = !config.tiled;
243+
}
244+
record.set("tiled", !singleTile);
245+
record.set("selected", config.selected || false);
246+
record.set("queryable", config.queryable || true);
247+
record.set("source", config.source);
248+
record.set("name", config.name);
249+
record.set("layerid", config.layerid);
250+
record.set("properties", "gxp_wmslayerpanel");
251+
if ("group" in config) {
252+
record.set("group", config.group);
253+
}
254+
record.commit();
255+
}
256+
return record;
257+
},
258+
259+
260+
/** api: method[getProjection]
261+
* :arg layerRecord: ``GeoExt.data.LayerRecord`` a record from this
262+
* source's store
263+
* :returns: ``OpenLayers.Projection`` A suitable projection for the
264+
* ``layerRecord``. If the layer is available in the map projection,
265+
* the map projection will be returned. Otherwise an equal projection,
266+
* or null if none is available.
267+
*
268+
* Get the projection that the source will use for the layer created in
269+
* ``createLayerRecord``. If the layer is not available in a projection
270+
* that fits the map projection, null will be returned.
271+
*/
272+
getArcProjection:function (srs) {
273+
var projection = this.getMapProjection();
274+
var compatibleProjection = projection;
275+
var layerSRS = "EPSG:" + srs + '';
276+
if (layerSRS !== projection.getCode()) {
277+
compatibleProjection = null;
278+
if ((p = new OpenLayers.Projection(layerSRS)).equals(projection)) {
279+
compatibleProjection = p;
280+
}
281+
}
282+
return compatibleProjection;
283+
},
284+
285+
/** private: method[createLazyLayerRecord]
286+
* :arg config: ``Object`` The application config for this layer.
287+
* :returns: ``GeoExt.data.LayerRecord``
288+
*
289+
* Create a minimal layer record
290+
*/
291+
createLazyLayerRecord: function(config) {
292+
var srs = config.srs || this.target.map.projection;
293+
config.srs = {};
294+
config.srs[srs] = true;
295+
296+
var bbox = config.bbox || this.target.map.maxExtent || OpenLayers.Projection.defaults[srs].maxExtent;
297+
config.bbox = {};
298+
config.bbox[srs] = {bbox: bbox};
299+
300+
var record = new GeoExt.data.LayerRecord(config);
301+
record.set("name", config.name);
302+
record.set("layerid", config.layerid || "show:0");
303+
record.set("format", config.format || "png");
304+
record.set("tiled", "tiled" in config ? config.tiled : true);
305+
306+
record.setLayer(new OpenLayers.Layer.ArcGIS93Rest(config.name, this.url.split("?")[0] + "/export",
307+
{
308+
layers: config.layerid,
309+
TRANSPARENT:true,
310+
FORMAT: "format" in config ? config.format : "png"
311+
},
312+
{
313+
isBaseLayer:false,
314+
displayInLayerSwitcher:true,
315+
projection:srs,
316+
singleTile: "tiled" in config ? !config.tiled : false,
317+
queryable: "queryable" in config ? config.queryable : false}
318+
)
319+
);
320+
return record;
321+
322+
},
323+
324+
325+
/** api: method[getConfigForRecord]
326+
* :arg record: :class:`GeoExt.data.LayerRecord`
327+
* :returns: ``Object``
328+
*
329+
* Create a config object that can be used to recreate the given record.
330+
*/
331+
getConfigForRecord: function(record) {
332+
var layer = record.getLayer();
333+
return {
334+
source: record.get("source"),
335+
name: record.get("name"),
336+
title: record.get("title"),
337+
tiled: record.get("tiled"),
338+
visibility: layer.getVisibility(),
339+
layerid: layer.params.LAYERS,
340+
format: layer.params.FORMAT,
341+
opacity: layer.opacity || undefined,
342+
group: record.get("group"),
343+
fixed: record.get("fixed"),
344+
selected: record.get("selected")
345+
};
346+
}
347+
348+
});
349+
350+
Ext.preg(gxp.plugins.ArcRestSource.prototype.ptype, gxp.plugins.ArcRestSource);

src/script/widgets/NewSourceDialog.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ gxp.NewSourceDialog = Ext.extend(Ext.Panel, {
4444
* ``String``
4545
* Message to display when an invalid URL is entered (i18n).
4646
*/
47-
invalidURLText: "Enter a valid URL to a WMS/TMS endpoint (e.g. http://example.com/geoserver/wms)",
47+
invalidURLText: "Enter a valid URL to a WMS/TMS/REST endpoint (e.g. http://example.com/geoserver/wms)",
4848

4949
/** api: config[contactingServerText]
5050
* ``String``
@@ -101,7 +101,8 @@ gxp.NewSourceDialog = Ext.extend(Ext.Panel, {
101101
triggerAction: 'all',
102102
store: [
103103
['WMS', 'Web Map Service (WMS)'],
104-
['TMS', 'Tiled Map Service (TMS)']
104+
['TMS', 'Tiled Map Service (TMS)'],
105+
['REST', 'ArcGIS REST Service (REST)']
105106
]
106107
}, this.urlTextField],
107108
border: false,

0 commit comments

Comments
 (0)