Developing a new plugin in MapStore2

1,291 views
Skip to first unread message

Ali Nasri

unread,
Jun 5, 2017, 10:55:46 AM6/5/17
to mapstore-developers
Hi,

I have an exprerience with ReactJS but I'm not familiar with MapStore2 ,i'm trying to create a react-component which renders like this:
<div {loads of div-properties}>
 <iframe id="something" src="http://www.google.com" style="width: 1000px; height: 800px;"></iframe> </div>
And has a method to set the src of the iframe:
 loadDocument: function(uri) {
 this.iframe.src = uri; }

I don't think that is so complicated but i don't have the main experience with your new FrameWork.
Does anybody have any idea about what should i do.
I'll be gratefull if the response will contain some documents or usefull links.

Thank you very much.

Kind Regards

Ali Nasri

Lorenzo Natali

unread,
Jun 6, 2017, 4:22:34 AM6/6/17
to mapstore-...@googlegroups.com
Hi,
There are some ways to render an Iframe in react.


We use to make MapStore2 components stateless and manage the state with Redux. So if you need change some properties (e.g. the iframe src) you should probably create a reducer and connect it to the component.

MapStore2 follows the convention to connect all the components to the application state in at the plugin level: http://dev.mapstore2.geo-solutions.it/mapstore/docs/plugins-architecture

 




--
You received this message because you are subscribed to the Google Groups "mapstore-developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mapstore-developers+unsub...@googlegroups.com.
To post to this group, send email to mapstore-developers@googlegroups.com.
Visit this group at https://groups.google.com/group/mapstore-developers.
For more options, visit https://groups.google.com/d/optout.



--


Regards,
Lorenzo Natali

==
GeoServer Professional Services from the experts! Visit
http://goo.gl/it488V for more information.

==

Dott. Ing. Lorenzo Natali
Software Engineer

GeoSolutions S.A.S.
Via di Montramito 3/A
55054  Massarosa (LU)
Italy
phone: +39 0584 962313
fax:     +39 0584 1660272
mob:   +39  333 8128928

http://www.geo-solutions.it
http://twitter.com/geosolutions_it

-------------------------------------------------------

AVVERTENZE AI SENSI DEL D.Lgs. 196/2003

Le informazioni contenute in questo messaggio di posta elettronica e/o nel/i file/s allegato/i sono da considerarsi strettamente riservate. Il loro utilizzo è consentito esclusivamente al destinatario del messaggio, per le finalità indicate nel messaggio stesso. Qualora riceviate questo messaggio senza esserne il destinatario, Vi preghiamo cortesemente di darcene notizia via e-mail e di procedere alla distruzione del messaggio stesso, cancellandolo dal Vostro sistema. Conservare il messaggio stesso, divulgarlo anche in parte, distribuirlo ad altri soggetti, copiarlo, od utilizzarlo per finalità diverse, costituisce comportamento contrario ai principi dettati dal D.Lgs. 196/2003.

 

The information in this message and/or attachments, is intended solely for the attention and use of the named addressee(s) and may be confidential or proprietary in nature or covered by the provisions of privacy act (Legislative Decree June, 30 2003, no.196 - Italy's New Data Protection Code).Any use not in accord with its purpose, any disclosure, reproduction, copying, distribution, or either dissemination, either whole or partial, is strictly forbidden except previous formal approval of the named addressee(s). If you are not the intended recipient, please contact immediately the sender by telephone, fax or e-mail and delete the information in this message that has been received in error. The sender does not give any warranty or accept liability as the content, accuracy or completeness of sent messages and accepts no responsibility  for changes made after they were sent or for other risks which arise as a result of e-mail transmission, viruses, etc.

Ali Nasri

unread,
Jun 7, 2017, 8:01:03 AM6/7/17
to mapstore-developers


Hi Lorenzo,
  
   Thank you for the quick reply, it was very helpful, the idea is to create a new plugin similar to "Identify",but when i click on the button it should display the Iframe, i followed the plugin architecture, but it doesn't show nothing on the map. Obviously the new plugin was not deloyed on the server. Any idea about this?
 
Best regards

Ali Nasri

Lorenzo Natali

unread,
Jun 7, 2017, 8:37:44 AM6/7/17
to mapstore-...@googlegroups.com
Hi,
Identify renders a modal. If you followed the same strategy you probably have to check if you set the properties to display it (see here as an example), but I'm trying to guess. 
Other options can be : 
  • You didn't configured the plugin in localConfig.json (or the file you're using to configure the application) In mapstore 2 product it have to stay in "desktop" section of localConfig)
  • You didn't added the plugin in your plugin.js file
  • ... other bugs
