Giter Club home page Giter Club logo

Comments (42)

DavidSpriggs avatar DavidSpriggs commented on June 12, 2024

This would be best as a widget. Do you want to do a 'find' where you search for a keyword across a bunch of layers or are you wanting to actually build a sql query?

from cmv-app.

tr3vorm avatar tr3vorm commented on June 12, 2024

I looked back through my code and I have always used the QueryTask - I have no idea why as it looks like the FindTask meets my requirements and is much simpler! The only potential problem I see with FindTask is if I want to search fields with numeric or date data types, but for now FindTask looks like it would work perfectly.

from cmv-app.

johnlucotch avatar johnlucotch commented on June 12, 2024

I first would like to thank you for your work on this viewer. I would also like to implement a find widget in my viewer.

from cmv-app.

tmcgee avatar tmcgee commented on June 12, 2024

I am working on a configurable search widget. Here's what appears to be the latest approach from ESRI for 10.2: https://github.com/Esri/configurable-place-finder (though still using version 3.5 of the JS api). Variations on this theme can be found in other ESRI GitHub repositories and at http://tryitlive.arcgis.com/ - different approaches to the UI are used. All of those examples that I have looked at use the QueryTask. That's where I'll be starting from. I'll share as I progress. If others are a step ahead of me and willing to share their efforts, please do. ;)

from cmv-app.

tr3vorm avatar tr3vorm commented on June 12, 2024

I have a very basic search working using FindTask (I just need to sort out zoom extents of found features when there is a single point feature found).

The downside of FindTask is that I can only search one map service at a time, although I am sure I could make the widget provide a checklist for maps to be searched. I will gladly share my code if that might help. I am not very familiar with github, so probably best I just paste code here?

from cmv-app.

tr3vorm avatar tr3vorm commented on June 12, 2024

config.js (note the output SRID)

            find: {
                include: true,
                title: "Find",
                open: true,
                position: 8,
serviceURL: "http://server:6080/arcgis/rest/services/BTWInternal/Oil_and_Gas/MapServer",
                layerIds: [1, 5], 
                searchFields: ["WellName", "WellsiteName"],
                outputSpatialReference: 4326
            },

controller.js

        find: function (widgetConfig, position) {
            var findTP = this._createTitlePane(widgetConfig.title, position, widgetConfig.open); // title pane

            require(['gis/dijit/Find'], lang.hitch(this, function (Find) {
                this.find = new Find({
                    map: this.map,
                    serviceURL: widgetConfig.serviceURL,
                    outputSpatialReference: widgetConfig.outputSpatialReference,
                    layerIds: widgetConfig.layerIds,
                    searchFields: widgetConfig.searchFields
                }, domConstruct.create("div")).placeAt(findTP.containerNode);
                this.find.startup();
            }));
        }

Find.js (extents is work in progress...)

