Skip to content

Commit 9510162

Browse files
committed
init
0 parents  commit 9510162

File tree

3 files changed

+324
-0
lines changed

3 files changed

+324
-0
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Create an Empty Feature Service using ArcGIS Maps SDK for JavaScript
2+
3+
Demo by Gavin Rehkemper
4+
5+
## Quick Start
6+
7+
To run the demo, in a terminal run:
8+
9+
```bash
10+
npx serve
11+
```
12+
13+
## Alternatives
14+
15+
See a slightly simpler version using ArcGIS REST JS here: <https://github.com/gavinr-maps/arcgis-rest-js-create-empty-feature-service-demo>

index.html

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width" />
6+
<title>Create Empty Feature Service</title>
7+
8+
<script src="https://cdn.tailwindcss.com"></script>
9+
10+
<!-- Load Calcite -->
11+
<script
12+
type="module"
13+
src="https://js.arcgis.com/calcite-components/3.0.3/calcite.esm.js"
14+
></script>
15+
16+
<script type="importmap">
17+
{
18+
"imports": {
19+
"@arcgis/core/": "https://js.arcgis.com/4.32/@arcgis/core/"
20+
}
21+
}
22+
</script>
23+
<link
24+
rel="stylesheet"
25+
href="https://js.arcgis.com/4.32/esri/themes/light/main.css"
26+
/>
27+
</head>
28+
<body>
29+
<div id="loginButtonWrapper">
30+
<calcite-button id="loginButton">Login</calcite-button>
31+
</div>
32+
<div id="createFeatureServiceWrapper" class="hidden">
33+
<form>
34+
<calcite-input
35+
placeholder="Name your Feature Service"
36+
id="featureServiceNameInput"
37+
></calcite-input>
38+
<calcite-button id="createFeatureServiceButton"
39+
>Create Feature Service</calcite-button
40+
>
41+
<calcite-button id="logoutButton">Logout</calcite-button>
42+
</form>
43+
44+
<!-- Area to show the results: -->
45+
<div id="results"></div>
46+
</div>
47+
<script src="main.js" type="module"></script>
48+
</body>
49+
</html>