You can debug to understand if your plugin the state evolves as you expect and if you are triggering the proper actions using the flag ?debug=true ( I updated the doc right now with this new section)


--
Message has been deleted

Lorenzo Natali

unread,
Jun 9, 2017, 3:49:23 AM6/9/17
to mapstore-...@googlegroups.com
Follow the web/client/product as a model for your application:
plugins are registered in plugins.js and added to desktop, mobile and/or embedded section in localConfig.json. 
Then the standardApp includes the plugins. 
You add your plugins directly to the product/plugins.js and localConfig.json to try. 
See also http://dev.mapstore2.geo-solutions.it/mapstore/examples/plugins/ and try the "Live edit your plugin" functionality to see what a plugin should export


2017-06-08 12:06 GMT+02:00 'Ali Nasri' via mapstore-developers <mapstore-...@googlegroups.com>:
Hi,
   I checked again and everything is done on the right way but it doesn't show yet. If it possible could you please send me an example if you you added some new plugins before?

Thank you very much.

BestRegards

Ali Nasri
Message has been deleted

Ali Nasri

unread,
Jun 21, 2017, 7:47:19 AM6/21/17
to mapstore-developers
Hi Lorenzo, 
Thank you very much for your help, now i have my plugin..but it still just one problem, how can i separate the too plugins,because as i made a copy of "identify" plugin to create "infoBar", the two plugins works together when i click one of them.
So what i need to change to have two independent plugins?
This is the infobar.jsx file i made:
const React = require('react');
const { Panel, Glyphicon, Modal } = require('react-bootstrap');
const { findIndex } = require('lodash');

require('./css/Infobar.css');

const Draggable = require('react-draggable');

const MapInfoUtils = require('../../../utils/MapInfoUtils');
const Spinner = require('../../misc/spinners/BasicSpinner/BasicSpinner');
const Message = require('../../I18N/Message');
const DefaultViewer = require('./DefaultViewer');
const GeocodeViewer = require('./GeocodeViewer');
const Dialog = require('../../misc/Dialog');