define([
    "dojo/_base/declare",
    "dijit/_WidgetBase",
    "dijit/_TemplatedMixin",
    "dojo/dom",
    "dojo/_base/lang",
    "dojo/_base/Color",
    "esri/layers/GraphicsLayer",
    "esri/graphic",
    "esri/renderers/SimpleRenderer",
    "esri/symbols/SimpleMarkerSymbol",
    "esri/symbols/SimpleLineSymbol",
    "esri/symbols/SimpleFillSymbol",
    "esri/layers/FeatureLayer",
    "esri/graphicsUtils",
    "esri/tasks/FindTask",
    "esri/tasks/FindParameters",
    'dojo/text!./Find/templates/Find.html'
], function (declare, _WidgetBase, _TemplatedMixin, dom, lang, Color, GraphicsLayer, Graphic, SimpleRenderer, SimpleMarkerSymbol, SimpleLineSymbol, SimpleFillSymbol, FeatureLayer, graphicsUtils, FindTask, FindParameters, template) {

    return declare([_WidgetBase, _TemplatedMixin], {
        templateString: template,
        postCreate: function () {
            this.inherited(arguments);

            this.pointSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_CIRCLE, 10, new SimpleLineSymbol(SimpleLineSymbol.STYLE_NONE, new Color([255, 255, 0]), 1), new Color([255, 255, 0, 2.0]));
            this.polylineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 255, 0]), 2);
            this.polygonSymbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_NONE, new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 255, 0]), 2), new Color([255, 255, 0, 1.0]));

            this.pointGraphics = new GraphicsLayer({
                id: "findGraphics_point",
                title: "Find"
            });
            this.pointRenderer = new SimpleRenderer(this.pointSymbol);
            this.pointRenderer.label = "User drawn points";
            this.pointRenderer.description = "User drawn points";
            this.pointGraphics.setRenderer(this.pointRenderer);

            // poly line 
            this.polylineGraphics = new GraphicsLayer({
                id: "findGraphics_line",
                title: "Find Graphics"
            });
            this.polylineRenderer = new SimpleRenderer(this.polylineSymbol);
            this.polylineRenderer.label = "User drawn lines";
            this.polylineRenderer.description = "User drawn lines";
            this.polylineGraphics.setRenderer(this.polylineRenderer);

            // polygons
            this.polygonGraphics = new FeatureLayer({
                layerDefinition: {
                    geometryType: "esriGeometryPolygon",
                    fields: [{
                        name: "OBJECTID",
                        type: "esriFieldTypeOID",
                        alias: "OBJECTID",
                        domain: null,
                        editable: false,
                        nullable: false
                    }, {
                        name: "ren",
                        type: "esriFieldTypeInteger",
                        alias: "ren",
                        domain: null,
                        editable: true,
                        nullable: false
                    }]
                },
                featureSet: null
            }, {
                id: "findGraphics_poly",
                title: "Find Graphics",
                mode: FeatureLayer.MODE_SNAPSHOT
            });
            this.polygonRenderer = new SimpleRenderer(this.polygonSymbol);
            this.polygonRenderer.label = "User drawn polygons";
            this.polygonRenderer.description = "User drawn polygons";
            this.polygonGraphics.setRenderer(this.polygonRenderer);

            this.map.addLayer(this.polygonGraphics);
            this.map.addLayer(this.polylineGraphics);
            this.map.addLayer(this.pointGraphics);
        },
        search: function () {
            findTask = new esri.tasks.FindTask(this.serviceURL);

            //create find parameters and define known values
            var findParams = new esri.tasks.FindParameters();
            findParams.returnGeometry = true;
            findParams.layerIds = this.layerIds;
            findParams.searchFields = this.searchFields; 

            findParams.searchText = dom.byId("searchText").value;

            findParams.outSpatialReference = {
                wkid: this.outputSpatialReference
            };

            findTask.execute(findParams, lang.hitch(this, 'showResults'));
        },
        showResults: function (results) {
            var counter = 0;
            this.pointGraphics.clear();
            this.polylineGraphics.clear();
            this.polygonGraphics.clear();

            dojo.forEach(results, function (result) {
                var graphic;
                counter++;
                switch (result.feature.geometry.type) {
                    case "point":
                        graphic = new Graphic(result.feature.geometry);
                        this.pointGraphics.add(graphic);
                        //                        alert('add: '+graphic.geometry.x +', '+ graphic.geometry.y);
                        break;
                    case "polyline":
                        graphic = new Graphic(result.feature.geometry);
                        this.polylineGraphics.add(graphic);
                        break;
                    case "polygon":
                        graphic = new Graphic(result.feature.geometry, null, {
                            ren: 1
                        });
                        this.polygonGraphics.add(graphic);
                        break;
                    default:
                }
            }, this);

            if (counter == 0) {
                alert("No matches found");
            } else {
                // zoom to layer extents!
                var zoomExtent = null; 
                //If the layer is a single point then extents are null
                // if there are no features in the layer then extents are null
                // the result of union() to null extents is null

                if (this.pointGraphics.graphics.length > 0) {
                    zoomExtent = graphicsUtils.graphicsExtent(this.pointGraphics.graphics);
                }
                if (this.polylineGraphics.graphics.length > 0) {
                    zoomExtent = zoomExtent.union(graphicsUtils.graphicsExtent(this.polylineGraphics.graphics));
                    //                    zoomExtent = (graphicsUtils.graphicsExtent(this.polylineGraphics.graphics));
                }
                if (this.polygonGraphics.graphics.length > 0) {
                    zoomExtent = zoomExtent.union(graphicsUtils.graphicsExtent(this.polygonGraphics.graphics));
                }

                if (zoomExtent === null) {
                    alert("Error: Zoom extents are null");
                } else {
                    this.map.setExtent(zoomExtent.expand(1.2));
                }
            }
        },
        clearResults: function () {
            this.pointGraphics.clear();
            this.polylineGraphics.clear();
            this.polygonGraphics.clear();

            dom.byId("searchText").value = "";
        }
    });
});

/templates/Find.html

<div data-dojo-type="Find" >
    <button data-dojo-type="dijit.form.Button" data-dojo-attach-event="onClick:search" >
        Search
    </button>
    <input id="searchText" type="text" data-dojo-type="dijit.form.ValidationTextBox" placeholder="Enter search text" />

    <button data-dojo-type="dijit.form.Button" data-dojo-props="iconClass:'clearIcon',showLabel:true"
            data-dojo-attach-event="onClick:clearResults">
        Clear Results
    </button>
</div>

from cmv-app.

tmcgee avatar tmcgee commented on June 12, 2024

@tr3vorm Thanks! I can see where you're going with this. My basic requirements will likely include multiple searches (dropdown list of searches?) and tabular list of results (attributes). You've given me something to start from. much appreciated.

from cmv-app.

tr3vorm avatar tr3vorm commented on June 12, 2024

@timcgee I updated zoom extents... (I am going to learn github next!)
Replace showResults with the following code

        showResults: function (results) {
            var counter = 0;
            this.pointGraphics.clear();
            this.polylineGraphics.clear();
            this.polygonGraphics.clear();

            dojo.forEach(results, function (result) {
                var graphic;
                counter++;
                switch (result.feature.geometry.type) {
                    case "point":
                        graphic = new Graphic(result.feature.geometry);
                        this.pointGraphics.add(graphic);
                        //                        alert('add: '+graphic.geometry.x +', '+ graphic.geometry.y);
                        break;
                    case "polyline":
                        graphic = new Graphic(result.feature.geometry);
                        this.polylineGraphics.add(graphic);
                        break;
                    case "polygon":
                        graphic = new Graphic(result.feature.geometry, null, {
                            ren: 1
                        });
                        this.polygonGraphics.add(graphic);
                        break;
                    default:
                }
            }, this);

            if (counter == 0) {
                alert("No matches found");
            } else {
                // zoom to layer extents!
                var zoomExtent = null;
                //If the layer is a single point then extents are null
                // if there are no features in the layer then extents are null
                // the result of union() to null extents is null

                if (this.pointGraphics.graphics.length > 0) {
                    zoomExtent = this.getPointExtent(this.pointGraphics.graphics);
                }
                if (this.polylineGraphics.graphics.length > 0) {
                    if (zoomExtent === null)
                        zoomExtent = graphicsUtils.graphicsExtent(this.polylineGraphics.graphics);
                    else
                        zoomExtent = zoomExtent.union(graphicsUtils.graphicsExtent(this.polylineGraphics.graphics));
                }
                if (this.polygonGraphics.graphics.length > 0) {
                    if (zoomExtent === null)
                        zoomExtent = graphicsUtils.graphicsExtent(this.polygonGraphics.graphics);
                    else
                        zoomExtent = zoomExtent.union(graphicsUtils.graphicsExtent(this.polygonGraphics.graphics));
                }

                this.map.setExtent(zoomExtent.expand(1.2));
            }
        },

