Google Earth Line Drawing Tool
In this tutorial, we'll deep dive into some interesting uses of the new drawing tools API.
- We'll learn how to dynamically get a grid and salvage it as a customer-go with geometry.
- We'll also build a simple assortment puppet that classifies supported user-drawn points.
Note: if you are copy and pasting the code snippets downstairs into the Code Editor, they may non work without drawing a geometry first.
Let's get started!
Backdrop
Before the Drawing Tools API, geometries had to be rendered using Represent.addLayer(geometry), which substance they had to round trip to the server whenever the user panned or zoomed on the map. With the new drawing tools API, we can right away render those geometries directly on the map as if we had drawn them ourselves!
Documentation on the drawing tools API is found here: https://developers.google.com/earth-engine/ui_widgets#ui.map.drawingtools.
The drawing tools API adds two new components to the ui.Map API:
-
ui.Map.DrawingTools, and -
ui.Map.GeometryLayer.
Together, these manage the geometry editing trading operations as well As some of the geometry layers on the drawing tools. The lottery tools provide a rich put of functionality that allow you to take heed for when the user draws, edits, or deletes a geometry, equally well as adding, removing, or selecting geometry layers.
To retrieve the lottery tools for a map, simply call the drawingTools() method acting on the map:
var drawingTools = Map.drawingTools(); Unlike the default Correspondenc, newly created ui.Maps don't have drafting tools by nonpayment, but we can add draftsmanship tools to newly created maps by merely calling the drawingTools() method acting to call back them:
volt-ampere map = ui.Map(); // Prints true since drawingTools() adds drawing tools to the map. publish(map.drawingTools().getShown()); // Supervene upon the default Map with the newly created represent. ui.root.widgets().reset([map]); Note: you john only accept combined drawing tools on a map at a time.
Use the layers() method on the drawing tools to regain an ActiveList of the geometry layers. A geometry layer contains a list of geometries that you can incur by calling the geometries() method on the layer. In that respect are two ways to contribute freshly geometry layers to the drafting tools:
var drawingTools = Represent.drawingTools(); // Use the addLayer method acting along the draft tools directly. var geometries = [ee.Geometry.Point([0, 0]), ee.Geometry.Rectangle([[0, 0], [1, 1]])]; drawingTools.addLayer(geometries, 'my_geometry1', 'red'); // Create a spic-and-span GeometryLayer and add it to the layers list. var layer = ui.Map.GeometryLayer(geometries, 'my_geometry2', 'blue'); drawingTools.layers().add(layer); // Print the list of geometries. print(level.geometries()); // Add a geometry to the layer. layer.geometries().add(EE.Geometry.Show(1, 1)); There are some nice configuration options for the drawing tools:
-
setShown(boolean)changes the profile of the drawing tools. -
setDrawModes(['point', 'line', 'polygon', 'rectangle'])changes the available draw modes to the user. -
setShape('point')sets the current draw shape and starts draw mode. -
setLinked(mathematician)configures whether geometries are linked to the imports.
Let's submit a deeper look at setLinked, since information technology's surprisingly useful!
- When linked modal value is turned on, the geometries in the draft tools are linked to the imports board.
- When coupled mode is turned off, the geometries in the imports empanel will be removed from the lottery tools (they won't be deleted), and recently created geometry layers wish non comprise added to the imports.
The nonremittal Map has linked fashion turned on by default whereas a newly created ui.Correspondenc has linked mode turned off by default.
Caution: If you delete a geometry layer while linked mode is on (like career layers().reset()), they will be deleted from the imports!
Example: Linked Maps
Suppose you craved to make a split map out where geometries are reflected on both maps. All you have to do is call on along linked modal value for both maps' drawing tools and any geometries that are drawn or edited on one map testament be reflected on the other!
Here is a demo script:
var map1 = ui.Map out(); map1.drawingTools().setLinked(true); var map2 = ui.Map(); map2.drawingTools().setLinked(true); ui.Map.Linker([map1, map2]); ui.tooth root.widgets().readjust([ui.SplitPanel({firstPanel: map1, secondPanel: map2})]); // Now sample drawing a geometry on some maps!
Figure 1. A split mapping with geometries reflected connected some sides.
Adroit, right? Permit's do something even to a greater extent advanced!
Client Side of meat Geometries
Next up, we'll learn how to dynamically generate a grid in Earthly concern Engine and add it A a node-broadside geometry so that information technology North Korean won't Be rerendered all time the map viewport changes.
The drawing tools let you take a waiter-side geometry and render it on the client. This requires us to exercise valuate connected the server-face geometries to fetch them from the server and prepar them in stock to be drawn directly on the map.
Also rising, your strange geometries now turn up in Apps so you only possess to run your expensive geometry rendering code once.
For example, suppose that you rendered a complex geometry in Land Engine, you can add it to the map out as a client go with geometry (with linked mode on). When you publish the app, the prerendered and imported geometry bequeath already be at that place; no further steganography required! Here's an example of that in action:
Example: Build a Client-Side Reference grid
Here's a link fully playscript. Beseech run to visualize the generated grid!
Bank note: this example doesn't work with all projections and doesn't work well with large geometries, so preserve that in mind.
Prototypic, we'll demand to write a function that generates a grid based on a geometry. Draw a geometry about a region of interest and then lead the following script:
// pixelLonLat returns an image with all picture element labeled with longitude and // latitude values. volt-ampere lonLat = ee.Image.pixelLonLat(); // Prize the longitude and latitude bands, procreate by a large keep down and so // truncate them to integers. volt-ampere lonGrid = lonLat .select('longitude') .multiply(10000000) .toInt(); var latGrid = lonLat .select('latitude') .reproduce(10000000) .toInt(); // To produce the grid, multiply the latitude and longitude images so use // reduce to vectors at the 10km resolution to group the grid into vectors. var grid = lonGrid .multiply(latGrid) .reduceToVectors({ geometry: geometry, // This is undefined until you draw a geometry. scale: 10000, geometryType: 'polygon', }); Map out.addLayer(grid); Then we enumerate over the generated Features in the FeatureCollection and build up a inclination of LinearRings that we'll use to build our GeometryLayer.
// Fetch the features in the grid and produce a list of linear rings. // The grid variable is defined in the step higher up. var geometries = grid.toList(1000).represent(function(feature) { volt-ampere featureGeometry = electrical engineering.Feature(boast).geometry(); var coordinates = featureGeometry.coordinates().stupefy(0); return ee.Geometry.LinearRing(coordinates); }); Lastly, we add the geometries to the mapping using the drawing tools. Notice that we expend value to fetch the geometries client-side in order to add them to the drawing tools.
var drawingTools = Map.drawingTools(); // Fetch the geometries so they can be added to the drawing tools. geometries.evaluate(function(geometriesList) { volt-ampere layer = ui.Map.GeometryLayer({ geometries: geometriesList, name: 'power grid', color: 'Afro-American', shown: true, // Show the layer (already defaults to true). locked: true, // Lock the bed. }); drawingTools.layers().set(1, level); }); Thither! Now we have a grid that is rendered client-side that won't go aside when the map viewport changes. Notice that the geometry is uneditable; that's because we constructed the layer with the latched dimension set to true.
Pattern 2. A map with a reference grid terminated the San Francisco Bay Area.
We'll refactor the code to use functions to make information technology more reusable:
occasion makeGrid(geometry, exfoliation) { // pixelLonLat returns an image with each pel tagged with longitude and // latitude values. volt-ampere lonLat = ee.Prototype.pixelLonLat(); // Select the longitude and parallel bands, multiply by a large number then // truncate them to integers. var lonGrid = lonLat .select('longitude') .multiply(10000000) .toInt(); var latGrid = lonLat .select('parallel') .reproduce(10000000) .toInt(); // To produce the grid, multiply the line of latitude and longitude images then use // tighten to vectors at the 10km resolution to group the grid into vectors. return lonGrid .multiply(latGrid) .reduceToVectors({ geometry: geometry, // This is undefined until you draw poker a geometry. scale of measurement: exfoliation, geometryType: 'polygonal shape', }); } function buildGeometryList(grid, demarcation line) { return power system.toList(specify).map(function(feature film) { var featureGeometry = EE.Feature(feature).geometry(); var coordinates = featureGeometry.coordinates().get(0); retrovert ee.Geometry.LinearRing(coordinates); }); } volt-ampere grid = makeGrid(geometry, 10000); // 10 km exfoliation var geometries = buildGeometryList(grid, 1000); var drawingTools = Represent.drawingTools(); geometries.evaluate(function(geometriesList) { var layer = ui.Map.GeometryLayer({ geometries: geometriesList, name: 'reference grid', color: 'contraband', shown: reliable, // Display the layer (already defaults to apodictic). locked: true, // Lock the layer. }); drawingTools.layers().set(1, layer); }); Since the grid has already been added to the imports, the grid variable is now on hand for us to use, then we can comment out the bit that generated the control grid in the first property to preserve us some computation clock time.
// var grid = makeGrid(geometry); // var geometries = buildGeometryList(grid); // var drawingTools = Map.drawingTools(); // geometries.evaluate(function(geometriesList) { // volt-ampere layer = ui.Map.GeometryLayer({ // geometries: geometriesList, // list: 'control grid', // color: 'black', // shown: true, // Show the layer (already defaults to true). // latched: genuine, // Lock the stratum. // }); // drawingTools.layers().set(1, layer); // }); If you publish this American Samoa an app, since the imports carry over to Apps, the grid leave Be there, ready to go!
Event handling
At last, the drafting tools API allows us to listen for when users interact with:
- individual geometries,
- layers,
- or the drawing tools themselves,
and lets us run callback functions when they happen. Hera are some utilizable events you can listen for:
Drawing Tools Events:
-
onShapeChange(recall)- A draw style is changed, for examplepoint,line,polygon, orrectangle.
Geometry Events (callback called with geometry, layer, and drawing tools):
-
onDraw(callback)- a geometry is added to a bed. -
onEdit(callback)- a geometry is edited. -
onErase(callback)- a geometry is removed. -
onSelect(callback)- a geometry is selected.
Layer Events (callback called with layer and draft tools):
-
onLayerAdd(callback)- a layer is added. -
onLayerRemove(callback)- a layer is far. -
onLayerSelect(callback)- a stratum is selected. -
onLayerConfig(callback)- a layer attribute (e.g. name, color) is changed.
We hindquarters use these to listen for substance abuser interactions with the drawing tools for a responsive and personalized, Worldly concern Engine see!
Model: Sorting With Exploiter-Drawn Geometries
The last example is adapting the Classifications lesson to embody more interactive. This example runs a political machine learning classification using points defined in three FeatureCollections, one for urban, vegetation, and water. Currently, if we wanted to rerun the assortment on new points, we would let to run the playscript after we edit the points. What if we wanted to rerun the classification mechanically when we edit the points?
We bottom refactor the classification to rely happening the points in the drawing tools, which are always kept equal to date. To stimulate this easier, we can use the getEeObject() method on the GeometryLayer to experience the Land Engine physical object joint with a GeometryLayer (e.g. the fundamental FeatureCollection).
Premiere, import the feature collections at the top of the file so we have municipality, vegetation, and water in your imports (loom over the textbook at the top of the script and press commute along the choice that says ""urban", "vegetation" and "pee" can Be converted to significance records.").
Figure 3. Classification tools example with foreign boast collections.
Next, get the geometry layers associated with each of the feature collections, since any edits to the geometries will be mirrored happening the layer (add to the peak of the file away):
// Starter script: https://code.earthengine.google.com/?scriptPath=Examples:Demos/Assortment var drawingTools = Map.drawingTools(); // Only allow drawing points. drawingTools.setDrawModes(['point']); // Baffle the layers list. var layers = drawingTools.layers(); // Assuming the order is city-bred, vegetation, then water. var urbanLayer = layers.get(0); var vegetationLayer = layers.get(1); var waterLayer = layers.get(2); Now that we bear the layers, we can get the FeatureCollection associated with apiece layer away calling the getEeObject() method.
Alteration the following line from:
// Merge the trio geometry layers into a uninominal FeatureCollection. var newfc = urban.merge(vegetation).merge(water); to:
// Merge the three geometry layers into a single FeatureCollection. var urbanfc = urbanLayer.getEeObject(); var vegetationfc = vegetationLayer.getEeObject(); var waterfc = waterLayer.getEeObject(); var newfc = urbanfc.merge(vegetationfc).merge(waterfc); and run the script to see that everything stayed the same. Next, we testament attach event listeners to the drafting tools to rerun the classification whenever a geometry is changed.
For the first time, we will bundle all of the compartmentalization code into a single function called assort and make some modifications to the Map.addLayer calls to hold for the function spurting multiple times:
function classify() { // Mix up the three geometry layers into a single FeatureCollection. var urbanfc = urbanLayer.getEeObject(); var vegetationfc = vegetationLayer.getEeObject(); volt-ampere waterfc = waterLayer.getEeObject(); volt-ampere newfc = urbanfc.merge(vegetationfc).merge(waterfc); // Use these bands for classification. var bands = ['B2', 'B3', 'B4', 'B5', 'B6', 'B7']; // The name of the property on the points storing the class label. volt-ampere classProperty = 'landcover'; // Sample the composite to generate education data. Government note that the // social class label is stored in the 'landcover' property. var breeding = composite.select(bands).sampleRegions({ collection: newfc, properties: [classProperty], scale: 30 }); // Train a CART classifier. volt-ampere classifier = ee.Classifier.smileCart().caravan({ features: preparation, classProperty: classProperty, }); // Black and white several info about the classifier (taxon to CART). print('CART, explained', classifier.excuse()); // Sort out the composite plant. var classified = composite.separate(classifier); var palette = {Amoy: 0, max: 2, palette: ['red', 'green', 'puritanic']}; var layer = ui.Map.Layer(classified, palette, 'classified ad'); Map.layers().adjust(0, layer); // Optionally, do some accuracy assessment. Fist, add a column of // random uniforms to the training dataset. var withRandom = training.randomColumn('random'); // We want to reserve some of the data for examination, // to annul overfitting the model. var split = 0.7; // Roughly 70% preparation, 30% testing. var trainingPartition = withRandom.permeate(electrical engineering.Filter.lt('random', split)); volt-ampere testingPartition = withRandom.strain(ee.Filter.gte('random', split)); // Skilled with 70% of our information. var trainedClassifier = ee.Classifier.smileRandomForest(5).train({ features: trainingPartition, classProperty: classProperty, inputProperties: bands }); // Classify the test FeatureCollection. var test = testingPartition.classify(trainedClassifier); // Print the confusion matrix. var confusionMatrix = test.errorMatrix(classProperty, 'classification'); print('Confusedness Intercellular substance', confusionMatrix); } classify(); Now, when we call classify, the script will use the up-to-go steady version of the imports to run the classifier, which way we don't need to rerun the book to fascinate unaccustomed points!
We sum up event listeners to detect when the user has made any edits to the geometries and persist the sort function. We also use ui.util.debounce to lone run it once every 100 milliseconds to prevent IT from firing too fast, the like when the user drags a item around.
// Use debounce to call the function at most all 100 milliseconds. drawingTools.onEdit(ui.util.debounce(classify, 100)); drawingTools.onDraw(ui.util.debounce(classify, 100)); drawingTools.onErase(ui.util.debounce(classify, 100)); Represent.centerObject(urbanised); That's altogether there is to it! Here's a radio link to the full hand.
Conclusion
The drawing tools API provides a rich set of features that cave in you full control of the client-sidelong geometries in your Globe Engine scripts and apps. We're excited to witness what you frame with them!
Incentive
Since you made IT this far down, here's an Dry land Engine script that recreates the drawing tools from moolah :)
Google Earth Line Drawing Tool
Source: https://developers.google.com/earth-engine/tutorials/community/drawing-tools
0 Response to "Google Earth Line Drawing Tool"
Postar um comentário