const Infobar = React.createClass({
propTypes: {
enabled: React.PropTypes.bool,
draggable: React.PropTypes.bool,
collapsible: React.PropTypes.bool,
style: React.PropTypes.object,
point: React.PropTypes.object,
format: React.PropTypes.string,
map: React.PropTypes.object,
layers: React.PropTypes.array,
buffer: React.PropTypes.number,
requests: React.PropTypes.array,
responses: React.PropTypes.array,
viewerOptions: React.PropTypes.object,
viewer: React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.func]),
purgeResults: React.PropTypes.func,
noQueryableLayers: React.PropTypes.func,
clearWarning: React.PropTypes.func,
queryableLayersFilter: React.PropTypes.func,
buildRequest: React.PropTypes.func,
sendRequest: React.PropTypes.func,
localRequest: React.PropTypes.func,
showMarker: React.PropTypes.func,
hideMarker: React.PropTypes.func,
changeMousePointer: React.PropTypes.func,
maxItems: React.PropTypes.number,
excludeParams: React.PropTypes.array,
includeOptions: React.PropTypes.array,
showRevGeocode: React.PropTypes.func,
hideRevGeocode: React.PropTypes.func,
showModalReverse: React.PropTypes.bool,
reverseGeocodeData: React.PropTypes.object,
enableRevGeocode: React.PropTypes.bool,
wrapRevGeocode: React.PropTypes.bool,
panelClassName: React.PropTypes.string,
headerClassName: React.PropTypes.string,
bodyClassName: React.PropTypes.string,
asPanel: React.PropTypes.bool,
headerGlyph: React.PropTypes.string,
closeGlyph: React.PropTypes.string,
allowMultiselection: React.PropTypes.bool,
warning: React.PropTypes.string
},
getDefaultProps() {
return {
enabled: false,
draggable: true,
collapsible: false,
format: MapInfoUtils.getDefaultInfoFormatValue(),
requests: [],
responses: [],
buffer: 2,
viewerOptions: {},
viewer: DefaultViewer,
purgeResults: () => { },
buildRequest: MapInfoUtils.buildIdentifyRequest,
localRequest: () => { },
sendRequest: () => { },
showMarker: () => { },
hideMarker: () => { },
noQueryableLayers: () => { },
clearWarning: () => { },
changeMousePointer: () => { },
showRevGeocode: () => { },
hideRevGeocode: () => { },
containerProps: {
continuous: false
},
showModalReverse: false,
reverseGeocodeData: {},
enableRevGeocode: true,
wrapRevGeocode: false,
queryableLayersFilter: MapInfoUtils.defaultQueryableFilter,
style: {
position: "absolute",
maxWidth: "500px",
top: "56px",
left: "45px",
zIndex: 1023,
boxShadow: "2px 2px 4px #A7A7A7"
},
point: {},
map: {},
layers: [],
maxItems: 10,
excludeParams: ["SLD_BODY"],
includeOptions: [
"buffer",
"cql_filter",
"filter",
"propertyName"
],
panelClassName: "modal-dialog info-panel modal-content",
headerClassName: "modal-header",
bodyClassName: "modal-body info-wrap",
asPanel: false,
headerGlyph: "",
closeGlyph: "1-close",
className: "square-button",
allowMultiselection: false
};
},
componentWillReceiveProps(newProps) {
if (this.needsRefresh(newProps)) {
if (!newProps.point.modifiers || newProps.point.modifiers.ctrl !== true || !newProps.allowMultiselection) {
this.props.purgeResults();
}
const queryableLayers = newProps.layers.filter(newProps.queryableLayersFilter);
queryableLayers.forEach((layer) => {
const { url, request, metadata } = this.props.buildRequest(layer, newProps);
if (url) {
this.props.sendRequest(url, request, metadata, this.filterRequestParams(layer));
} else {
this.props.localRequest(layer, request, metadata);
}

});
if (queryableLayers.length === 0) {
this.props.noQueryableLayers();
} else {
this.props.showMarker();
}

}

if (newProps.enabled && !this.props.enabled) {
this.props.changeMousePointer('pointer');
} else if (!newProps.enabled && this.props.enabled) {
this.props.changeMousePointer('auto');
this.props.hideMarker();
this.props.purgeResults();
}
},
onModalHiding() {
this.props.hideMarker();
this.props.purgeResults();
},
renderHeader(missing) {
return (
<span role="header">
{(missing !== 0) ? <Spinner value={missing} sSize="sp-small" /> : null}
{this.props.headerGlyph ? <Glyphicon glyph={this.props.headerGlyph} /> : null}&nbsp;<Message msgId="identifyTitle" />
<button onClick={this.onModalHiding} className="close">{this.props.closeGlyph ? <Glyphicon glyph={this.props.closeGlyph} /> : <span>×</span>}</button>
</span>
);
},
renderResults(missingResponses) {
const Viewer = this.props.viewer;
return (<Viewer format={this.props.format} missingResponses={missingResponses} responses={this.props.responses} {...this.props.viewerOptions} />);
},
renderReverseGeocode(latlng) {
if (this.props.enableRevGeocode) {
let reverseGeocodeData = this.props.reverseGeocodeData;
const Viewer = (<GeocodeViewer
latlng={latlng}
showRevGeocode={this.props.showRevGeocode}
showModalReverse={this.props.showModalReverse}
identifyRevGeocodeModalTitle={<Message msgId="identifyRevGeocodeModalTitle" />}
revGeocodeDisplayName={reverseGeocodeData.error ? <Message msgId="identifyRevGeocodeError" /> : this.props.reverseGeocodeData.display_name}
hideRevGeocode={this.props.hideRevGeocode}
identifyRevGeocodeSubmitText={<Message msgId="identifyRevGeocodeSubmitText" />}
identifyRevGeocodeCloseText={<Message msgId="identifyRevGeocodeCloseText" />}
modalOptions={{ bsClass: 'mapstore-identify-modal modal' }} />);
return this.props.wrapRevGeocode ? (
<Panel
header={<span><Glyphicon glyph="globe" />&nbsp;<Message msgId="identifyRevGeocodeHeader" /></span>}>
{Viewer}
</Panel>
) : (<div id="mapstore-identify-revgeocoder">{Viewer}</div>);
}
return null;
},
renderContent() {
let missingResponses = this.props.requests.length - this.props.responses.length;
let latlng = this.props.point.latlng;
return this.props.asPanel ? (
<Panel
defaultExpanded={true}
style={this.props.style}

collapsible={this.props.collapsible}
id="mapstore-getfeatureinfo"
className={this.props.panelClassName}>
<div className={this.props.headerClassName ? this.props.headerClassName : "panel-heading"}>
{this.renderHeader(missingResponses)}
</div>
</Panel>
) : (
<Dialog id="mapstore-getfeatureinfo"
className={this.props.panelClassName}
headerClassName={this.props.headerClassName}
bodyClassName={this.props.bodyClassName}

><div role="body">
</iframe>
</div>
</Dialog>
); // );
},
render() {
if (this.props.enabled && this.props.requests.length !== 0) {
return this.props.draggable && this.props.asPanel ? (
<Draggable>
{this.renderContent()}
</Draggable>
) : this.renderContent();
}
if (this.props.warning) {
return (<Modal show={true} bsSize="small" onHide={() => {
this.props.clearWarning();
}}>
<Modal.Header className="dialog-error-header-side" closeButton>
<Modal.Title><Message msgId="warning" /></Modal.Title>
</Modal.Header>
<Modal.Body>
<div className="mapstore-error"><Message msgId="identifyNoQueryableLayers" /></div>
</Modal.Body>
<Modal.Footer>
</Modal.Footer>
</Modal>);
}
return null;
},
needsRefresh(props) {
if (props.enabled && props.point && props.point.pixel) {
if (!this.props.point.pixel || this.props.point.pixel.x !== props.point.pixel.x ||
this.props.point.pixel.y !== props.point.pixel.y) {
return true;
}
if (!this.props.point.pixel || props.point.pixel && this.props.format !== props.format) {
return true;
}
}
return false;
},
filterRequestParams(layer) {
let includeOpt = this.props.includeOptions || [];
let excludeList = this.props.excludeParams || [];
let options = Object.keys(layer).reduce((op, next) => {
if (next !== "params" && includeOpt.indexOf(next) !== -1) {
op[next] = layer[next];
} else if (next === "params" && excludeList.length > 0) {
let params = layer[next];
Object.keys(params).forEach((n) => {
if (findIndex(excludeList, (el) => { return (el === n); }) === -1) {
op[n] = params[n];
}
}, {});
}
return op;
}, {});
return options;
}
});