And add the following getPointExtent() function...

        getPointExtent: function (pointFeatures) {
            var extent = graphicsUtils.graphicsExtent(pointFeatures);
            var factor = 0.0001; // hack: 0.0001 of a degree

            if (extent == null && pointFeatures.length > 0) {
                var pt = pointFeatures[0].geometry;
                extent = new esri.geometry.Extent(pt.x - factor, pt.y - factor, pt.x + factor, pt.y + factor, pt.SpatialReference);
            }
            return extent;
        }

from cmv-app.

tmcgee avatar tmcgee commented on June 12, 2024

@tr3vorm,

The getPointExtent function is incomplete. I get the gist though - it needs to return the extent.

thanks again.

return extent;

from cmv-app.

tr3vorm avatar tr3vorm commented on June 12, 2024

Thanks - cut/paste error. I have edited my comment appropriately.

from cmv-app.

rogers259 avatar rogers259 commented on June 12, 2024

@tr3vorm,

Where exactly are you inserting the getPointExtent function? Thanks!

from cmv-app.

tmcgee avatar tmcgee commented on June 12, 2024

@rogers259 the following lines are In the second version of "showResults" function that @tr3vorm posted above:

if (this.pointGraphics.graphics.length > 0) {
    zoomExtent = this.getPointExtent(this.pointGraphics.graphics);
}

from cmv-app.

rogers259 avatar rogers259 commented on June 12, 2024

@tmcgee
Thanks, But what i'm finding is if a single record is returned it zooms off to never never land. However if 2 or more results are returned everything functions and zooms correctly. When a single item is returned it is highlighting that one item (you need to zoom all the way out to see it but it does highlight).

Any ideas for correcting so that when single results are returned getting it to zoom to that one item? Thanks

from cmv-app.

tmcgee avatar tmcgee commented on June 12, 2024

first a caveat: I've used this code for polygons, not points so I have no direct experience using the getPointExtent function.

my first thought is "zooms off to never never land" sounds possibly like a projection/spatial reference mismatch.

You should first your "outputSpatialReference" in the config for the find widget. The features returned from the FIndTask use that wkid.

Then check to see whether the if statement In the getPointExtent function:

if (extent == null && pointFeatures.length > 0) {
    var pt = pointFeatures[0].geometry;
    extent = new esri.geometry.Extent(pt.x - factor, pt.y - factor, pt.x + factor, pt.y + factor, pt.SpatialReference);
}

is triggered. I believe it would be with just a single point. If so, the code constructs the extent from the X/Y/SpatialReference from first point in the "pointFeatures" array. If you have a spatial reference mismatch, it may be putting the returned extent somewhere out in never never land.

from cmv-app.

tr3vorm avatar tr3vorm commented on June 12, 2024

It works fine for polygons too. I had trouble at first trying to get the found features drawn on my map in the correct projection, until I set outputSpatialReference. Try zooming out to see if the found features are being drawn elsewhere (after turning off scaling for the searched layer in the map service if necessary).

from cmv-app.

rogers259 avatar rogers259 commented on June 12, 2024

@tr3vorm
Single Features returned from the search zoom to never never land, but when you hit the home button the returned single item that was searched is selected and in the right location. Maybe I've got something incorrect in code. Except for this one thing it works perfect..as long as it doesnt return a single result. And outputSpatialReference for that layer is set 3857 same as service.

config.js

            find: {
                include: true,
                title: "Search",
                open: false,
                position: 2,
                serviceURL: "...Map_Service",
                layerIds: [0, 1],
                searchFields: ["Sample", "Sample"],
                outputSpatialReference: 3857
            },

Controller.js

        find: function (widgetConfig, position) {
            var findTP = this._createTitlePane(widgetConfig.title, position, widgetConfig.open);
            require(['gis/dijit/Find'], lang.hitch(this, function (Find) {
                this.find = new Find({
                    map: this.map,
                    serviceURL: widgetConfig.serviceURL,
                    outputSpatialReference: widgetConfig.outputSpatialReference,
                    layerIds: widgetConfig.layerIds,
                    searchFields: widgetConfig.searchFields
                }, domConstruct.create("div")).placeAt(findTP.containerNode);
                this.find.startup();
            }));
        },