main.js

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
// import { ArcGISIdentityManager } from "https://cdn.skypack.dev/@esri/arcgis-rest-request@4";
2+
// import {
3+
// createFeatureService,
4+
// addToServiceDefinition,
5+
// } from "https://cdn.skypack.dev/@esri/arcgis-rest-feature-service@4";
6+
import MapView from "@arcgis/core/views/MapView.js";
7+
import Portal from "@arcgis/core/portal/Portal.js";
8+
import OAuthInfo from "@arcgis/core/identity/OAuthInfo.js";
9+
import esriId from "@arcgis/core/identity/IdentityManager.js";
10+
import esriRequest from "@arcgis/core/request.js";
11+
12+
const oAuthOptions = {
13+
// https://prof-services.maps.arcgis.com/home/item.html?id=bd2f2c00808747c2a5bee10040f0cc31#overview
14+
appId: "5zExhoFVwOyHuQaL",
15+
portalUrl: "https://prof-services.maps.arcgis.com", // INCLUDE IF USING ARCGIS ENTERPRISE!
16+
popup: false,
17+
};
18+
19+
const createFeatureService = async ({ portal, item }) => {
20+
console.log("portal", portal);
21+
const url = `${portal.restUrl}/content/users/${portal.user.username}/createService`;
22+
23+
const body = {
24+
createParameters: JSON.stringify(item),
25+
outputType: "featureService",
26+
f: "json",
27+
};
28+
29+
// the credential/token is automatically added when using esriRequest:
30+
return await esriRequest(url, {
31+
method: "post",
32+
responseType: "json",
33+
query: body,
34+
});
35+
};
36+
37+
const createFS = async (portal, featureServiceName) => {
38+
results.innerHTML = "Creating service ...";
39+
// we SHOULD check to see if the service name is available using
40+
// https://org.arcgis.com/sharing/rest/portals/_ID_/isServiceNameAvailable?name=test_Layer_1&type=Feature%20Service&f=json&token=
41+
// but we're skipping that for now.
42+
const createFeatureServiceResult = await createFeatureService({
43+
portal,
44+
item: {
45+
name: featureServiceName,
46+
capabilities: "Create,Delete,Query,Update,Editing",
47+
},
48+
});
49+
50+
console.log("createFeatureServiceResult", createFeatureServiceResult);
51+
52+
// Now call addToServiceDefinition to add layers to the Feature Service
53+
const adminUrl = `${createFeatureServiceResult.data.serviceurl.replace(
54+
`/rest/services`,
55+
`/rest/admin/services`
56+
)}/addToDefinition`;
57+
58+
const body = {
59+
f: "json",
60+
addToDefinition: JSON.stringify({
61+
layers: [
62+
{
63+
currentVersion: 10.51,
64+
id: 0,
65+
name: featureServiceName,
66+
geometryType: "esriGeometryPoint",
67+
type: "Feature Layer",
68+
displayField: "",
69+
description: "",
70+
copyrightText: "",
71+
defaultVisibility: true,
72+
editingInfo: { lastEditDate: null },
73+
isDataVersioned: false,
74+
supportsAppend: true,
75+
supportsCalculate: true,
76+
supportsTruncate: true,
77+
supportsAttachmentsByUploadId: true,
78+
supportsAttachmentsResizing: true,
79+
supportsRollbackOnFailureParameter: true,
80+
supportsStatistics: true,
81+
supportsAdvancedQueries: true,
82+
supportsValidateSql: true,
83+
supportsCoordinatesQuantization: true,
84+
supportsApplyEditsWithGlobalIds: false,
85+
supportsMultiScaleGeometry: true,
86+
hasGeometryProperties: true,
87+
geometryProperties: {
88+
shapeLengthFieldName: "Shape__Length",
89+
units: "esriMeters",
90+
},
91+
advancedQueryCapabilities: {
92+
supportsPagination: true,
93+
supportsPaginationOnAggregatedQueries: true,
94+
supportsQueryRelatedPagination: true,
95+
supportsQueryWithDistance: true,
96+
supportsReturningQueryExtent: true,
97+
supportsStatistics: true,
98+
supportsOrderBy: true,
99+
supportsDistinct: true,
100+
supportsQueryWithResultType: true,
101+
supportsSqlExpression: true,
102+
supportsAdvancedQueryRelated: true,
103+
supportsCountDistinct: true,
104+
supportsLod: true,
105+
supportsReturningGeometryCentroid: false,
106+
supportsReturningGeometryProperties: true,
107+
supportsQueryWithDatumTransformation: true,
108+
supportsHavingClause: true,
109+
supportsOutFieldSQLExpression: true,
110+
},
111+
useStandardizedQueries: true,
112+
minScale: 0,
113+
maxScale: 0,
114+
extent: {
115+
xmin: -17811118.526923772,
116+
ymin: -15538711.096309224,
117+
xmax: 17811118.526923772,
118+
ymax: 15538711.096309224,
119+
spatialReference: { wkid: 102100, latestWkid: 3857 },
120+
},
121+
drawingInfo: {
122+
renderer: {
123+
type: "simple",
124+
symbol: {
125+
type: "esriPMS",
126+
url: "RedSphere.png",
127+
imageData:
128+
"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",
129+
contentType: "image/png",
130+
width: 15,
131+
height: 15,
132+
},
133+
},
134+
},
135+
allowGeometryUpdates: true,
136+
hasAttachments: true,
137+
htmlPopupType: "esriServerHTMLPopupTypeNone",
138+
hasMetadata: true,
139+
hasM: false,
140+
hasZ: false,
141+
objectIdField: "OBJECTID",
142+
uniqueIdField: { name: "OBJECTID", isSystemMaintained: true },
143+
fields: [
144+
{
145+
name: "OBJECTID",
146+
type: "esriFieldTypeOID",
147+
alias: "OBJECTID",
148+
sqlType: "sqlTypeOther",
149+
nullable: false,
150+
editable: false,
151+
domain: null,
152+
defaultValue: null,
153+
},
154+
],
155+
capabilities: "Query,Editing,Create,Update,Delete,Sync",
156+
maxRecordCount: 2000,
157+
supportedQueryFormats: "JSON, geoJSON, PBF",
158+
indexes: [],
159+
types: [],
160+
templates: [
161+
{
162+
name: "New Feature",
163+
description: "",
164+
drawingTool: "esriFeatureEditToolPoint",
165+
prototype: { attributes: {} },
166+
},
167+
],
168+
globalIdField: "",
169+
hasStaticData: false,
170+
},
171+
],
172+
}),
173+
};
174+
175+
const addToServiceDefinitionResults = await esriRequest(adminUrl, {
176+
method: "post",
177+
responseType: "json",
178+
query: body,
179+
});
180+
181+
console.log("addToServiceDefinitionResults", addToServiceDefinitionResults);
182+
183+
if (addToServiceDefinitionResults.data.success === true) {
184+
const credential = await esriId.getCredential(
185+
createFeatureServiceResult.data.serviceurl
186+
);
187+
results.innerHTML = `Created service: <br />
188+
<a target="_blank" href="https://arcgis.com/home/item.html?id=${createFeatureServiceResult.data.itemId}">Item</a><br />
189+
<a target="_blank" href="${createFeatureServiceResult.data.serviceurl}?token=${credential.token}">Service URL</a><br />
190+
`;
191+
}
192+
};
193+
194+
const checkSignIn = async () => {
195+
try {
196+
const credential = await esriId.checkSignInStatus(
197+
oAuthOptions.portalUrl + "/sharing"
198+
);
199+
console.log("credential", credential);
200+
const portal = new Portal({
201+
authMode: "immediate",
202+
});
203+
204+
await portal.load();
205+
return portal;
206+
} catch (E) {
207+
console.log("NOT SIGNED IN - SHOW LOGIN BUTTON");
208+
return false;
209+
}
210+
};
211+
212+
const signOut = () => {
213+
esriId.destroyCredentials();
214+
window.location.reload();
215+
};
216+
const signIn = async () => {
217+
// If the user is not signed in, generate a new credential.
218+
await esriId.getCredential(oAuthOptions.portalUrl + "/sharing", {
219+
oAuthPopupConfirmation: false,
220+
});
221+
222+
await updateUI();
223+
};
224+
225+
const updateUI = async () => {
226+
const portal = await checkSignIn();
227+
228+
if (portal) {
229+
loginButtonWrapper.classList.add("hidden");
230+
createFeatureServiceWrapper.classList.remove("hidden");
231+
}
232+
};
233+
234+
const main = async () => {
235+
const info = new OAuthInfo(oAuthOptions);
236+
esriId.registerOAuthInfos([info]);
237+
238+
await updateUI();
239+
240+
// // When the login button is clicked, start the login process:
241+
loginButton.addEventListener("click", () => {
242+
signIn();
243+
});
244+
245+
logoutButton.addEventListener("click", () => {
246+
signOut();
247+
});
248+
249+
// When the "create service" button is clicked, create the feature service
250+
createFeatureServiceButton.addEventListener("click", async () => {
251+
const portal = await checkSignIn();
252+
if (portal && featureServiceNameInput.value !== "") {
253+
createFS(portal, featureServiceNameInput.value);
254+
} else {
255+
console.error("Invalid inputs.");
256+
}
257+
});
258+
};
259+
260+
main();

0 commit comments

Comments
 (0)