module.exports = Infobar;


Lorenzo Natali

unread,
Jun 21, 2017, 10:47:11 AM6/21/17
to mapstore-...@googlegroups.com
Hi Ali,
I don't know if you need all that stuff for your plugin. 

If you want to have both, you should have a separate reducer with different actions to modify it, because the state of that two plugins in that case will be indipendent.

The identify plugin uses thunks that is an old and, IMHO, difficult approch to manage async actions, and as far as I can see, you don't need anything async. 

If I understood well, you only need to show the dialog with the iframe when the button is pressed, right?
In this case I suggest a structured like this: 

About plugin connect enabled to the controlled part of the state

const selector = createSelector([
    (state) => state.controls && state.controls.infobar && state.controls.infobar.enabled,
        // (state) => ....get other parts of the state you want to use...
], (enabled) => ({ enabled
}));

const InfobarPlugin = connect(selector)(require('../components/data/infobar/Infobar'));
//....
module.exports = {
    InfobarPlugin: assign(InfobarPlugin, {
        Toolbar: {
            name: 'infobar',
            position: 6,
            tooltip: "info.tooltip",
            icon: <Glyphicon glyph="map-marker"/>,
            help: <Message msgId="helptexts.infoButton"/>,
            toggle: true
        }
    }),
    reducers: {mapInfo: require('../reducers/infobar')} // <-- only if needed
};