Find.js

        search: function () {
            findTask = new esri.tasks.FindTask(this.serviceURL);

            //create find parameters and define known values
            var findParams = new esri.tasks.FindParameters();
            findParams.returnGeometry = true;
            findParams.layerIds = this.layerIds;
            findParams.searchFields = this.searchFields;

            findParams.searchText = dom.byId("searchText").value;

            findParams.outSpatialReference = {
                wkid: this.outputSpatialReference
            };

            findTask.execute(findParams, lang.hitch(this, 'showResults'));
        },        
        showResults: function (results) {
            var counter = 0;
            this.pointGraphics.clear();
            this.polylineGraphics.clear();
            this.polygonGraphics.clear();

            dojo.forEach(results, function (result) {
                var graphic;
                counter++;
                switch (result.feature.geometry.type) {
                    case "point":
                        graphic = new Graphic(result.feature.geometry);
                        this.pointGraphics.add(graphic);
                        //                        alert('add: '+graphic.geometry.x +', '+ graphic.geometry.y);
                        break;
                    case "polyline":
                        graphic = new Graphic(result.feature.geometry);
                        this.polylineGraphics.add(graphic);
                        break;
                    case "polygon":
                        graphic = new Graphic(result.feature.geometry, null, {
                            ren: 1
                        });
                        this.polygonGraphics.add(graphic);
                        break;
                    default:
                }
            }, this);

            if (counter == 0) {
                alert("No matches found");
            } else {
                // zoom to layer extents!
                var zoomExtent = null;
                //If the layer is a single point then extents are null
                // if there are no features in the layer then extents are null
                // the result of union() to null extents is null
                if (this.pointGraphics.graphics.length > 0) {
                    zoomExtent = this.getPointExtent(this.pointGraphics.graphics);
                }
                if (this.polylineGraphics.graphics.length > 0) {
                    if (zoomExtent === null)
                        zoomExtent = graphicsUtils.graphicsExtent(this.polylineGraphics.graphics);
                    else
                        zoomExtent = zoomExtent.union(graphicsUtils.graphicsExtent(this.polylineGraphics.graphics));
                }
                if (this.polygonGraphics.graphics.length > 0) {
                    if (zoomExtent === null)
                        zoomExtent = graphicsUtils.graphicsExtent(this.polygonGraphics.graphics);
                    else
                        zoomExtent = zoomExtent.union(graphicsUtils.graphicsExtent(this.polygonGraphics.graphics));
                }

                this.map.setExtent(zoomExtent.expand(1.2));
            }
        },
        clearResults: function () {
            this.pointGraphics.clear();
            this.polylineGraphics.clear();
            this.polygonGraphics.clear();

            dom.byId("searchText").value = "";
        },
        getPointExtent: function (pointFeatures) {
            var extent = graphicsUtils.graphicsExtent(pointFeatures);
            var factor = 0.0001; // hack: 0.0001 of a degree

            if (extent == null && pointFeatures.length > 0) {
                var pt = pointFeatures[0].geometry;
                extent = new esri.geometry.Extent(pt.x - factor, pt.y - factor, pt.x + factor, pt.y + factor, pt.SpatialReference);
            }
            return extent;
        }
    });

from cmv-app.

tr3vorm avatar tr3vorm commented on June 12, 2024

My only thought is that you need to set factor to a larger number e.g. try var factor = 100.0; as I suspect your projection is in metres, or some unit other than degrees as in my example.

from cmv-app.

tmcgee avatar tmcgee commented on June 12, 2024

3857 is definitely in meters.

1/1000 of meter is a pretty small extent. :)

@rogers259, If you use:

outputSpatialReference: 4326

it might work without modification. Using 4326 lets the server do the "heavy lifting" of converting the point coordinates from 3857 to 4326 (lat/lng)

from cmv-app.

rogers259 avatar rogers259 commented on June 12, 2024

Changing to 4326 fixed the issue. facepalm

Thanks Tim!!

from cmv-app.

tmcgee avatar tmcgee commented on June 12, 2024

I've done many a facepalm myself! Glad it worked for you.

from cmv-app.

DavidSpriggs avatar DavidSpriggs commented on June 12, 2024

I would also look at extent.expand() expand(1) will keep it the same size expand(1.5) will make it 50% bigger and expand(0.5) will make it 50% smaller.

from cmv-app.

tr3vorm avatar tr3vorm commented on June 12, 2024

I would like to get rid of factor but the whole problem stems from the extents of a layer with single point == null as x1==x2, and y1==y2. extent.expand() does not work in this case as the sides of the extent are always length == 0 and the extent remains unchanged regardless of the value of the expand() parameter.

from cmv-app.

gobsmack avatar gobsmack commented on June 12, 2024

I'm really glad to see the ongoing work on this, because I need this widget, too. Here's a suggestion, maybe a little crazy.

After you get the FIndResult, rather than calling drawing the function on the map, what if you were to do something like Topic/Publish?
http://dojotoolkit.org/reference-guide/1.9/dojo/topic.html
It sets up a sort of event bus for notifications. You can see a good example of this in the interaction between the Growler widget and the LocateButton widget.

topic.publish('growler/growl', {
   title: 'GPS Position',
   message: lang.replace(this.growlTemplate, stats),
   level: 'default', //can be: 'default', 'warning', 'info', 'error', 'success'.
   timeout: 10000, //set to 0 for no timeout
   opacity: 1.0
});

But, in this case, you'd publish the FindResult array, and maybe use the growler to announce any error.

findTask.execute(findParams, 
   lang.hitch(this, function(results) {
      topic.publish('finder/complete', {
         results: results
      });
   }),
   lang.hitch(this, function(err) {
      topic.publish('growler/growl', {
         title: 'Find Error',
         message: err,
         level: 'error',
         timeout: 10000,
         opacity: 1.0
      });
   })
);

That would leave the handling of the results to whomever subscribes to the result. So, if you want to draw on the map, great. If I want to show results in a grid, then great.

I haven't thought through what kind of widget would actually listen to that event, though. I'm afraid it would require a FindResultGrid widget, maybe that uses David's new FloatingWidget mixin. It's starting to sound complicated. But, it would really separate from of the "finding" work from the "resulting" work.

from cmv-app.

tmcgee avatar tmcgee commented on June 12, 2024

@gobsmack I like the publish/subscribe idea.

As far as a result grid, I have one working in the sidebar - not as a separate widget but as part of the "Find" widget. I would like to consider a floating widget for this and other tasks so I look forward to David's next iteration. I have a few remaining issues before I push the Find Widget to a public repo. I am checking with my client to verify before I post a screenshot of the WIP.