Toolbar container elements with toggle: true create automatically a button that automatically switch state.controls[name].enabled when clicked. In your case  state.controls.infobar.enabled

About your component (../components/data/infobar/Infobar), you can create a simple Dialog with the iframe inside. I created a simple one just a few hours ago: 

Very much easier, isn't it?

Than you can create your reducer and the actions to collect the specific data for your plugin (reducers/infobar) , if any. 



--
You received this message because you are subscribed to the Google Groups "mapstore-developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mapstore-developers+unsubscribe...@googlegroups.com.

To post to this group, send email to mapstore-developers@googlegroups.com.
Visit this group at https://groups.google.com/group/mapstore-developers.
For more options, visit https://groups.google.com/d/optout.



--

Regards,
Lorenzo Natali
==
GeoServer Professional Services from the experts! Visit http://goo.gl/it488V for more information.
==

Dott. Ing. Lorenzo Natali
Software Engineer

GeoSolutions S.A.S.
Via di Montramito 3/A
55054  Massarosa (LU)
Italy
fax:      +39 0584 1660272

Message has been deleted

Ali Nasri

unread,
Jun 23, 2017, 7:47:20 AM6/23/17
to mapstore-developers
Hi Lorenzo,

Thank you for the detailed explanation, that is exactly what i need.

Now i'm asking how can i enrich the URL of the iframe with content of the feature info response? As the plugin is a copy of identify I reckon that it already has all the logic to deliver the attributes oft he clicked feature. So i'd like to determine the url oft he iframe by the attributes oft he clicked feature.

Kind Regards.

Ali Nasri

Lorenzo Natali

unread,
Jun 26, 2017, 3:55:52 AM6/26/17
to mapstore-...@googlegroups.com
All the logic to build the request is here:

Following the new way we develop You have to capture a map click event on the map (maybe in an epic, when your tool is active) to create a request and trigger your personal action to put data in your state (using your reducer). Than you can use the response to create the URL. 
And this 2 presentations:


--
You received this message because you are subscribed to the Google Groups "mapstore-developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mapstore-developers+unsub...@googlegroups.com.

To post to this group, send email to mapstore-developers@googlegroups.com.
Visit this group at https://groups.google.com/group/mapstore-developers.
For more options, visit https://groups.google.com/d/optout.

Ali Nasri

unread,
Jul 3, 2017, 1:26:03 PM7/3/17
to mapstore-developers
Hi Lorenzo,

Could you please explain me how to get the URL of the iframe and deliver the attributes of the clicked feature, you already serve me a lot of information but i didn't get from where i should start.I will be very thankfull if you can provide me a step by step to enrich this step.

Kind Regards
Ali Nasri

Lorenzo Natali

unread,
Jul 4, 2017, 7:07:10 AM7/4/17
to mapstore-...@googlegroups.com
You should derivate your iframe URL from your reducers state connecting the props of the component with connect.
To update your reducer's state with the click event information you have to capture the click action to update the reducer's state.

One hint: 

You can run mapstore2 with ?debug=true option (I suggest you to install the redux dev tools extension for chrome). 
In the dev tools you can see the state of the redux store and the redux actions triggered on any event (with also the changes in the store state). 

Clicking on the map you can see CLICK_ON_MAP action is performed. 
Immagine incorporata 1
The action body, from the dev tool, looks like this: 
{
    type: 'CLICK_ON_MAP',
    point: {
        pixel: {
            x: 523,
            y: 340
        },
        latlng: {
            lat: 36.80928470205937,
            lng: -120.84960937499999
        },
        modifiers: {
            alt: false,
            ctrl: false,
            shift: false
        }
    }
}
There are typically 2 ways you can use the action:
1) You have all the information you need in the action (e.g the URL can be composed using lat/lng -> myurl.com/data/36.80/-120.84) --> simply capture the action CLICK_ON_MAP in your reducer  (typically a switch case) to change the state properly. This will reflect on your view
2) You need to do some processing to get the information you have from the action --> capture the action in an epic (they have to be included in your plugin) to start the async process to retrieve the information you need (via ajax calls, state inspection...) When you have the information to generate the iframe URL, trigger an action that properly update your reducer with the new information (see step 1). 