from cmv-app.

rogers259 avatar rogers259 commented on June 12, 2024

@tmcgee
Ultimately headed in the same direction with this as you guys. I think with the FloatingWidget idea would offer the most options. Two things that i think would be good options with this results grid 1. Need an option within config to turn off floatingwidget when in mobile view. I can see that getting too much for a mobile user and what the find widget does now is perfect for mobile...keep it simple. & 2. option for user to Export results to .csv or excel.

Also and this is off topic... clip and ship widget. Can we offer that with results? I really think this should be a separate widget put together for clip & ship. But I can see this being something users want.

from cmv-app.

gobsmack avatar gobsmack commented on June 12, 2024

@tr3vorm @tmcgee
I've extended your idea a little bit. The user may have a choice of which fields to search. Rather than having more than one widget in the side panel, lets optionally populate a dropdown with the different searchable fields. Config looks like this:

find: {
    include: true,
    title: 'Address Search',
    open: false,
    position: 2,
    items: [
        {
            title: 'Name',
            placeholder: 'Enter a First Name or Last Name',
            serviceURL: "http://someserviceurl",
            layerIds: [0],
            searchFields: ['FirstName', 'LastName']
        },
        {
            title: 'City',
            placeholder: 'Enter a City',
            serviceURL: "http://someserviceurl",
            layerIds: [2],
            searchFields: ['City_Name']
        }
    ]
},

The in Find.js you get something like this:

postCreate: function () {
    this.inherited(arguments);
    if (this.items.length == 1) {
        this.selectedIndex = 0;
        domConstruct.place('<label>Search By ' + this.items[0].title + ':</label>', this.searchFieldContainer);
        this.searchTextBox.set('placeHolder', this.items[0].placeholder);
    }
    else if (this.items.length > 1) {
        domConstruct.place('<label>Search By:</label>', this.searchFieldContainer);
        var select = new Select();
        array.forEach(this.items, function (item, index) {
            select.addOption({ disabled: false, label: item.title, selected: false, value: index });
        }, this);
        select.placeAt(this.searchFieldContainer);
        select.on('change', lang.hitch(this, function () {
            this.selectedIndex = select.get('value');
            this.searchTextBox.set('placeHolder', this.items[this.selectedIndex].placeholder);
        }));
        select.set('value', 0);
    }
},

And, the template just includes a placeholder for the label or the dropdown:

<div>
    <div data-dojo-attach-point="searchFieldContainer"></div>
    <input type="text" data-dojo-type="dijit/form/ValidationTextBox" placeholder="Enter search text" data-dojo-attach-point="searchTextBox" />
    <button data-dojo-type="dijit/form/Button" data-dojo-attach-event="onClick:search" >Search</button>
</div>

I still have to add some validation, ajax suggest, and a "loading" spinner in the text box.

from cmv-app.

tmcgee avatar tmcgee commented on June 12, 2024

@gobsmack: I have that working in a similar fashion in my Find Widget. I really need to get the code into a public repo. In the meantime, this screenshot showing the drop down and one possible result grid option will hopefully get the idea across:

image

from cmv-app.

tmcgee avatar tmcgee commented on June 12, 2024

And your config is similar to the one I posted earlier in issue #65:

find: {
  id: 'sidebar_Find',
  include: true,
  title: 'Search',
  open: true,
  position: 0,
  queries: [
      {
          id: 0,
          name: 'Find A Map By Name',
          url: 'http://server/arcgis/rest/services/Parcelviewer/Flatfiles_AGS/MapServer',
          textSearchLabel: 'Name',
          textSearchHint: 'Enter the first few characters of the map\'s name',
          layerIds: [0,1,2,3],
          searchFields: ['NAME', 'PUDNAME', 'MAP_TITLE_']
      },
      {
          id: 1,
          name: 'Find A Parcel By APN',
          url: 'http://server/arcgis/rest/services/BaseMaps/Basemap_WM/MapServer',
          textSearchLabel: 'APN',
          textSearchHint: 'Enter the first few characters of the APN',
          layerIds: [9],
          searchFields: ['APN'],
          minChars: 5,
          hideGrid: true
      }
  ],
  outputSpatialReference: 4326,
  symbols: {
      point: { // creates a circle
          size: 10,
          lineColor: [0, 255, 255],
          lineWidth: 3,
          fillColor: [0, 255, 255, 1.0]
      },
      line: {
          color: [0, 255, 255],
          width: 3
      },
      polygon: {
          lineColor: [0, 255, 255],
          lineWidth: 4,
          fillColor: [255, 255, 255, 0]
      }
  }
},

from cmv-app.

friendde avatar friendde commented on June 12, 2024

I am also building a Find widget. I like the findTask over the queryTask because findTask allows for many services with many fields.

However, I disagree with the use of a drop-down pick list. The user should simply enter keywords in a search box and let the config file define the search within the services and field names. Results can appear in a content pane below the map (concealable like the sidebar). Tabs within the pane will hold results for each service. Field name headings in the datagrid should be driven by the Find widget's config file.

Before joining Github I sent a few emails to @DavidSpriggs asking for validation for my approach. He stated that using the Identify Widget, as he built in this project, would be a good way to proceed. I looked at the API and the two Tasks are nearly identical. So as a newbie I am struggling but I think I am making progress. This project is a great way to learn JS.

In the config file, js/Viewer/config.js, the following snippet returns results on the last web service in the list and does not seem to search the two services preceeding it. By the way I added the find code that @tr3vorm and @rogers259 posted above (thank you, very useful) which is a modified snippet below.

        find: {
            include: true,
            title: "Find",
            open: true,
            position: 8,
            serviceURL: ("http://ServerName/arcgis/rest/services/Gas/MapServer","http://ServerName/arcgis/rest/services/Electric/MapServer","http://ServerName/arcgis/rest/services/Parcel/MapServer"),
            layerIds: [23,1,10,12],
            searchFields: ["FACILITYID","NAME","TaxParcelID"],
            outputSpatialReference: 4326
        }

I am trying to use this project's configurable-widget model and create a Find widget modeled after the Identify widget as David Spriggs suggested.

Our organization has several utilities we map. It would be great for someone to simply enter a valve number or pole number or man-hole id number in the search box. And any feature found, as defined in the widget's config file, will appear in a datagrid with tabs representing each map service.

I'll keep watch here and thanks to all for your contributions. if my limited skills can positively contribute I will post my success.

from cmv-app.

rogers259 avatar rogers259 commented on June 12, 2024

Think this broke with the new widget loader as well.... just be sure to add... good to go after that.

declaredClass: 'gis.digit.Find',

from cmv-app.

friendde avatar friendde commented on June 12, 2024

It's definitely not a cut/paste from the old Controller.js to the recently published Controller.js. I see the syntax is different. Please educate me on the difference between the old Controller.js syntax so I can learn the JavaScript nuances. I challenged myself to create a configurable findTask widget with multiple map services. The new Controller.js has sidetracked me into understanding the workings of the different Controller.js files.

In the old Controller.js the format was
define([],
function(){
var controller = {};
var widgetLoaders = {};
return controller;
});

In the new Controller.js
define([]
function(){
return{
widgetLoader: function(){}
};
});

Why is return{} at the top of the new Controller.js versus the bottom of the old Controller.js
And what is the difference is using var widgetLoaders = {} versus widgetLoader:function(){}

from cmv-app.

DavidSpriggs avatar DavidSpriggs commented on June 12, 2024