Example for case 1: 

  • your reducer should look like this: 
function infobar( state= {}, action) {
switch(action.type) {
    case CLICK_ON_MAP:
    return {
        ...state,
        point: action.point
    }
}
}
  • a sample selector: 
const selector = createSelector([
    (state) => state.controls && state.controls.infobar && state.controls.infobar.enabled,
    (state) => state.infobar && state.infobar.point
], (enabled, point) => ({
    enabled,point
})); 
  • Then your plugin
module.exports = {
    InfobarPlugin: assign(InfobarPlugin, {
        Toolbar: {
            name: 'infobar',
            position: 6,
            tooltip: "info.tooltip",
            icon: <Glyphicon glyph="map-marker"/>,
            help: <Message msgId="helptexts.infoButton"/>,
            toggle: true
        }
    }),
    reducers: {infobar: require('../reducers/infobar')} // <-- only if needed
};
Then in your component (InfoBarPlugin) you will have the point object as property to use to compose the URL.
//...
render() {
    const url = composeURL(point) <-- here you create the string from the object
    ...
    return <iframe url={url}
}
//...

Example for case 2: 
You could capture the click event in an epic to do some stuff and then update your plugin's state, this time directly with the URL.

  • your epic will look like this: 
infoBarClickEpic = (action$) = action$.ofType(CLICK_ON_MAP).switchMap(
   action => Rx.Observable.fromPromise(axios.get(.....)).map( response => {
   return {
      type:  IFRAME_URL_UPDATE,
      url: response.url
    }
}))
  • your reducer should look like this: 
function infobar( state= {}, action) {
switch(action.type) {
    case IFRAME_URL_UPDATE:
    return {
        ...state,
        url: action.url
    }
}
}
  • a sample selector: 
const selector = createSelector([
    (state) => state.controls && state.controls.infobar && state.controls.infobar.enabled,
    (state) => state.infobar && state.infobar.url
], (enabled, url) => ({
enabled,url
})); 
  • Then the plugin
module.exports = {
    InfobarPlugin: assign(InfobarPlugin, {
        Toolbar: {
            name: 'infobar',
            position: 6,
            tooltip: "info.tooltip",
            icon: <Glyphicon glyph="map-marker"/>,
            help: <Message msgId="helptexts.infoButton"/>,
            toggle: true
        }
    }),
epics: infoBarClickEpic
    reducers: {infobar: require('../reducers/infobar')} // <-- only if needed
}; 
Then in your component you will have the URL property to use.
//...
render() {
    ...
    return <iframe url={this.props.url}
}
//...



Of course I didn't tried this code. Is just a sample :). 

Hope it helps


--
Message has been deleted

Ali Nasri

unread,
Jul 10, 2017, 11:13:58 AM7/10/17
to mapstore-developers
Hi Lorenzo,