@friendde See here (#69) for context on the changes to the widget loader. I have started some docs for the new widget config style here: https://github.com/DavidSpriggs/ConfigurableViewerJSAPI/wiki/The-config.js-file

from cmv-app.

friendde avatar friendde commented on June 12, 2024

I moved the findTask over into the new viewer.js. But the new Controller.js does not seem to make use of Find.js. However If hard I code the find params inside Find.js, after line 84 in search: function () {}, the findTask works as provided by @tr3vorm and worked out by the group in this thread.

I also added a check box so the user can search for text that is contained within the search string, rather than be expected to perform an exact match.

//Find.html, it seems I can't add html in a forum post so I truncated it to show the new lines
input id="containsSearchText" type="checkbox" data-dojo-type="dijit/form/CheckBox" name="containsSearchText"
label for="containsSearchText" Contains Search Text label
button data-dojo-type="dijit.form.Button" data-dojo-attach-event="onClick:search"
Search
/button

//viewer.js, I added the following inside widgets:{} around line 96

            find: {
                include: true,
                id: 'find',
                type: 'titlePane',
                path: 'gis/dijit/Find',
                title: "Find",
                open: true,
                position: 8,
                options: {
                    map: true,
                    options: {
                        url: 'http://ServerName/arcgis/rest/services/Gas/MapServer',
                        layerIds: [2],
                        searchFields: ['ValveNumber'],
                        outputSpatialReference: 4326
                    }
                }
            }

//Find.js

    search: function () {
        findTask = new esri.tasks.FindTask('http://ServerName/arcgis/rest/services/Gas/MapServer'); //(this.url) //defined in configViewer.js
        var oSR = new esri.SpatialReference({wkid:4326});
        //create find parameters
        var findParams = new esri.tasks.FindParameters();
        findParams.searchText = dom.byId("searchText").value;
        findParams.contains = dom.byId("containsSearchText").checked; //unchecked is false for findParams.contains;
        findParams.searchFields = ['ValveNumber']; //this.searchFields; //defined in configViewer.js
        findParams.outSpatialReference = oSR;
        findParams.returnGeometry = true;
        findParams.layerIds = [2]; //this.layerIds; //defined in configViewer.js

        findTask.execute(findParams, lang.hitch(this, 'showResults'));

        //findParams.outSpatialReference = {
            //wkid: this.outputSpatialReference
            //wkid: "102660"
            //map.spatialReference
        //};

from cmv-app.

DavidSpriggs avatar DavidSpriggs commented on June 12, 2024

@tmcgee are you going to PR this widget? Would be a nice addition.

@friendde You probably just need to add the params to the options object, thats what gets passed into the widget.

from cmv-app.

friendde avatar friendde commented on June 12, 2024

@DavidSpriggs thx I did do that. Sorry my snippets above did not format well in my post. I see you have options {} twice for directions. I did that for find as well. I'll try with just one when I get back to the office.

from cmv-app.

DavidSpriggs avatar DavidSpriggs commented on June 12, 2024

@friendde I added the formating for you, to format a code see here. Also, never use dom.byId instead use data-dojo-attach-point see here for details.

from cmv-app.

DavidSpriggs avatar DavidSpriggs commented on June 12, 2024

@friendde To use the prams from the config that you have above:

search: function () {
        findTask = new esri.tasks.FindTask(this.options.url)
        var oSR = new esri.SpatialReference({wkid:this.options.outputSpatialReference});
        //create find parameters
        var findParams = new esri.tasks.FindParameters();
        findParams.searchText = dom.byId("searchText").value;
        findParams.contains = dom.byId("containsSearchText").checked;
        findParams.searchFields = ['ValveNumber']; //this.searchFields; //defined in configViewer.js
        findParams.outSpatialReference = oSR;
        findParams.returnGeometry = true;
        findParams.layerIds = this.options.layerIds;

        findTask.execute(findParams, lang.hitch(this, 'showResults'));

You could also move them out of the options.options, to the top level of options.

from cmv-app.

tmcgee avatar tmcgee commented on June 12, 2024

Yes I do plan to create a PR for the Find widget. I've been pulled away this last week to work on a proposal so I haven't even had a chance to transition to the new widget loader. I will be focusing on code again starting today and most of this next week.

from cmv-app.

friendde avatar friendde commented on June 12, 2024

I have the findTask working for a single service with the new Controller.js and Viewer.js format. Big thanks to @DavidSpriggs for guidance and the authors of the original code posted above.

My next goal is to wire up multiple services and fields, using the format in viewer\js\gis\dijit\Identify\config.js for consistency of this configurable model. If I can get that going then pass the results into a zoom-to grid I mentioned above.

//Viewer.js
            find: {
                include: true,
                id: 'find',
                type: 'titlePane',
                path: 'gis/dijit/Find',
                title: "Find Features",
                open: true,
                position: 8,
                options: {
                    map: true,
                    findTaskUrl: 'http://ServerName/arcgis/rest/services/Electric/MapServer',
                    layerIds: [1],
                    searchFields: ['FACILITYID'],
                    outputSpatialReference: 4326
                }
            }
//Find.js

define([
    "dojo/_base/declare",
    "dijit/_WidgetBase",
    "dijit/_TemplatedMixin",
    "dojo/dom",
    "dojo/_base/lang",
    "dojo/_base/Color",
    "esri/layers/GraphicsLayer",
    "esri/graphic",
    "esri/renderers/SimpleRenderer",
    "esri/symbols/SimpleMarkerSymbol",
    "esri/symbols/SimpleLineSymbol",
    "esri/symbols/SimpleFillSymbol",
    "esri/layers/FeatureLayer",
    "esri/graphicsUtils",
    "esri/tasks/FindTask",
    "esri/tasks/FindParameters",
    'dojo/text!./Find/templates/Find.html'
], function (declare, _WidgetBase, _TemplatedMixin, dom, lang, Color, GraphicsLayer, Graphic, SimpleRenderer, SimpleMarkerSymbol, SimpleLineSymbol, SimpleFillSymbol, FeatureLayer, graphicsUtils, FindTask, FindParameters, template) {

    return declare([_WidgetBase, _TemplatedMixin], {
        templateString: template,
        postCreate: function () {
            this.inherited(arguments);

            this.pointSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_CIRCLE, 10, new SimpleLineSymbol(SimpleLineSymbol.STYLE_NONE, new Color([255, 255, 0]), 1), new Color([255, 255, 0, 2.0]));
            this.polylineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 255, 0]), 2);
            this.polygonSymbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_NONE, new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 255, 0]), 2), new Color([255, 255, 0, 1.0]));

            this.pointGraphics = new GraphicsLayer({
                id: "findGraphics_point",
                title: "Find"
            });
            this.pointRenderer = new SimpleRenderer(this.pointSymbol);
            this.pointRenderer.label = "User drawn points";
            this.pointRenderer.description = "User drawn points";
            this.pointGraphics.setRenderer(this.pointRenderer);

            // poly line 
            this.polylineGraphics = new GraphicsLayer({
                id: "findGraphics_line",
                title: "Find Graphics"
            });
            this.polylineRenderer = new SimpleRenderer(this.polylineSymbol);
            this.polylineRenderer.label = "User drawn lines";
            this.polylineRenderer.description = "User drawn lines";
            this.polylineGraphics.setRenderer(this.polylineRenderer);

            // polygons
            this.polygonGraphics = new FeatureLayer({
                layerDefinition: {
                    geometryType: "esriGeometryPolygon",
                    fields: [{
                        name: "OBJECTID",
                        type: "esriFieldTypeOID",
                        alias: "OBJECTID",
                        domain: null,
                        editable: false,
                        nullable: false
                    }, {
                        name: "ren",
                        type: "esriFieldTypeInteger",
                        alias: "ren",
                        domain: null,
                        editable: true,
                        nullable: false
                    }]
                },
                featureSet: null
            }, {
                id: "findGraphics_poly",
                title: "Find Graphics",
                mode: FeatureLayer.MODE_SNAPSHOT
            });
            this.polygonRenderer = new SimpleRenderer(this.polygonSymbol);
            this.polygonRenderer.label = "User drawn polygons";
            this.polygonRenderer.description = "User drawn polygons";
            this.polygonGraphics.setRenderer(this.polygonRenderer);

            this.map.addLayer(this.polygonGraphics);
            this.map.addLayer(this.polylineGraphics);
            this.map.addLayer(this.pointGraphics);
        },
        search: function () {
            findTask = new esri.tasks.FindTask(this.findTaskUrl); //defined in configViewer.js
            var oSR = new esri.SpatialReference({wkid:4326});
            //create find parameters
            var findParams = new esri.tasks.FindParameters();
            findParams.searchText = this.searchText.value; //dom.byId("searchText").value;
            findParams.contains = this.containsSearchText.checked; //dom.byId("containsSearchText").checked; //unchecked is false for findParams.contains;
            findParams.searchFields = this.searchFields; //defined in configViewer.js
            findParams.outSpatialReference = oSR;
            findParams.returnGeometry = true;
            findParams.layerIds = this.layerIds; //defined in configViewer.js

            findTask.execute(findParams, lang.hitch(this, 'showResults'));

            //findParams.outSpatialReference = {
                //wkid: this.outputSpatialReference
                //wkid: "102660"
                //map.spatialReference
            //};


        },
        showResults: function (results) {
            var counter = 0;
            this.pointGraphics.clear();
            this.polylineGraphics.clear();
            this.polygonGraphics.clear();

            dojo.forEach(results, function (result) {
                var graphic;
                counter++;
                switch (result.feature.geometry.type) {
                    case "point":
                        graphic = new Graphic(result.feature.geometry);
                        this.pointGraphics.add(graphic);
                        //                        alert('add: '+graphic.geometry.x +', '+ graphic.geometry.y);
                        break;
                    case "polyline":
                        graphic = new Graphic(result.feature.geometry);
                        this.polylineGraphics.add(graphic);
                        break;
                    case "polygon":
                        graphic = new Graphic(result.feature.geometry, null, {
                            ren: 1
                        });
                        this.polygonGraphics.add(graphic);
                        break;
                    default:
                }
            }, this);

            if (counter == 0) {
                alert("No matches found."+'\n'+"Try again with/without box checked");
            } else {
                // zoom to layer extents!
                var zoomExtent = null;
                //If the layer is a single point then extents are null
                // if there are no features in the layer then extents are null
                // the result of union() to null extents is null

                if (this.pointGraphics.graphics.length > 0) {
                    zoomExtent = this.getPointExtent(this.pointGraphics.graphics);
                }
                if (this.polylineGraphics.graphics.length > 0) {
                    if (zoomExtent === null)
                        zoomExtent = graphicsUtils.graphicsExtent(this.polylineGraphics.graphics);
                    else
                        zoomExtent = zoomExtent.union(graphicsUtils.graphicsExtent(this.polylineGraphics.graphics));
                }
                if (this.polygonGraphics.graphics.length > 0) {
                    if (zoomExtent === null)
                        zoomExtent = graphicsUtils.graphicsExtent(this.polygonGraphics.graphics);
                    else
                        zoomExtent = zoomExtent.union(graphicsUtils.graphicsExtent(this.polygonGraphics.graphics));
                }

                this.map.setExtent(zoomExtent.expand(1.2));
            }
        },
        getPointExtent: function (pointFeatures) {
            var extent = graphicsUtils.graphicsExtent(pointFeatures);
            var factor = 0.0001; // hack: 0.0001 of a degree

            if (extent == null && pointFeatures.length > 0) {
                var pt = pointFeatures[0].geometry;
                extent = new esri.geometry.Extent(pt.x - factor, pt.y - factor, pt.x + factor, pt.y + factor, pt.SpatialReference);
            }
            return extent;
        },
        clearResults: function () {
            this.pointGraphics.clear();
            this.polylineGraphics.clear();
            this.polygonGraphics.clear();

            dom.byId("searchText").value = "";
        }
    });
});
Find.html
<div data-dojo-type="Find" >
    <input data-dojo-attach-point="searchText" data-dojo-type="dijit.form.ValidationTextBox" placeholder="Enter search text" />
    <input type="checkbox" data-dojo-attach-point="containsSearchText"  data-dojo-type="dijit.form.CheckBox" name="containsSearchText" /> 
    <label for="containsSearchText">Contains Search Text</label>
    <button data-dojo-type="dijit.form.Button" data-dojo-attach-event="onClick:search" >
        Search
    </button>

    <button data-dojo-type="dijit.form.Button" data-dojo-props="iconClass:'clearIcon',showLabel:true"
            data-dojo-attach-event="onClick:clearResults">
        Clear Results
    </button>
</div>

from cmv-app.

friendde avatar friendde commented on June 12, 2024

It's not the most elegant way of doing it, but I have a loop that works with any map service in the app (if it supports the request). If the "app Admin" configures the settings in Viewer.js then the search will find features in the layerIds and searchFields configured there with any supported map service in the app.

So the app Admin has control over the layerIds and searchFields but no control over which map service is utilized.

// Viewer.js

            find: {
                include: true,
                id: 'find',
                type: 'titlePane',
                path: 'gis/dijit/Find',
                title: "Find Features",
                open: true,
                position: 8,
                options: {
                    map: true,
                    // findTaskUrl: 'http://ServerName/arcgis/rest/services/Gas/MapServer',
                    layerIds: [23],
                    searchFields: ['FACILITYID'],
                    outputSpatialReference: 4326
                }
            },
// in Find.js replace search:function with this
        search: function () {
            this.layers = [];
            var oSR = new esri.SpatialReference({wkid:4326});
            var findParams = new esri.tasks.FindParameters();
            findParams.searchText = this.searchText.value;
            findParams.contains = this.containsSearchText.checked;
            findParams.searchFields = this.searchFields; //defined in configViewer.js
            findParams.outSpatialReference = oSR;
            findParams.returnGeometry = true;
            findParams.layerIds = this.layerIds; //defined in configViewer.js
            array.forEach(this.map.layerIds, function(layerId) {
                var layer = this.map.getLayer(layerId);
                //console.log('layerID: ', layer.url);
                if (layer.declaredClass === 'esri.layers.ArcGISDynamicMapServiceLayer') {
                    console.log('Finding in layerUrl: ' + layer.url )
                    findTask = esri.tasks.FindTask(layer.url)
                    findTask.execute(findParams, lang.hitch(this, 'showResults'));
                }
            }, this);

        },

from cmv-app.

DavidSpriggs avatar DavidSpriggs commented on June 12, 2024

@friendde Your getting there. Just a few observations:

  1. You don't want to create a new find task each time a search is made. Instead create the find task once at startup (like in identify) then simply use them each time there is a search made.
  2. You forgot the new when creating the find task.

from cmv-app.

tr3vorm avatar tr3vorm commented on June 12, 2024

Closing this off as the @tmcgee's Find widget satisfies my original requirements.

from cmv-app.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.