Sorry to be late for my feedback, i tried the first case code and i'm facing this error:ReferenceErrorcomposeURL is not defined 
In my code i trying to render the url from renderContent() method as it shows the <iframe> is located there and then it will be called in render() method to be displayed, can you please help me to fix this bug?Thank you in advance. About the second case i couldn't find where should i inject the "epic" code,i need the file path from MapStore2 project :(
Ali
renderContent() {
const url = composeURL(point);

let missingResponses = this.props.requests.length - this.props.responses.length;
let latlng = this.props.point.latlng;
return this.props.asPanel ? (
<Panel
defaultExpanded={true}
style={this.props.style}

collapsible={this.props.collapsible}
id="mapstore-getfeatureinfo"
className={this.props.panelClassName}>
<div className={this.props.headerClassName ? this.props.headerClassName : "panel-heading"}>
{this.renderHeader(missingResponses)}
</div>
</Panel>
) : (
<Dialog id="mapstore-getfeatureinfo"
className={this.props.panelClassName}
headerClassName={this.props.headerClassName}
bodyClassName={this.props.bodyClassName}

>
{this.renderHeader(missingResponses)}
<div role="body">
<iframe url={url} width= "800px" height= "600px">

Lorenzo Natali

unread,
Jul 10, 2017, 11:23:12 AM7/10/17
to mapstore-...@googlegroups.com
About the red error:
composeURL implementation is up to you. It is the function that transforms the info from the map into the URL you want to define. It depends on what you need to do to create the URL. 
The redbox error typically appears when some properties you are using are not defined in the component, maybe because it is missing or because you need a default for it. 

About the epic, it is injected by the plugin see the line: 
epics: infoBarClickEpic
in the sample code I wrote in the previous email. I don't know if you have to do some ajax calls or not, the case 2 is if only you need to do an ajax call to retrieve information to compose the URL.



--

Ali Nasri

unread,
Jul 11, 2017, 10:45:37 AM7/11/17
to mapstore-developers
Hi,
 
Thank you again for the quick reply. It works with static information now, i wich if you can give me a sample of a "composeURL" function to make it right. 

Regards
Ali 

Lorenzo Natali

unread,
Jul 12, 2017, 3:42:54 AM7/12/17
to mapstore-...@googlegroups.com
I don't know how it is useful for you, because is your own application that must do it. 

anyway, following the example i provided, if you want to generate an url like http://myurl.com/data/<longitude>/<latitude>. your compose url should look like:

Witch kind of URL do you have to generate?


--
Message has been deleted
Message has been deleted

Ali Nasri

unread,
Jul 12, 2017, 1:21:39 PM7/12/17
to mapstore-developers

I need to pass the feature from the getfeatureinfo-response as a Parameter to composeURL Method: composeURL: function(feature) {
    return URL + 'NodeGraphics.aspx?ObjectId=' + feature.ID + '&ObjectType=' + feature.OBJECTTYPE, 
   }

A click/point action is needed to retrieve wms getfeatureinfo, which is available in the existing Feature Info plugin, but instead of just displaying the result, i should parse the result and use object related information to populate the url.
In my example that would be mapping of Objecttype=10000 to Layoutname AbwHaltung. I.e. if objecttype=10000, and Id =foo_bar, the mapping would be .../Web/Arena/WAC/Dataform/AbwHaltung/foo_bar

that means that i need to manage and retrieve the attribute ID from the wms getfeatureinfo response and paste it to the url of "http://basysweb.barthauer.com/BaSYSWeb/Web/Arena/WAC/DataForm/AbwHaltung/" . The result should be, that clicking an object in a Map based on my sample WMS will open the Object-Dataform for this specific object. 

Regards 
Ali 

Lorenzo Natali

unread,
Jul 14, 2017, 5:03:30 AM7/14/17
to mapstore-...@googlegroups.com
If I understood well, you need some data from the WMS getFeatureInfo response.
So you need an epic. 

You can use the utility buildRequst in web/client/mapinfo/wms to create the getFeatureinfo request parameters (or create it on your own) from the clicked point. 
Unfortunatly the GetFeatureInfo stuff has not been converted  in the RxJS way yet so probably this utility function is a little strange in terms of params.

You have to pass the layer (get it from the state)as the first parameter and a props object as second parameter that contains the map (from the state) and the clicked point.

I didn't tried this code but you can start from this draft to do your own and debug. 

//  NOTE getLayerFromId and mapSelector are from web/client/selectors/layers and web/client/selectors/map
const createGetFeatureInfoRequest = (state, action) => {
const layer = getLayerFromId(state ,id); // find the layer from the application state. if you assigned one id to it you can use getLayerFromId, otherwise you can use layersSelector and then find it with filter const props = { map: mapSelector(state), point: action.point };
  const {url, request} = buildRequest(layer, props);
return axios.get(url,request);
} const composeIframeURL = (response) => { // TODO get your information from the response.data (json) and retrn your URL. you should find the information in the features array } infoBarClickEpic = (action$, store) = action$.ofType(CLICK_ON_MAP).switchMap( action => Rx.Observable.fromPromise(createGetFeatureInfoRequest(store.getState(), action)).map( response => { return { type: IFRAME_URL_UPDATE, url: composeIframeURL(response) } }))



--
Reply all
Reply to author
Forward
Message has been deleted
Message has been deleted
0 new messages